-
Notifications
You must be signed in to change notification settings - Fork 0
feat(cli): deploy a persona by id, not just by path #320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,11 @@ import { | |
| setWorkspaceKey, | ||
| type StoredAuth | ||
| } from '@agent-relay/cloud'; | ||
| import { | ||
| formatPersonaSourceLabel, | ||
| PersonaResolutionError, | ||
| resolvePersonaReference | ||
| } from '@agentworkforce/persona-registry'; | ||
| import { | ||
| canonicalizeCloudUrl, | ||
| clearActiveWorkspace, | ||
|
|
@@ -67,7 +72,7 @@ export function configureDeployCommandForTest(overrides: Partial<DeployCommandDe | |
| } | ||
|
|
||
| /** | ||
| * Argv parser + dispatcher for `agentworkforce deploy <persona-path> [flags]`. | ||
| * Argv parser + dispatcher for `agentworkforce deploy <persona-id|persona-path>`. | ||
| * Keeps cli.ts itself slim — the file is already a large dispatcher and | ||
| * each command lands in its own module when it grows past trivial. | ||
| */ | ||
|
|
@@ -253,7 +258,11 @@ export async function runLogout(args: readonly string[]): Promise<void> { | |
| } | ||
| } | ||
|
|
||
| const DEPLOY_USAGE = `usage: agentworkforce deploy <persona-path> [flags] | ||
| const DEPLOY_USAGE = `usage: agentworkforce deploy <persona-id|persona-path> [flags] | ||
|
|
||
| A bare id resolves through the registry cascade — including agents kept in | ||
| .agentworkforce/workforce/agents/<name>/. A path may be a prebuilt persona.json | ||
| or an authored persona.ts/js. | ||
|
|
||
| Flags: | ||
| --mode dev|sandbox|cloud Pick a run mode (prompts in an interactive terminal) | ||
|
|
@@ -303,6 +312,49 @@ Flags: | |
|
|
||
| const ON_EXISTS_CHOICES = ['update', 'destroy', 'cancel'] as const; | ||
|
|
||
| /** | ||
| * A selector is a path when it carries path syntax or a persona-source | ||
| * extension. Anything else is a persona id looked up through the registry | ||
| * cascade, so an agent that lives in `.agentworkforce/workforce/agents/<name>/` | ||
| * deploys by name from anywhere in the repo. | ||
| * | ||
| * Syntax decides, not the filesystem: a bare `proposal-agent` that happens to | ||
| * match a directory in cwd must still mean the persona, or the same command | ||
| * would deploy different things depending on where it ran. | ||
| */ | ||
| export function looksLikeDeployPath(selector: string): boolean { | ||
| return ( | ||
| selector.startsWith('.') || | ||
| selector.startsWith('/') || | ||
| selector.startsWith('~') || | ||
| selector.includes(path.sep) || | ||
| selector.includes('/') || | ||
| isPersonaSourcePath(selector) || | ||
| selector.toLowerCase().endsWith('.json') | ||
| ); | ||
| } | ||
|
|
||
| export function resolveDeployPersonaSelector(selector: string): string { | ||
| if (looksLikeDeployPath(selector)) return path.resolve(selector); | ||
|
|
||
| let resolved; | ||
| try { | ||
| resolved = resolvePersonaReference(selector); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a valid handler-based deploy persona omits Useful? React with 👍 / 👎.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Resolve ID selectors without constructing an interactive Prompt for AI agents |
||
| } catch (err) { | ||
| if (err instanceof PersonaResolutionError) { | ||
| die(`deploy: ${err.message}`); | ||
| } | ||
| throw err; | ||
| } | ||
| if (!resolved.path) { | ||
| die( | ||
| `deploy: persona "${selector}" resolves to the ${formatPersonaSourceLabel(resolved.source)} catalog, which has no file to deploy. ` + | ||
| 'Pass a path to a persona.json or persona.ts instead.' | ||
| ); | ||
| } | ||
| return resolved.path; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the selected ID is defined by a partial registry overlay, returning its declaring path discards the merged Useful? React with 👍 / 👎.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: When a higher-priority partial overlay shadows an agent persona, Prompt for AI agents |
||
| } | ||
|
|
||
| export function parseDeployArgs(args: readonly string[]): DeployOptions { | ||
| let personaPath: string | undefined; | ||
| let mode: DeployMode | undefined; | ||
|
|
@@ -373,14 +425,14 @@ export function parseDeployArgs(args: readonly string[]): DeployOptions { | |
| } else if (a.startsWith('--')) { | ||
| die(`deploy: unknown flag "${a}"`); | ||
| } else if (!personaPath) { | ||
| personaPath = path.resolve(a); | ||
| personaPath = resolveDeployPersonaSelector(a); | ||
| } else { | ||
| die(`deploy: unexpected positional argument "${a}"`); | ||
| } | ||
| } | ||
|
|
||
| if (!personaPath) { | ||
| die('deploy: missing persona path. Usage: agentworkforce deploy <persona-path>'); | ||
| die('deploy: missing persona. Usage: agentworkforce deploy <persona-id|persona-path>'); | ||
| } | ||
|
|
||
| return { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: The built-in-id test is not isolated from ambient developer configuration, so it can fail (or false-pass) depending on the machine it runs on.
resolveDeployPersonaSelector('persona-maker')resolves through the registry cascade, where a local persona namedpersona-makerunder the runner's cwd (process.cwd()) or in the configurable persona dirs (default~/.agentworkforce/workforce/personas) wins over the built-in catalog and returns a real file path, so the/no file to deploy/assertion fails even though the behavior under test is correct. The other new selector test isolates this by chdir'ing into a fresh mkdtemp root; this one leaves cwd and the ambient config untouched. Run the assertion from an isolated temporary cwd (and remove it in finally) so only the built-in resolution drives the outcome.Prompt for AI agents