Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"command": "agent:activate",
"flagAliases": [],
"flagChars": ["n", "o"],
"flags": ["api-name", "api-version", "flags-dir", "json", "target-org"],
"flags": ["api-name", "api-version", "flags-dir", "json", "target-org", "version"],
"plugin": "@salesforce/plugin-agent"
},
{
Expand Down
4 changes: 4 additions & 0 deletions messages/agent.activate.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ You must know the agent's API name to activate it; you can either be prompted fo

API name of the agent to activate.

# flags.version.summary

Version number of the agent to activate.

# error.missingRequiredFlags

Missing required flags: %s.
19 changes: 19 additions & 0 deletions schemas/agent-activate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/AgentActivateResult",
"definitions": {
"AgentActivateResult": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
},
"version": {
"type": "number"
}
},
"required": ["success", "version"],
"additionalProperties": false
}
}
}
19 changes: 19 additions & 0 deletions schemas/agent-deactivate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/AgentActivateResult",
"definitions": {
"AgentActivateResult": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
},
"version": {
"type": "number"
}
},
"required": ["success", "version"],
"additionalProperties": false
}
}
}
82 changes: 79 additions & 3 deletions src/agentActivation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import { Messages, Org, SfError, SfProject } from '@salesforce/core';
import { Agent, type BotMetadata, ProductionAgent } from '@salesforce/agents';
import { Agent, type BotMetadata, type BotVersionMetadata, ProductionAgent } from '@salesforce/agents';
import { select } from '@inquirer/prompts';

type Choice<Value> = {
Expand All @@ -28,6 +28,11 @@ type AgentValue = {
DeveloperName: string;
};

type VersionChoice = {
version: number;
status: string;
};

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

Expand All @@ -48,8 +53,10 @@ export const getAgentChoices = (agents: BotMetadata[], status: 'Active' | 'Inact
agents.map((agent) => {
let disabled: string | boolean = false;

const lastBotVersion = agent.BotVersions.records[agent.BotVersions.records.length - 1];
if (lastBotVersion.Status === status) {
// For deactivate (status='Inactive'), check if any version is Active (can be deactivated)
// For activate (status='Active'), check if any version is Inactive (can be activated)
const hasAvailableVersion = agent.BotVersions.records.some((version) => version.Status !== status);
if (!hasAvailableVersion) {
disabled = `(Already ${status})`;
}
if (agentIsUnsupported(agent.DeveloperName)) {
Expand All @@ -66,6 +73,22 @@ export const getAgentChoices = (agents: BotMetadata[], status: 'Active' | 'Inact
};
});

export const getVersionChoices = (
versions: BotVersionMetadata[],
status: 'Active' | 'Inactive'
): Array<Choice<VersionChoice>> =>
versions.map((version) => {
const isTargetStatus = version.Status === status;
return {
name: `Version ${version.VersionNumber}`,
value: {
version: version.VersionNumber,
status: version.Status,
},
disabled: isTargetStatus ? `(Already ${status})` : false,
};
});

export const getAgentForActivation = async (config: {
targetOrg: Org;
status: 'Active' | 'Inactive';
Expand Down Expand Up @@ -110,3 +133,56 @@ export const getAgentForActivation = async (config: {
project: SfProject.getInstance(),
});
};

export const getVersionForActivation = async (config: {
agent: ProductionAgent;
status: 'Active' | 'Inactive';
versionFlag?: number;
jsonEnabled?: boolean;
}): Promise<{ version: number | undefined; warning?: string }> => {
const { agent, status, versionFlag, jsonEnabled } = config;

// If version flag is provided, return it
if (versionFlag !== undefined) {
return { version: versionFlag };
}

// Get bot metadata to access versions
const botMetadata = await agent.getBotMetadata();
// Filter out deleted versions as a defensive measure
const versions = botMetadata.BotVersions.records.filter((v) => !v.IsDeleted);

// If there's only one version, return it
if (versions.length === 1) {
return { version: versions[0].VersionNumber };
}

// Get version choices and filter out disabled ones
const choices = getVersionChoices(versions, status);
const availableChoices = choices.filter((choice) => !choice.disabled);

// If there's only one available choice, return it automatically
if (availableChoices.length === 1) {
return { version: availableChoices[0].value.version };
}

// If JSON mode is enabled, automatically select the latest available version
if (jsonEnabled) {
// Find the latest (highest version number) available version
const latestVersion = availableChoices.reduce((latest, choice) =>
choice.value.version > latest.value.version ? choice : latest
);
return {
version: latestVersion.value.version,
warning: `No version specified, automatically selected latest available version: ${latestVersion.value.version}`,
};
}

// Prompt user to select a version
const versionChoice = await select({
message: 'Select a version',
choices,
});

return { version: versionChoice.version };
};
27 changes: 20 additions & 7 deletions src/commands/agent/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@
*/
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import { getAgentForActivation } from '../../agentActivation.js';
import { getAgentForActivation, getVersionForActivation } from '../../agentActivation.js';

export type AgentActivateResult = { success: boolean; version: number };

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

export default class AgentActivate extends SfCommand<void> {
export default class AgentActivate extends SfCommand<AgentActivateResult> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
public static readonly enableJsonFlag = true;

public static readonly flags = {
'target-org': Flags.requiredOrg(),
Expand All @@ -32,9 +35,10 @@ export default class AgentActivate extends SfCommand<void> {
summary: messages.getMessage('flags.api-name.summary'),
char: 'n',
}),
version: Flags.integer({ summary: messages.getMessage('flags.version.summary') }),
};

public async run(): Promise<void> {
public async run(): Promise<AgentActivateResult> {
const { flags } = await this.parse(AgentActivate);

const apiNameFlag = flags['api-name'];
Expand All @@ -43,11 +47,20 @@ export default class AgentActivate extends SfCommand<void> {
if (!apiNameFlag && this.jsonEnabled()) {
throw messages.createError('error.missingRequiredFlags', ['api-name']);
}

const agent = await getAgentForActivation({ targetOrg, status: 'Active', apiNameFlag });
await agent.activate();
const agentName = (await agent.getBotMetadata()).DeveloperName;
const { version, warning } = await getVersionForActivation({
agent,
status: 'Active',
versionFlag: flags.version,
jsonEnabled: this.jsonEnabled(),
});
const result = await agent.activate(version);
const metadata = await agent.getBotMetadata();

this.log(`Agent ${agentName} activated.`);
this.log(`${metadata.DeveloperName} v${result.VersionNumber} activated.`);
if (warning) {
this.warn(warning);
}
return { success: true, version: result.VersionNumber };
}
}
12 changes: 7 additions & 5 deletions src/commands/agent/deactivate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import { getAgentForActivation } from '../../agentActivation.js';
import { AgentActivateResult } from './activate.js';

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

export default class AgentDeactivate extends SfCommand<void> {
export default class AgentDeactivate extends SfCommand<AgentActivateResult> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
Expand All @@ -34,7 +35,7 @@ export default class AgentDeactivate extends SfCommand<void> {
}),
};

public async run(): Promise<void> {
public async run(): Promise<AgentActivateResult> {
const { flags } = await this.parse(AgentDeactivate);

const apiNameFlag = flags['api-name'];
Expand All @@ -45,9 +46,10 @@ export default class AgentDeactivate extends SfCommand<void> {
}

const agent = await getAgentForActivation({ targetOrg, status: 'Inactive', apiNameFlag });
await agent.deactivate();
const agentName = (await agent.getBotMetadata()).DeveloperName;
const result = await agent.deactivate();
const metadata = await agent.getBotMetadata();

this.log(`Agent ${agentName} deactivated.`);
this.log(`${metadata.DeveloperName} v${result.VersionNumber} deactivated.`);
return { success: true, version: result.VersionNumber };
}
}
Loading
Loading