Skip to content
Merged
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
4 changes: 2 additions & 2 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@
"alias": [],
"command": "agent:preview",
"flagAliases": [],
"flagChars": ["a", "d", "n", "o", "x"],
"flags": ["apex-debug", "api-name", "api-version", "connected-app-user", "flags-dir", "output-dir", "target-org"],
"flagChars": ["c", "d", "n", "o", "x"],
"flags": ["apex-debug", "api-name", "api-version", "client-app", "flags-dir", "output-dir", "target-org"],
"plugin": "@salesforce/plugin-agent"
},
{
Expand Down
28 changes: 13 additions & 15 deletions messages/agent.preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,35 @@ Find the agent's API name in its main details page in your org's Agent page in S

Before you use this command, you must complete these steps:

1. Create a connected app in your org as described in the "Create a Connected App" section here: https://developer.salesforce.com/docs/einstein/genai/guide/agent-api-get-started.html#create-a-connected-app. Do these four additional steps:
1. Using your org's Setup UI, create a connected app in your org as described in the "Create a Connected App" section here: https://developer.salesforce.com/docs/einstein/genai/guide/agent-api-get-started.html#create-a-connected-app. Do these additional steps:

a. When specifying the connected app's Callback URL, add this second callback URL on a new line: "http://localhost:1717/OauthRedirect".

b. When adding the scopes to the connected app, add "Manage user data via Web browsers (web)".

c. Ensure that the "Require Secret for Web Server Flow" option is not selected.

d. Make note of the user that you specified as the "Run As" user when updating the Client Credentials Flow section.

2. Add the connected app to your agent as described in the "Add Connected App to Agent" section here: https://developer.salesforce.com/docs/einstein/genai/guide/agent-api-get-started.html#add-connected-app-to-agent.

3. Copy the consumer key from your connected app as described in the "Obtain Credentials" section here: https://developer.salesforce.com/docs/einstein/genai/guide/agent-api-get-started.html#obtain-credentials.

4. Set the "SFDX_AUTH_SCOPES" environment variable to "refresh_token sfap_api chatbot_api web api". This step ensures that you get the specific OAuth scopes required by this command.
4. If you haven't already, run the "org login web" CLI command as usual to authorize the development org that contains the agent you want to preview.

5. Re-run the "org web login" command to link the new connected app to your already-authenticated user. Use the --client-app flag to give the link a name; you can specify any string, but make a note of it because you'll need it later. Use --username to specify the username that you used to log into the org in the previous step. Use --client-id to specify the consumer key you previously copied. Finally, use --scopes as indicated to specify the required API scopes. Here's an example:

5. Using the username of the user you specified as the "Run As" user above, authorize your org using the web server flow, as described in this document: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_web_flow.htm.
sf org login web --client-app agent-app --username <username> --client-id <consumer-key> --scopes "sfap_api chatbot_api refresh_token api web"

IMPORTANT: You must use the "--client-id <CONNECTED-APP-CONSUMER-KEY>" flag of "org login web", where CONNECTED-APP-CONSUMER-KEY is the consumer key you previously copied. This step ensures that the "org login web" command uses your custom connected app, and not the default CLI connected app.
IMPORTANT: You must use the "--client-id <CONNECTED-APP-CONSUMER-KEY>" flag of "org login web", where CONNECTED-APP-CONSUMER-KEY is the consumer key you previously copied. This step ensures that the "org login web" command uses your custom connected app, and not the default CLI connected app.

Press Enter to skip sharing the client secret.
6. Press Enter to skip sharing the client secret, then log in with your org username as usual and click Accept.

6. When you run this command to interact with an agent, specify the username you authorized in the preceding step with the --connected-app-user (-a) flag.
7. Run this command ("agent preview") to interact with an agent by using the --target-org flag to specify the org username or alias as usual and --client-app to specify the linked connected app ("agent-app" in the previous example). Use the "org display" command to get the list of client apps associated with an org.

# flags.api-name.summary

API name of the agent you want to interact with.

# flags.connected-app-user.summary
# flags.client-app.summary

Username or alias of the connected app user that's configured with web-based access tokens to the agent.
Name of the linked client app to use for the agent connection. You must have previously created this link with "org login web --client-app". Run "org display" to see the available linked client apps.

# flags.output-dir.summary

Expand All @@ -56,10 +54,10 @@ Enable Apex debug logging during the agent preview conversation.

# examples

- Interact with an agent with API name "Resort_Manager" in the org with alias "my-org". Connect to your agent using the alias "my-agent-user"; this alias must point to the username who is authorized using the Web server flow:
- Interact with an agent with API name "Resort_Manager" in the org with alias "my-org" and the linked "agent-app" connected app:

<%= config.bin %> <%= command.id %> --api-name "Resort_Manager" --target-org my-org --connected-app-user my-agent-user
<%= config.bin %> <%= command.id %> --api-name "Resort_Manager" --target-org my-org --client-app agent-app

- Same as the preceding example, but this time save the conversation transcripts to the "./transcripts/my-preview" directory rather than the default "./temp/agent-preview":

<%= config.bin %> <%= command.id %> --api-name "Resort_Manager" --target-org my-org --connected-app-user my-agent-user --output-dir "transcripts/my-preview"
<%= config.bin %> <%= command.id %> --api-name "Resort_Manager" --target-org my-org --client-app agent-app --output-dir "transcripts/my-preview"
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"@oclif/core": "^4",
"@oclif/multi-stage-output": "^0.7.12",
"@salesforce/agents": "0.15.0",
"@salesforce/core": "^8.10.2",
"@salesforce/core": "^8.13.0",
"@salesforce/kit": "^3.2.3",
"@salesforce/sf-plugins-core": "^12.2.0",
"@salesforce/source-deploy-retrieve": "^12.19.3",
Expand Down
21 changes: 15 additions & 6 deletions src/commands/agent/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { resolve, join } from 'node:path';
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages, SfError } from '@salesforce/core';
import { AuthInfo, Connection, Messages, SfError } from '@salesforce/core';
import React from 'react';
import { render } from 'ink';
import { env } from '@salesforce/kit';
Expand Down Expand Up @@ -54,10 +54,11 @@ export default class AgentPreview extends SfCommand<AgentPreviewResult> {
public static readonly flags = {
'target-org': Flags.requiredOrg(),
'api-version': Flags.orgApiVersion(),
'connected-app-user': Flags.requiredOrg({
summary: messages.getMessage('flags.connected-app-user.summary'),
char: 'a',
'client-app': Flags.string({
char: 'c',
summary: messages.getMessage('flags.client-app.summary'),
required: true,
dependsOn: ['target-org'],
}),
'api-name': Flags.string({
summary: messages.getMessage('flags.api-name.summary'),
Expand All @@ -78,7 +79,15 @@ export default class AgentPreview extends SfCommand<AgentPreviewResult> {

const { 'api-name': apiNameFlag } = flags;
const conn = flags['target-org'].getConnection(flags['api-version']);
const apiConn = flags['connected-app-user'].getConnection(flags['api-version']);

const authInfo = await AuthInfo.create({
username: flags['target-org'].getUsername(),
});

const jwtConn = await Connection.create({
authInfo,
clientApp: flags['client-app'],
});

const agentsQuery = await conn.query<AgentData>(
'SELECT Id, DeveloperName, (SELECT Status FROM BotVersions) FROM BotDefinition WHERE IsDeleted = false'
Expand All @@ -102,7 +111,7 @@ export default class AgentPreview extends SfCommand<AgentPreviewResult> {
}

const outputDir = await resolveOutputDir(flags['output-dir'], flags['apex-debug']);
const agentPreview = new Preview(apiConn, selectedAgent.Id);
const agentPreview = new Preview(jwtConn, selectedAgent.Id);
agentPreview.toggleApexDebugMode(flags['apex-debug']);

const instance = render(
Expand Down
42 changes: 21 additions & 21 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1271,10 +1271,10 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"

"@jsforce/jsforce-node@^3.8.1":
version "3.8.1"
resolved "https://registry.yarnpkg.com/@jsforce/jsforce-node/-/jsforce-node-3.8.1.tgz#482fcf2820b48a6b10930d33550eb4e4cbd1e480"
integrity sha512-+IZZC7VfNjhkTyeAspBc4Z35Y5eAP0RIWQnyfKahsbY/aLjiFRIM9ejl1YbWbrbabf5ODFSUgBGmOiEYLW3f7Q==
"@jsforce/jsforce-node@^3.8.2":
version "3.8.2"
resolved "https://registry.yarnpkg.com/@jsforce/jsforce-node/-/jsforce-node-3.8.2.tgz#68b903f6733ae479086ab02ea4a2de87a7f208eb"
integrity sha512-ewaRr9JnZRW6I28C/TzUnv5p70zMrWsKCq2ovRW6X557/ikdfvA24F9k4cQXZnTG2lZLEfVn+WwdBGEtY7pPnQ==
dependencies:
"@sindresorhus/is" "^4"
base64url "^3.0.1"
Expand Down Expand Up @@ -1472,12 +1472,12 @@
strip-ansi "6.0.1"
ts-retry-promise "^0.8.1"

"@salesforce/core@^8.10.0", "@salesforce/core@^8.10.2", "@salesforce/core@^8.10.3", "@salesforce/core@^8.5.1", "@salesforce/core@^8.8.0", "@salesforce/core@^8.8.5":
version "8.10.3"
resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.10.3.tgz#3cc2c99d097757cb4b08dab921254cfa3a00c7c1"
integrity sha512-juqbU304TBrrjb8sZGw+QkeAJISKu4+v2XIMTCxGJoEjs4LLhsyI7/drxCUY+7FNye+veAGeJdn/PCxkKhSgcA==
"@salesforce/core@^8.10.0", "@salesforce/core@^8.10.3", "@salesforce/core@^8.13.0", "@salesforce/core@^8.5.1", "@salesforce/core@^8.8.0", "@salesforce/core@^8.8.5":
version "8.13.0"
resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.13.0.tgz#ffc00a776c60a401d4385abfeb6cd7fee0d90f3b"
integrity sha512-FyAn0UGa93D0N++8poeJt7yEaWQH++qxrv/Wf4TjNaUCLoh19g57lrXuos3qDJPr8Ut4x6QjVxEc49XLy+vBkw==
dependencies:
"@jsforce/jsforce-node" "^3.8.1"
"@jsforce/jsforce-node" "^3.8.2"
"@salesforce/kit" "^3.2.2"
"@salesforce/schemas" "^1.9.0"
"@salesforce/ts-types" "^2.0.10"
Expand All @@ -1489,9 +1489,9 @@
js2xmlparser "^4.0.1"
jsonwebtoken "9.0.2"
jszip "3.10.1"
pino "^9.4.0"
pino "^9.7.0"
pino-abstract-transport "^1.2.0"
pino-pretty "^11.2.2"
pino-pretty "^11.3.0"
proper-lockfile "^4.1.2"
semver "^7.6.3"
ts-retry-promise "^0.8.1"
Expand Down Expand Up @@ -6985,7 +6985,7 @@ pino-abstract-transport@^2.0.0:
dependencies:
split2 "^4.0.0"

pino-pretty@^11.2.2:
pino-pretty@^11.3.0:
version "11.3.0"
resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-11.3.0.tgz#390b3be044cf3d2e9192c7d19d44f6b690468f2e"
integrity sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==
Expand All @@ -7010,17 +7010,17 @@ pino-std-serializers@^7.0.0:
resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz#7c625038b13718dbbd84ab446bd673dc52259e3b"
integrity sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==

pino@^9.4.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-9.6.0.tgz#6bc628159ba0cc81806d286718903b7fc6b13169"
integrity sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==
pino@^9.7.0:
version "9.7.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-9.7.0.tgz#ff7cd86eb3103ee620204dbd5ca6ffda8b53f645"
integrity sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==
dependencies:
atomic-sleep "^1.0.0"
fast-redact "^3.1.1"
on-exit-leak-free "^2.1.0"
pino-abstract-transport "^2.0.0"
pino-std-serializers "^7.0.0"
process-warning "^4.0.0"
process-warning "^5.0.0"
quick-format-unescaped "^4.0.3"
real-require "^0.2.0"
safe-stable-stringify "^2.3.1"
Expand Down Expand Up @@ -7079,10 +7079,10 @@ process-on-spawn@^1.0.0:
dependencies:
fromentries "^1.2.0"

process-warning@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-4.0.1.tgz#5c1db66007c67c756e4e09eb170cdece15da32fb"
integrity sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==
process-warning@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-5.0.0.tgz#566e0bf79d1dff30a72d8bbbe9e8ecefe8d378d7"
integrity sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==

process@^0.11.10:
version "0.11.10"
Expand Down
Loading