Skip to content

Commit 633e484

Browse files
committed
feat: add multi-stage-output
1 parent 1404caf commit 633e484

File tree

7 files changed

+111
-26
lines changed

7 files changed

+111
-26
lines changed

command-snapshot.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
"flagAliases": [],
66
"flagChars": [
77
"f",
8+
"n",
89
"o"
910
],
1011
"flags": [
1112
"api-version",
1213
"flags-dir",
14+
"job-spec",
1315
"json",
14-
"spec",
16+
"name",
1517
"target-org"
1618
],
1719
"plugin": "@salesforce/plugin-agent"
@@ -22,7 +24,7 @@
2224
"flagAliases": [],
2325
"flagChars": [
2426
"d",
25-
"n",
27+
"f",
2628
"o",
2729
"t"
2830
],
@@ -31,9 +33,9 @@
3133
"company-description",
3234
"company-name",
3335
"company-website",
36+
"file-name",
3437
"flags-dir",
3538
"json",
36-
"name",
3739
"output-dir",
3840
"role",
3941
"target-org",

messages/agent.create.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ Create an Agent from an agent spec.
66

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

9-
# flags.spec.summary
9+
# flags.job-spec.summary
1010

1111
The path to an agent spec file.
1212

13-
# flags.spec.description
13+
# flags.job-spec.description
1414

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

17+
# flags.name.summary
18+
19+
The name of the agent.
20+
1721
# examples
1822

1923
- Create an Agent:

messages/agent.create.spec.md renamed to messages/agent.generate.spec.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ Create an Agent spec.
66

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

9-
# flags.name.summary
10-
11-
The name of the agent to create.
12-
139
# flags.type.summary
1410

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

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

33+
# flags.file-name.summary
34+
35+
The name of the file to write the agent spec to.
36+
3737
# examples
3838

3939
- Create an Agent spec in the default location:

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"@inquirer/input": "^4.0.1",
1010
"@inquirer/select": "^4.0.1",
1111
"@oclif/core": "^4",
12+
"@oclif/multi-stage-output": "^0.7.12",
1213
"@salesforce/core": "^8.5.2",
1314
"@salesforce/kit": "^3.2.1",
1415
"@salesforce/sf-plugins-core": "^12",

src/commands/agent/create.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
99
import { Messages } from '@salesforce/core';
1010
import { Duration, sleep } from '@salesforce/kit';
11+
import { MultiStageOutput } from '@oclif/multi-stage-output';
12+
import { colorize } from '@oclif/core/ux';
1113

1214
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
1315
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.create');
@@ -29,24 +31,66 @@ export default class AgentCreate extends SfCommand<AgentCreateResult> {
2931
public static readonly flags = {
3032
'target-org': Flags.requiredOrg(),
3133
'api-version': Flags.orgApiVersion(),
32-
spec: Flags.file({
34+
'job-spec': Flags.file({
3335
char: 'f',
3436
required: true,
35-
summary: messages.getMessage('flags.spec.summary'),
36-
description: messages.getMessage('flags.spec.description'),
37+
summary: messages.getMessage('flags.job-spec.summary'),
38+
description: messages.getMessage('flags.job-spec.description'),
39+
}),
40+
name: Flags.string({
41+
char: 'n',
42+
required: true,
43+
summary: messages.getMessage('flags.name.summary'),
3744
}),
3845
};
3946

4047
public async run(): Promise<AgentCreateResult> {
4148
const { flags } = await this.parse(AgentCreate);
49+
const mso = new MultiStageOutput<{ file?: string }>({
50+
jsonEnabled: this.jsonEnabled(),
51+
title: `Creating ${flags.name} Agent`,
52+
stages: [
53+
'Parsing agent spec JSON',
54+
'Generating GenAiPlanner metadata',
55+
'Creating agent in org',
56+
'Retrieving agent metadata',
57+
],
58+
stageSpecificBlock: [
59+
{
60+
stage: 'Parsing agent spec JSON',
61+
get: (data) => data?.file,
62+
type: 'static-key-value',
63+
label: 'file',
64+
},
65+
],
66+
});
67+
68+
mso.goto('Parsing agent spec JSON', { file: flags['job-spec'] });
69+
await sleep(Duration.milliseconds(200));
70+
71+
mso.goto('Generating GenAiPlanner metadata');
72+
await sleep(Duration.milliseconds(200));
4273

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

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

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

81+
mso.goto('Retrieving agent metadata');
82+
await sleep(Duration.seconds(3));
83+
84+
mso.stop();
85+
86+
this.log(
87+
colorize(
88+
'green',
89+
`Successfully created ${flags.name} in ${flags['target-org'].getUsername() ?? 'the target org'}.`
90+
)
91+
);
92+
this.log(`Use ${colorize('dim', `sf agent open --agent ${flags.name}`)} to view the agent in the browser.`);
93+
5094
return { isSuccess: true };
5195
}
5296
}

src/commands/agent/generate/spec.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ import ansis from 'ansis';
1313
import select from '@inquirer/select';
1414
import inquirerInput from '@inquirer/input';
1515
import figures from '@inquirer/figures';
16+
// eslint-disable-next-line import/no-extraneous-dependencies
1617
import { Agent, SfAgent } from '@salesforce/agents';
1718

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

2122
export type AgentCreateSpecResult = {
2223
isSuccess: boolean;
@@ -40,12 +41,6 @@ type FlagsOfPrompts<T extends Record<string, FlaggablePrompt>> = Record<
4041
>;
4142

4243
const FLAGGABLE_PROMPTS = {
43-
name: {
44-
message: messages.getMessage('flags.name.summary'),
45-
validate: (d: string): boolean | string => d.length > 0 || 'Name cannot be empty',
46-
char: 'n',
47-
required: true,
48-
},
4944
type: {
5045
message: messages.getMessage('flags.type.summary'),
5146
validate: (d: string): boolean | string => d.length > 0 || 'Type cannot be empty',
@@ -124,6 +119,11 @@ export default class AgentCreateSpec extends SfCommand<AgentCreateSpecResult> {
124119
summary: messages.getMessage('flags.output-dir.summary'),
125120
default: 'config',
126121
}),
122+
'file-name': Flags.string({
123+
char: 'f',
124+
summary: messages.getMessage('flags.file-name.summary'),
125+
default: 'agentSpec.json',
126+
}),
127127
};
128128

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

143143
this.log();
144144
this.styledHeader('Agent Details');
145-
const name = await this.getFlagOrPrompt(flags.name, FLAGGABLE_PROMPTS.name);
146-
const type = await this.getFlagOrPrompt(flags.type, FLAGGABLE_PROMPTS.type) as 'customer_facing' | 'employee_facing';
145+
const type = (await this.getFlagOrPrompt(flags.type, FLAGGABLE_PROMPTS.type)) as
146+
| 'customer_facing'
147+
| 'employee_facing';
147148
const role = await this.getFlagOrPrompt(flags.role, FLAGGABLE_PROMPTS.role);
148149
const companyName = await this.getFlagOrPrompt(flags['company-name'], FLAGGABLE_PROMPTS['company-name']);
149-
const companyDescription = await this.getFlagOrPrompt(flags['company-description'], FLAGGABLE_PROMPTS['company-description']);
150+
const companyDescription = await this.getFlagOrPrompt(
151+
flags['company-description'],
152+
FLAGGABLE_PROMPTS['company-description']
153+
);
150154
const companyWebsite = await this.getFlagOrPrompt(flags['company-website'], FLAGGABLE_PROMPTS['company-website']);
151155

152156
this.log();
@@ -155,11 +159,16 @@ export default class AgentCreateSpec extends SfCommand<AgentCreateSpecResult> {
155159
const connection = flags['target-org'].getConnection(flags['api-version']);
156160
const agent = new Agent(connection, this.project as SfProject) as SfAgent;
157161
const agentSpec = await agent.createSpec({
158-
name, type, role, companyName, companyDescription, companyWebsite
162+
name: flags['file-name'].split('.json')[0],
163+
type,
164+
role,
165+
companyName,
166+
companyDescription,
167+
companyWebsite,
159168
});
160169

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

165174
this.spinner.stop();

yarn.lock

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,19 @@
12931293
wordwrap "^1.0.0"
12941294
wrap-ansi "^7.0.0"
12951295

1296+
"@oclif/multi-stage-output@^0.7.12":
1297+
version "0.7.12"
1298+
resolved "https://registry.yarnpkg.com/@oclif/multi-stage-output/-/multi-stage-output-0.7.12.tgz#04df5efb6dce527920cf475c9ad9f20236803ccd"
1299+
integrity sha512-MmCgqPb7jC7QUOiX9jMik2njplCO+tRybGxiB55OXDmZ7osifM3KuQb/ykgP4XYn559k3DXeNLFS0NpokuH+mw==
1300+
dependencies:
1301+
"@oclif/core" "^4"
1302+
"@types/react" "^18.3.12"
1303+
cli-spinners "^2"
1304+
figures "^6.1.0"
1305+
ink "^5.0.1"
1306+
react "^18.3.1"
1307+
wrap-ansi "^9.0.0"
1308+
12961309
"@oclif/plugin-command-snapshot@^5.2.19":
12971310
version "5.2.19"
12981311
resolved "https://registry.yarnpkg.com/@oclif/plugin-command-snapshot/-/plugin-command-snapshot-5.2.19.tgz#02f3f2c426aa0791bfcc598c9210e061f98caf54"
@@ -2958,7 +2971,7 @@ cli-progress@^3.12.0:
29582971
dependencies:
29592972
string-width "^4.2.3"
29602973

2961-
cli-spinners@^2.9.2:
2974+
cli-spinners@^2, cli-spinners@^2.9.2:
29622975
version "2.9.2"
29632976
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41"
29642977
integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==
@@ -3881,6 +3894,13 @@ faye@^1.4.0:
38813894
tough-cookie "*"
38823895
tunnel-agent "*"
38833896

3897+
figures@^6.1.0:
3898+
version "6.1.0"
3899+
resolved "https://registry.yarnpkg.com/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a"
3900+
integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==
3901+
dependencies:
3902+
is-unicode-supported "^2.0.0"
3903+
38843904
file-entry-cache@^6.0.1:
38853905
version "6.0.1"
38863906
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -4780,6 +4800,11 @@ is-unicode-supported@^0.1.0:
47804800
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
47814801
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
47824802

4803+
is-unicode-supported@^2.0.0:
4804+
version "2.1.0"
4805+
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a"
4806+
integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==
4807+
47834808
is-weakref@^1.0.2:
47844809
version "1.0.2"
47854810
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"

0 commit comments

Comments
 (0)