@phnx-labs/agents-cli 1.20.22 → 1.20.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/cloud.js +126 -6
- package/dist/commands/exec.js +13 -1
- package/dist/index.js +266 -224
- package/dist/lib/cloud/codex.d.ts +1 -0
- package/dist/lib/cloud/codex.js +8 -2
- package/dist/lib/cloud/factory.d.ts +12 -1
- package/dist/lib/cloud/factory.js +58 -3
- package/dist/lib/cloud/types.d.ts +41 -0
- package/dist/lib/cloud/types.js +17 -0
- package/dist/lib/exec.d.ts +2 -0
- package/dist/lib/exec.js +5 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +20 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +115 -0
- package/dist/lib/secrets/agent.d.ts +11 -0
- package/dist/lib/secrets/agent.js +28 -9
- package/dist/lib/startup/command-registry.d.ts +99 -0
- package/dist/lib/startup/command-registry.js +136 -0
- package/package.json +3 -2
package/dist/commands/cloud.js
CHANGED
|
@@ -4,6 +4,7 @@ import ora from 'ora';
|
|
|
4
4
|
import { resolveProvider, getAllProviders, getDefaultProviderId } from '../lib/cloud/registry.js';
|
|
5
5
|
import { insertTask, updateTaskStatus, getTaskById, listTasks as listStoredTasks, listActiveTasks } from '../lib/cloud/store.js';
|
|
6
6
|
import { renderStream } from '../lib/cloud/stream.js';
|
|
7
|
+
import { MissingTargetError } from '../lib/cloud/types.js';
|
|
7
8
|
/** Print an error message to stderr and exit. */
|
|
8
9
|
function die(msg, code = 1) {
|
|
9
10
|
console.error(chalk.red(msg));
|
|
@@ -43,6 +44,47 @@ function statusColor(status) {
|
|
|
43
44
|
function isJsonMode(opts) {
|
|
44
45
|
return Boolean(opts.json) || !process.stdout.isTTY;
|
|
45
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* After a `MissingTargetError`, try to resolve the target interactively.
|
|
49
|
+
* Returns the chosen id, or undefined when no interactive resolution is
|
|
50
|
+
* possible (non-TTY/JSON, provider can't enumerate, or user cancels) — the
|
|
51
|
+
* caller then prints the error's guidance.
|
|
52
|
+
*
|
|
53
|
+
* Codex has no `listTargets` (no list-environments CLI), so it always returns
|
|
54
|
+
* undefined here and the user sees the `codex cloud` guidance. Factory lists
|
|
55
|
+
* Droid Computers; if listing fails (not signed in) or parses to nothing, we
|
|
56
|
+
* fall back to a free-text prompt so a dispatch is never hard-blocked.
|
|
57
|
+
*/
|
|
58
|
+
async function pickMissingTarget(provider, err, json) {
|
|
59
|
+
if (json || !process.stdout.isTTY)
|
|
60
|
+
return undefined;
|
|
61
|
+
if (!provider.listTargets)
|
|
62
|
+
return undefined;
|
|
63
|
+
const { select, input } = await import('@inquirer/prompts');
|
|
64
|
+
const promptName = err.kind === 'env' ? 'environment' : 'computer';
|
|
65
|
+
let targets;
|
|
66
|
+
try {
|
|
67
|
+
targets = await provider.listTargets();
|
|
68
|
+
}
|
|
69
|
+
catch (listErr) {
|
|
70
|
+
process.stderr.write(chalk.dim(`Could not list ${promptName}s: ${listErr.message}\n`));
|
|
71
|
+
targets = [];
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
if (targets.length > 0) {
|
|
75
|
+
return await select({
|
|
76
|
+
message: `Select a ${promptName}`,
|
|
77
|
+
choices: targets.map((t) => ({ value: t.id, name: t.label ? `${t.id} ${chalk.dim(t.label)}` : t.id })),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const typed = (await input({ message: `No ${promptName}s found. Enter a ${promptName} name (blank to cancel):` })).trim();
|
|
81
|
+
return typed || undefined;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// User hit Ctrl-C / Esc on the prompt.
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
46
88
|
/** Register the `agents cloud` command tree (run, list, status, logs, cancel, message, providers). */
|
|
47
89
|
export function registerCloudCommands(program) {
|
|
48
90
|
const cloud = program
|
|
@@ -182,16 +224,41 @@ Examples:
|
|
|
182
224
|
}
|
|
183
225
|
if (options.uploadAccountTokens)
|
|
184
226
|
dispatchOptions.providerOptions.uploadAccountTokens = true;
|
|
185
|
-
// Dispatch
|
|
186
|
-
|
|
227
|
+
// Dispatch. On a missing pre-provisioned target (Codex env / Factory
|
|
228
|
+
// computer), offer an interactive picker instead of a raw error.
|
|
229
|
+
const dispatchOnce = async () => {
|
|
230
|
+
const spinner = ora({ text: `Dispatching to ${provider.name}...`, stream: process.stderr }).start();
|
|
231
|
+
try {
|
|
232
|
+
const t = await provider.dispatch(dispatchOptions);
|
|
233
|
+
spinner.succeed(`Task ${t.id} dispatched to ${provider.name}`);
|
|
234
|
+
return t;
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
spinner.fail('Dispatch failed');
|
|
238
|
+
throw err;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
187
241
|
let task;
|
|
188
242
|
try {
|
|
189
|
-
task = await
|
|
190
|
-
spinner.succeed(`Task ${task.id} dispatched to ${provider.name}`);
|
|
243
|
+
task = await dispatchOnce();
|
|
191
244
|
}
|
|
192
245
|
catch (err) {
|
|
193
|
-
|
|
194
|
-
|
|
246
|
+
if (err instanceof MissingTargetError) {
|
|
247
|
+
const picked = await pickMissingTarget(provider, err, json);
|
|
248
|
+
if (!picked) {
|
|
249
|
+
die(err.guidance ? `${err.message}\n\n${err.guidance}` : err.message);
|
|
250
|
+
}
|
|
251
|
+
dispatchOptions.providerOptions[err.kind] = picked;
|
|
252
|
+
try {
|
|
253
|
+
task = await dispatchOnce();
|
|
254
|
+
}
|
|
255
|
+
catch (err2) {
|
|
256
|
+
die(err2.message);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
die(err.message);
|
|
261
|
+
}
|
|
195
262
|
}
|
|
196
263
|
// Persist locally
|
|
197
264
|
insertTask(task);
|
|
@@ -422,4 +489,57 @@ Examples:
|
|
|
422
489
|
console.log(` ${p.id.padEnd(12)} ${p.name.padEnd(20)} ${status}${defaultTag}`);
|
|
423
490
|
}
|
|
424
491
|
});
|
|
492
|
+
// ── agents cloud envs ─────────────────────────────────────────────────
|
|
493
|
+
// Discover the pre-provisioned targets a provider runs inside — Codex
|
|
494
|
+
// environments, Factory Droid Computers — so users don't copy opaque IDs
|
|
495
|
+
// out of a web UI.
|
|
496
|
+
cloud
|
|
497
|
+
.command('envs')
|
|
498
|
+
.alias('targets')
|
|
499
|
+
.description('List the pre-provisioned targets (Codex environments / Droid Computers) you can dispatch into.')
|
|
500
|
+
.option('--provider <id>', 'Only this provider (codex, factory, ...)')
|
|
501
|
+
.option('--json', 'JSON output')
|
|
502
|
+
.action(async (options) => {
|
|
503
|
+
const json = isJsonMode(options);
|
|
504
|
+
const only = options.provider;
|
|
505
|
+
// Providers that run inside a pre-provisioned target declare targetKind.
|
|
506
|
+
const providers = getAllProviders().filter((p) => p.targetKind && (!only || p.id === only));
|
|
507
|
+
if (only && providers.length === 0) {
|
|
508
|
+
die(`Provider '${only}' has no pre-provisioned targets (or is unknown). Targets apply to: codex, factory.`);
|
|
509
|
+
}
|
|
510
|
+
const results = [];
|
|
511
|
+
for (const p of providers) {
|
|
512
|
+
const kind = p.targetKind;
|
|
513
|
+
if (!p.listTargets) {
|
|
514
|
+
// Not enumerable (Codex). Surface guidance instead of a list.
|
|
515
|
+
const guidance = kind === 'env'
|
|
516
|
+
? 'Codex environments are not listable from the CLI. Browse/create them with `codex cloud` (interactive), then use --env <id>.'
|
|
517
|
+
: 'Not enumerable from the CLI.';
|
|
518
|
+
results.push({ provider: p.id, kind, targets: [], note: guidance });
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
try {
|
|
522
|
+
const targets = await p.listTargets();
|
|
523
|
+
results.push({ provider: p.id, kind, targets: targets.map((t) => ({ id: t.id, label: t.label })) });
|
|
524
|
+
}
|
|
525
|
+
catch (err) {
|
|
526
|
+
results.push({ provider: p.id, kind, targets: [], note: err.message });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (json) {
|
|
530
|
+
process.stdout.write(JSON.stringify(results, null, 2) + '\n');
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
for (const r of results) {
|
|
534
|
+
console.log(chalk.bold(`\n${r.provider}`) + chalk.dim(` (${r.kind})`));
|
|
535
|
+
if (r.targets.length > 0) {
|
|
536
|
+
for (const t of r.targets) {
|
|
537
|
+
console.log(` ${t.id}${t.label ? ' ' + chalk.dim(t.label) : ''}`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
console.log(chalk.dim(` ${r.note ?? 'none'}`));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
});
|
|
425
545
|
}
|
package/dist/commands/exec.js
CHANGED
|
@@ -150,6 +150,10 @@ export function registerRunCommand(program) {
|
|
|
150
150
|
|
|
151
151
|
# Inject a keychain-backed secrets bundle
|
|
152
152
|
agents run claude "deploy the worker" --secrets prod --mode edit
|
|
153
|
+
|
|
154
|
+
# Pass arbitrary native flags to the underlying CLI via -- separator
|
|
155
|
+
agents run kimi -- --plan --some-kimi-option value
|
|
156
|
+
agents run claude "fix the bug" -- --custom-flag
|
|
153
157
|
`,
|
|
154
158
|
notes: `
|
|
155
159
|
Modes (not every agent supports every mode — check agents.yaml capabilities):
|
|
@@ -168,9 +172,16 @@ export function registerRunCommand(program) {
|
|
|
168
172
|
Fallback: --fallback codex,gemini retries on rate-limit failure via /continue handoff. Each entry accepts @version.
|
|
169
173
|
|
|
170
174
|
Resume: --session-id <id> continues a prior Claude conversation.
|
|
175
|
+
|
|
176
|
+
Passthrough: everything after -- is forwarded verbatim to the underlying agent CLI.
|
|
177
|
+
agents run kimi -- --plan --some-native-flag value
|
|
171
178
|
`,
|
|
172
179
|
});
|
|
173
|
-
runCmd.action(async (agentSpec, prompt, options) => {
|
|
180
|
+
runCmd.action(async (agentSpec, prompt, options, command) => {
|
|
181
|
+
// Capture everything after -- as passthrough args forwarded verbatim to the underlying CLI.
|
|
182
|
+
// Use command.args (all positional strings) and strip the declared positional args from the front.
|
|
183
|
+
const declaredArgCount = prompt !== undefined ? 2 : 1;
|
|
184
|
+
const passthroughArgs = command.args.slice(declaredArgCount);
|
|
174
185
|
// --resume-checkpoint short-circuits normal dispatch entirely: the
|
|
175
186
|
// checkpoint already carries the agent, version, prompt, session id,
|
|
176
187
|
// iteration, and loop config of the killed run. Reconstruct ExecOptions
|
|
@@ -642,6 +653,7 @@ export function registerRunCommand(program) {
|
|
|
642
653
|
env,
|
|
643
654
|
toolsRestrict: workflowToolsRestrict,
|
|
644
655
|
mcpConfigPath: workflowMcpConfigPath,
|
|
656
|
+
passthroughArgs,
|
|
645
657
|
};
|
|
646
658
|
if (options.interactive && options.headless) {
|
|
647
659
|
console.error(chalk.red('--interactive and --headless are mutually exclusive. Pass one, or neither (mode is inferred from prompt presence).'));
|