Skip to content

Commit b9d7d14

Browse files
Merge branch 'wr/nga' into wr/addMSO
2 parents 5d63fa9 + c7605b7 commit b9d7d14

11 files changed

+156
-51
lines changed

command-snapshot.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"command": "agent:generate:authoring-bundle",
5757
"flagAliases": [],
5858
"flagChars": ["d", "f", "n", "o"],
59-
"flags": ["api-version", "flags-dir", "json", "name", "output-dir", "spec", "target-org"],
59+
"flags": ["api-name", "api-version", "flags-dir", "json", "name", "output-dir", "spec", "target-org"],
6060
"plugin": "@salesforce/plugin-agent"
6161
},
6262
{

messages/agent.generate.authoring-bundle.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Generate an authoring bundle from an agent specification.
44

55
# description
66

7-
Generates an authoring bundle containing AFScript and its meta.xml file from an agent specification file.
7+
Generates an authoring bundle containing Agent and its meta.xml file from an agent specification file.
88

99
# flags.spec.summary
1010

@@ -16,11 +16,15 @@ Directory where the authoring bundle files will be generated.
1616

1717
# flags.name.summary
1818

19-
Name (label) of the authoring bundle. If not provided, you will be prompted for it.
19+
Name (label) of the authoring bundle.
2020

21-
# flags.name.prompt
21+
# flags.api-name.summary
2222

23-
Name (label) of the authoring bundle
23+
API name of the new authoring bundle; if not specified, the API name is derived from the authoring bundle name (label); the API name must not exist in the org.
24+
25+
# flags.api-name.prompt
26+
27+
API name of the new authoring bundle
2428

2529
# examples
2630

@@ -38,6 +42,6 @@ No agent specification file found at the specified path.
3842

3943
The specified file is not a valid agent specification file.
4044

41-
# error.failed-to-create-afscript
45+
# error.failed-to-create-agent
4246

43-
Failed to create AFScript from the agent specification.
47+
Failed to create Agent from the agent specification.

messages/agent.preview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ API name of the agent you want to interact with.
2020

2121
# flags.authoring-bundle.summary
2222

23-
Preview an ephemeral afscript agent by specifying the API name of the Authoring Bundle metadata
23+
Preview an ephemeral agent by specifying the API name of the Authoring Bundle metadata
2424

2525
# flags.client-app.summary
2626

messages/agent.publish.authoring-bundle.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Publishes an Agent Authoring Bundle by compiling the AF script and creating a ne
1515

1616
API name of the Agent Authoring Bundle to publish
1717

18+
# flags.api-name.prompt
19+
20+
API name of the authoring bundle to publish
21+
1822
# flags.agent-name.summary
1923

2024
Name for the new agent to be created
@@ -32,10 +36,10 @@ Invalid bundle path. Please provide a valid path to an Agent Authoring Bundle.
3236
Failed to publish agent with the following errors:
3337
%s
3438

35-
# error.afscriptNotFound
39+
# error.agentNotFound
3640

3741
Could not find an .agent file with API name '%s' in the project.
3842

39-
# error.afscriptNotFoundAction
43+
# error.agentNotFoundAction
4044

4145
Please check that the API name is correct and that the .agent file exists in your project directory.

messages/agent.validate.authoring-bundle.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Validates an Agent Authoring Bundle by compiling the AF script and checking for
1515

1616
Path to the Agent Authoring Bundle to validate
1717

18+
# flags.api-name.prompt
19+
20+
API name of the authoring bundle to validate
21+
1822
# error.missingRequiredFlags
1923

2024
Required flag(s) missing: %s
@@ -28,10 +32,10 @@ Invalid bundle path. Please provide a valid path to an Agent Authoring Bundle.
2832
AF Script compilation failed with the following errors:
2933
%s
3034

31-
# error.afscriptNotFound
35+
# error.agentNotFound
3236

3337
Could not find an .agent file with API name '%s' in the project.
3438

35-
# error.afscriptNotFoundAction
39+
# error.agentNotFoundAction
3640

3741
Please check that the API name is correct and that the .agent file exists in your project directory.

schemas/agent-generate-authoring__bundle.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"AgentGenerateAuthoringBundleResult": {
66
"type": "object",
77
"properties": {
8-
"afScriptPath": {
8+
"agentPath": {
99
"type": "string"
1010
},
1111
"metaXmlPath": {
@@ -15,7 +15,7 @@
1515
"type": "string"
1616
}
1717
},
18-
"required": ["afScriptPath", "metaXmlPath", "outputDir"],
18+
"required": ["agentPath", "metaXmlPath", "outputDir"],
1919
"additionalProperties": false
2020
}
2121
}

src/commands/agent/generate/authoring-bundle.ts

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,21 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { join } from 'node:path';
18-
import { mkdirSync, writeFileSync, readFileSync } from 'node:fs';
17+
import { join, resolve } from 'node:path';
18+
import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
1919
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
20-
import { Messages, SfError } from '@salesforce/core';
20+
import { generateApiName, Messages, SfError } from '@salesforce/core';
2121
import { Agent, AgentJobSpec } from '@salesforce/agents';
2222
import YAML from 'yaml';
23-
import { FlaggablePrompt, promptForFlag } from '../../../flags.js';
23+
import { input as inquirerInput } from '@inquirer/prompts';
24+
import { theme } from '../../../inquirer-theme.js';
25+
import { FlaggablePrompt, promptForFlag, promptForYamlFile } from '../../../flags.js';
2426

2527
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2628
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.generate.authoring-bundle');
2729

2830
export type AgentGenerateAuthoringBundleResult = {
29-
afScriptPath: string;
31+
agentPath: string;
3032
metaXmlPath: string;
3133
outputDir: string;
3234
};
@@ -39,6 +41,9 @@ export default class AgentGenerateAuthoringBundle extends SfCommand<AgentGenerat
3941

4042
public static readonly flags = {
4143
'target-org': Flags.requiredOrg(),
44+
'api-name': Flags.string({
45+
summary: messages.getMessage('flags.api-name.summary'),
46+
}),
4247
'api-version': Flags.orgApiVersion(),
4348
spec: Flags.file({
4449
summary: messages.getMessage('flags.spec.summary'),
@@ -58,13 +63,36 @@ export default class AgentGenerateAuthoringBundle extends SfCommand<AgentGenerat
5863
private static readonly FLAGGABLE_PROMPTS = {
5964
name: {
6065
message: messages.getMessage('flags.name.summary'),
61-
promptMessage: messages.getMessage('flags.name.prompt'),
62-
validate: (d: string): boolean | string => d.length > 0 || 'Name cannot be empty',
66+
validate: (d: string): boolean | string =>
67+
d.trim().length > 0 || 'Name cannot be empty or contain only whitespace',
6368
required: true,
6469
},
70+
'api-name': {
71+
message: messages.getMessage('flags.api-name.summary'),
72+
promptMessage: messages.getMessage('flags.api-name.prompt'),
73+
validate: (d: string): boolean | string => {
74+
if (d.length === 0) {
75+
return true;
76+
}
77+
if (d.length > 80) {
78+
return 'API name cannot be over 80 characters.';
79+
}
80+
const regex = /^[A-Za-z][A-Za-z0-9_]*[A-Za-z0-9]+$/;
81+
if (!regex.test(d)) {
82+
return 'Invalid API name.';
83+
}
84+
return true;
85+
},
86+
},
6587
spec: {
6688
message: messages.getMessage('flags.spec.summary'),
67-
validate: (d: string): boolean | string => d.length > 0 || 'Spec file path cannot be empty',
89+
validate: (d: string): boolean | string => {
90+
const specPath = resolve(d);
91+
if (!existsSync(specPath)) {
92+
return 'Please enter an existing agent spec (yaml) file';
93+
}
94+
return true;
95+
},
6896
required: true,
6997
},
7098
} satisfies Record<string, FlaggablePrompt>;
@@ -74,29 +102,42 @@ export default class AgentGenerateAuthoringBundle extends SfCommand<AgentGenerat
74102
const { 'output-dir': outputDir, 'target-org': targetOrg } = flags;
75103

76104
// If we don't have a spec yet, prompt for it
77-
const spec = flags['spec'] ?? (await promptForFlag(AgentGenerateAuthoringBundle.FLAGGABLE_PROMPTS['spec']));
105+
const spec = flags.spec ?? (await promptForYamlFile(AgentGenerateAuthoringBundle.FLAGGABLE_PROMPTS['spec']));
78106

79107
// If we don't have a name yet, prompt for it
80-
const name = (
81-
flags['name'] ?? (await promptForFlag(AgentGenerateAuthoringBundle.FLAGGABLE_PROMPTS['name']))
82-
).replaceAll(' ', '_');
108+
const name = flags['name'] ?? (await promptForFlag(AgentGenerateAuthoringBundle.FLAGGABLE_PROMPTS['name']));
109+
110+
// If we don't have an api name yet, prompt for it
111+
let bundleApiName = flags['api-name'];
112+
if (!bundleApiName) {
113+
bundleApiName = generateApiName(name);
114+
const promptedValue = await inquirerInput({
115+
message: messages.getMessage('flags.api-name.prompt'),
116+
validate: AgentGenerateAuthoringBundle.FLAGGABLE_PROMPTS['api-name'].validate,
117+
default: bundleApiName,
118+
theme,
119+
});
120+
if (promptedValue?.length) {
121+
bundleApiName = promptedValue;
122+
}
123+
}
83124

84125
try {
85126
// Get default output directory if not specified
86127
const defaultOutputDir = join(this.project!.getDefaultPackage().fullPath, 'main', 'default');
87128
const targetOutputDir = join(outputDir ?? defaultOutputDir, 'aiAuthoringBundles', name);
88129

89130
// Generate file paths
90-
const afScriptPath = join(targetOutputDir, `${name}.agent`);
131+
const agentPath = join(targetOutputDir, `${name}.agent`);
91132
const metaXmlPath = join(targetOutputDir, `${name}.aiAuthoringBundle-meta.xml`);
92133

93-
// Write AFScript file
134+
// Write Agent file
94135
const conn = targetOrg.getConnection(flags['api-version']);
95136
const specContents = YAML.parse(readFileSync(spec, 'utf8')) as AgentJobSpec;
96-
const afScript = await Agent.createAfScript(conn, specContents);
137+
const agent = await Agent.createAfScript(conn, specContents);
97138
// Create output directory if it doesn't exist
98139
mkdirSync(targetOutputDir, { recursive: true });
99-
writeFileSync(afScriptPath, afScript);
140+
writeFileSync(agentPath, agent);
100141

101142
// Write meta.xml file
102143
const metaXml = `<?xml version="1.0" encoding="UTF-8"?>
@@ -113,15 +154,13 @@ export default class AgentGenerateAuthoringBundle extends SfCommand<AgentGenerat
113154
this.logSuccess(`Successfully generated ${name} Authoring Bundle`);
114155

115156
return {
116-
afScriptPath,
157+
agentPath,
117158
metaXmlPath,
118159
outputDir: targetOutputDir,
119160
};
120161
} catch (error) {
121162
const err = SfError.wrap(error);
122-
throw new SfError(messages.getMessage('error.failed-to-create-afscript'), 'AfScriptGenerationError', [
123-
err.message,
124-
]);
163+
throw new SfError(messages.getMessage('error.failed-to-create-agent'), 'AgentGenerationError', [err.message]);
125164
}
126165
}
127166
}

src/commands/agent/publish/authoring-bundle.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Messages, Lifecycle, SfError } from '@salesforce/core';
2222
import { Agent, findAuthoringBundle } from '@salesforce/agents';
2323
import { RequestStatus, type ScopedPostRetrieve } from '@salesforce/source-deploy-retrieve';
2424
import { ensureArray } from '@salesforce/kit';
25+
import { FlaggablePrompt, promptForFlag } from '../../../flags.js';
2526

2627
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2728
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.publish.authoring-bundle');
@@ -44,26 +45,45 @@ export default class AgentPublishAuthoringBundle extends SfCommand<AgentPublishA
4445
'api-name': Flags.string({
4546
char: 'n',
4647
summary: messages.getMessage('flags.api-name.summary'),
47-
required: true,
4848
}),
4949
};
5050

51+
private static readonly FLAGGABLE_PROMPTS = {
52+
'api-name': {
53+
message: messages.getMessage('flags.api-name.summary'),
54+
promptMessage: messages.getMessage('flags.api-name.prompt'),
55+
validate: (d: string): boolean | string => {
56+
if (d.length > 80) {
57+
return 'API name cannot be over 80 characters.';
58+
}
59+
const regex = /^[A-Za-z][A-Za-z0-9_]*[A-Za-z0-9]+$/;
60+
if (d.length === 0 || !regex.test(d)) {
61+
return 'Invalid API name.';
62+
}
63+
return true;
64+
},
65+
},
66+
} satisfies Record<string, FlaggablePrompt>;
67+
5168
public async run(): Promise<AgentPublishAuthoringBundleResult> {
5269
const { flags } = await this.parse(AgentPublishAuthoringBundle);
70+
// If we don't have an api name yet, prompt for it
71+
const apiName =
72+
flags['api-name'] ?? (await promptForFlag(AgentPublishAuthoringBundle.FLAGGABLE_PROMPTS['api-name']));
5373
// todo: this eslint warning can be removed once published
5474
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
55-
const authoringBundleDir = findAuthoringBundle(this.project!.getPath(), flags['api-name']);
75+
const authoringBundleDir = findAuthoringBundle(this.project!.getPath(), apiName);
5676

5777
if (!authoringBundleDir) {
58-
throw new SfError(messages.getMessage('error.afscriptNotFound', [flags['api-name']]), 'AfScriptNotFoundError', [
59-
messages.getMessage('error.afscriptNotFoundAction'),
78+
throw new SfError(messages.getMessage('error.agentNotFound', [apiName]), 'AgentNotFoundError', [
79+
messages.getMessage('error.agentNotFoundAction'),
6080
]);
6181
}
6282
// Create multi-stage output
6383
const mso = new MultiStageOutput<{ agentName: string }>({
6484
stages: ['Validate Bundle', 'Publish Agent', 'Retrieve Metadata'],
6585
title: 'Publishing Agent',
66-
data: { agentName: flags['api-name'] },
86+
data: { agentName: apiName },
6787
jsonEnabled: this.jsonEnabled(),
6888
postStagesBlock: [
6989
{
@@ -83,7 +103,7 @@ export default class AgentPublishAuthoringBundle extends SfCommand<AgentPublishA
83103
// First compile the AF script to get the Agent JSON
84104
const agentJson = await Agent.compileAfScript(
85105
conn,
86-
readFileSync(join(authoringBundleDir, `${flags['api-name']}.agent`), 'utf8')
106+
readFileSync(join(authoringBundleDir, `${apiName}.agent`), 'utf8')
87107
);
88108
mso.skipTo('Publish Agent');
89109

src/commands/agent/validate/authoring-bundle.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { MultiStageOutput } from '@oclif/multi-stage-output';
2121
import { Agent, findAuthoringBundle } from '@salesforce/agents';
2222
import { Duration, sleep } from '@salesforce/kit';
2323
import { colorize } from '@oclif/core/ux';
24+
import { FlaggablePrompt, promptForFlag } from '../../../flags.js';
2425

2526
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2627
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.validate.authoring-bundle');
@@ -42,16 +43,37 @@ export default class AgentValidateAuthoringBundle extends SfCommand<AgentValidat
4243
'api-name': Flags.string({
4344
char: 'n',
4445
summary: messages.getMessage('flags.api-name.summary'),
45-
required: true,
4646
}),
4747
};
4848

49+
private static readonly FLAGGABLE_PROMPTS = {
50+
'api-name': {
51+
message: messages.getMessage('flags.api-name.summary'),
52+
promptMessage: messages.getMessage('flags.api-name.prompt'),
53+
validate: (d: string): boolean | string => {
54+
if (d.length > 80) {
55+
return 'API name cannot be over 80 characters.';
56+
}
57+
const regex = /^[A-Za-z][A-Za-z0-9_]*[A-Za-z0-9]+$/;
58+
if (d.length === 0 || !regex.test(d)) {
59+
return 'Invalid API name.';
60+
}
61+
return true;
62+
},
63+
},
64+
} satisfies Record<string, FlaggablePrompt>;
65+
4966
public async run(): Promise<AgentValidateAuthoringBundleResult> {
5067
const { flags } = await this.parse(AgentValidateAuthoringBundle);
51-
const authoringBundleDir = findAuthoringBundle(this.project!.getPath(), flags['api-name']);
68+
// If we don't have an api name yet, prompt for it
69+
const apiName =
70+
flags['api-name'] ?? (await promptForFlag(AgentValidateAuthoringBundle.FLAGGABLE_PROMPTS['api-name']));
71+
// todo: this eslint warning can be removed once published
72+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
73+
const authoringBundleDir = findAuthoringBundle(this.project!.getPath(), apiName);
5274
if (!authoringBundleDir) {
53-
throw new SfError(messages.getMessage('error.afscriptNotFound', [flags['api-name']]), 'AfScriptNotFoundError', [
54-
messages.getMessage('error.afscriptNotFoundAction'),
75+
throw new SfError(messages.getMessage('error.agentNotFound', [apiName]), 'AgentNotFoundError', [
76+
messages.getMessage('error.agentNotFoundAction'),
5577
]);
5678
}
5779
const mso = new MultiStageOutput<{ status: string; errors: string }>({
@@ -81,7 +103,7 @@ export default class AgentValidateAuthoringBundle extends SfCommand<AgentValidat
81103
const conn = targetOrg.getConnection(flags['api-version']);
82104
// Call Agent.compileAfScript() API
83105
await sleep(Duration.seconds(2));
84-
await Agent.compileAfScript(conn, readFileSync(join(authoringBundleDir, `${flags['api-name']}.agent`), 'utf8'));
106+
await Agent.compileAfScript(conn, readFileSync(join(authoringBundleDir, `${!apiName}.agent`), 'utf8'));
85107
mso.updateData({ status: 'COMPLETED' });
86108
mso.stop('completed');
87109
return {

0 commit comments

Comments
 (0)