Skip to content

Commit

Permalink
Merge pull request #16 from salesforcecli/mdonnalley/mso
Browse files Browse the repository at this point in the history
feat: add multi-stage-output
  • Loading branch information
mdonnalley authored Nov 12, 2024
2 parents f62a615 + 7dede90 commit f5daa54
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 26 deletions.
8 changes: 5 additions & 3 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
"flagAliases": [],
"flagChars": [
"f",
"n",
"o"
],
"flags": [
"api-version",
"flags-dir",
"job-spec",
"json",
"spec",
"name",
"target-org"
],
"plugin": "@salesforce/plugin-agent"
Expand All @@ -22,7 +24,7 @@
"flagAliases": [],
"flagChars": [
"d",
"n",
"f",
"o",
"t"
],
Expand All @@ -31,9 +33,9 @@
"company-description",
"company-name",
"company-website",
"file-name",
"flags-dir",
"json",
"name",
"output-dir",
"role",
"target-org",
Expand Down
8 changes: 6 additions & 2 deletions messages/agent.create.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ Create an Agent from an agent spec.

Create an Agent from an agent spec. Agent metadata is created in the target org and retrieved to the local project.

# flags.spec.summary
# flags.job-spec.summary

The path to an agent spec file.

# flags.spec.description
# flags.job-spec.description

The agent spec file defines job titles and descriptions for the agent and can be created using the `sf agent create spec` command.

# flags.name.summary

The name of the agent.

# examples

- Create an Agent:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ Create an Agent spec.

Create an Agent spec, which is a list of job titles and descriptions that the agent performs.

# flags.name.summary

The name of the agent to create.

# flags.type.summary

The type of agent to create.
Expand All @@ -34,6 +30,10 @@ The website URL for the company.

The location within the project where the agent spec will be written.

# flags.file-name.summary

The name of the file to write the agent spec to.

# examples

- Create an Agent spec in the default location:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@inquirer/input": "^4.0.1",
"@inquirer/select": "^4.0.1",
"@oclif/core": "^4",
"@oclif/multi-stage-output": "^0.7.12",
"@salesforce/agents": "^0.1.2",
"@salesforce/core": "^8.5.2",
"@salesforce/kit": "^3.2.1",
Expand Down
45 changes: 41 additions & 4 deletions src/commands/agent/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import { Duration, sleep } from '@salesforce/kit';
import { MultiStageOutput } from '@oclif/multi-stage-output';
import { colorize } from '@oclif/core/ux';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.create');
Expand All @@ -29,24 +31,59 @@ export default class AgentCreate extends SfCommand<AgentCreateResult> {
public static readonly flags = {
'target-org': Flags.requiredOrg(),
'api-version': Flags.orgApiVersion(),
spec: Flags.file({
'job-spec': Flags.file({
char: 'f',
required: true,
summary: messages.getMessage('flags.spec.summary'),
description: messages.getMessage('flags.spec.description'),
summary: messages.getMessage('flags.job-spec.summary'),
description: messages.getMessage('flags.job-spec.description'),
}),
name: Flags.string({
char: 'n',
required: true,
summary: messages.getMessage('flags.name.summary'),
}),
};

public async run(): Promise<AgentCreateResult> {
const { flags } = await this.parse(AgentCreate);
const jsonParsingStage = `Parsing ${flags['job-spec']}`;
const mso = new MultiStageOutput({
jsonEnabled: this.jsonEnabled(),
title: `Creating ${flags.name} Agent`,
stages: [
jsonParsingStage,
'Generating GenAiPlanner metadata',
'Creating agent in org',
'Retrieving agent metadata',
],
});

mso.goto(jsonParsingStage);
await sleep(Duration.milliseconds(200));

mso.goto('Generating GenAiPlanner metadata');
await sleep(Duration.milliseconds(200));

this.log(`Creating agent from spec: ${flags.spec}`);
mso.goto('Creating agent in org');

// POST to /services/data/{api-version}/connect/attach-agent-topics

// To simulate time spent on the server generating the spec.
await sleep(Duration.seconds(5));

mso.goto('Retrieving agent metadata');
await sleep(Duration.seconds(3));

mso.stop();

this.log(
colorize(
'green',
`Successfully created ${flags.name} in ${flags['target-org'].getUsername() ?? 'the target org'}.`
)
);
this.log(`Use ${colorize('dim', `sf org open agent --name ${flags.name}`)} to view the agent in the browser.`);

return { isSuccess: true };
}
}
32 changes: 20 additions & 12 deletions src/commands/agent/generate/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import figures from '@inquirer/figures';
import { Agent, SfAgent } from '@salesforce/agents';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.create.spec');
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.generate.spec');

export type AgentCreateSpecResult = {
isSuccess: boolean;
Expand All @@ -40,12 +40,6 @@ type FlagsOfPrompts<T extends Record<string, FlaggablePrompt>> = Record<
>;

const FLAGGABLE_PROMPTS = {
name: {
message: messages.getMessage('flags.name.summary'),
validate: (d: string): boolean | string => d.length > 0 || 'Name cannot be empty',
char: 'n',
required: true,
},
type: {
message: messages.getMessage('flags.type.summary'),
validate: (d: string): boolean | string => d.length > 0 || 'Type cannot be empty',
Expand Down Expand Up @@ -124,6 +118,11 @@ export default class AgentCreateSpec extends SfCommand<AgentCreateSpecResult> {
summary: messages.getMessage('flags.output-dir.summary'),
default: 'config',
}),
'file-name': Flags.string({
char: 'f',
summary: messages.getMessage('flags.file-name.summary'),
default: 'agentSpec.json',
}),
};

public async run(): Promise<AgentCreateSpecResult> {
Expand All @@ -142,11 +141,15 @@ export default class AgentCreateSpec extends SfCommand<AgentCreateSpecResult> {

this.log();
this.styledHeader('Agent Details');
const name = await this.getFlagOrPrompt(flags.name, FLAGGABLE_PROMPTS.name);
const type = await this.getFlagOrPrompt(flags.type, FLAGGABLE_PROMPTS.type) as 'customer_facing' | 'employee_facing';
const type = (await this.getFlagOrPrompt(flags.type, FLAGGABLE_PROMPTS.type)) as
| 'customer_facing'
| 'employee_facing';
const role = await this.getFlagOrPrompt(flags.role, FLAGGABLE_PROMPTS.role);
const companyName = await this.getFlagOrPrompt(flags['company-name'], FLAGGABLE_PROMPTS['company-name']);
const companyDescription = await this.getFlagOrPrompt(flags['company-description'], FLAGGABLE_PROMPTS['company-description']);
const companyDescription = await this.getFlagOrPrompt(
flags['company-description'],
FLAGGABLE_PROMPTS['company-description']
);
const companyWebsite = await this.getFlagOrPrompt(flags['company-website'], FLAGGABLE_PROMPTS['company-website']);

this.log();
Expand All @@ -155,11 +158,16 @@ export default class AgentCreateSpec extends SfCommand<AgentCreateSpecResult> {
const connection = flags['target-org'].getConnection(flags['api-version']);
const agent = new Agent(connection, this.project as SfProject) as SfAgent;
const agentSpec = await agent.createSpec({
name, type, role, companyName, companyDescription, companyWebsite
name: flags['file-name'].split('.json')[0],
type,
role,
companyName,
companyDescription,
companyWebsite,
});

// Write a file with the returned job specs
const filePath = join(flags['output-dir'], 'agentSpec.json');
const filePath = join(flags['output-dir'], flags['file-name']);
writeFileSync(filePath, JSON.stringify(agentSpec, null, 4));

this.spinner.stop();
Expand Down
27 changes: 26 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,19 @@
wordwrap "^1.0.0"
wrap-ansi "^7.0.0"

"@oclif/multi-stage-output@^0.7.12":
version "0.7.12"
resolved "https://registry.yarnpkg.com/@oclif/multi-stage-output/-/multi-stage-output-0.7.12.tgz#04df5efb6dce527920cf475c9ad9f20236803ccd"
integrity sha512-MmCgqPb7jC7QUOiX9jMik2njplCO+tRybGxiB55OXDmZ7osifM3KuQb/ykgP4XYn559k3DXeNLFS0NpokuH+mw==
dependencies:
"@oclif/core" "^4"
"@types/react" "^18.3.12"
cli-spinners "^2"
figures "^6.1.0"
ink "^5.0.1"
react "^18.3.1"
wrap-ansi "^9.0.0"

"@oclif/plugin-command-snapshot@^5.2.19":
version "5.2.19"
resolved "https://registry.yarnpkg.com/@oclif/plugin-command-snapshot/-/plugin-command-snapshot-5.2.19.tgz#02f3f2c426aa0791bfcc598c9210e061f98caf54"
Expand Down Expand Up @@ -2966,7 +2979,7 @@ cli-progress@^3.12.0:
dependencies:
string-width "^4.2.3"

cli-spinners@^2.9.2:
cli-spinners@^2, cli-spinners@^2.9.2:
version "2.9.2"
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41"
integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==
Expand Down Expand Up @@ -3889,6 +3902,13 @@ faye@^1.4.0:
tough-cookie "*"
tunnel-agent "*"

figures@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a"
integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==
dependencies:
is-unicode-supported "^2.0.0"

file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
Expand Down Expand Up @@ -4788,6 +4808,11 @@ is-unicode-supported@^0.1.0:
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==

is-unicode-supported@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a"
integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==

is-weakref@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
Expand Down

0 comments on commit f5daa54

Please sign in to comment.