@phnx-labs/agents-cli 1.20.34 → 1.20.36
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/CHANGELOG.md +113 -0
- package/LICENSE +185 -21
- package/README.md +13 -5
- package/dist/commands/audit.d.ts +14 -0
- package/dist/commands/audit.js +68 -0
- package/dist/commands/browser.js +82 -8
- package/dist/commands/check.d.ts +15 -0
- package/dist/commands/check.js +84 -0
- package/dist/commands/cloud.js +143 -3
- package/dist/commands/computer-actions.d.ts +15 -0
- package/dist/commands/computer-actions.js +120 -22
- package/dist/commands/computer.js +81 -0
- package/dist/commands/cost.js +17 -12
- package/dist/commands/daemon.js +4 -1
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +54 -97
- package/dist/commands/events.d.ts +16 -0
- package/dist/commands/events.js +173 -0
- package/dist/commands/exec.d.ts +19 -0
- package/dist/commands/exec.js +283 -12
- package/dist/commands/hosts.js +37 -29
- package/dist/commands/inspect.d.ts +2 -0
- package/dist/commands/inspect.js +62 -16
- package/dist/commands/lock.d.ts +12 -0
- package/dist/commands/lock.js +70 -0
- package/dist/commands/logs.d.ts +17 -0
- package/dist/commands/logs.js +139 -0
- package/dist/commands/message.d.ts +15 -0
- package/dist/commands/message.js +56 -0
- package/dist/commands/models.d.ts +3 -0
- package/dist/commands/models.js +27 -8
- package/dist/commands/permissions.js +9 -2
- package/dist/commands/repo.d.ts +34 -0
- package/dist/commands/repo.js +243 -65
- package/dist/commands/resource-view.d.ts +20 -0
- package/dist/commands/resource-view.js +90 -28
- package/dist/commands/routines.js +101 -5
- package/dist/commands/secrets-migrate.js +106 -57
- package/dist/commands/secrets.d.ts +31 -18
- package/dist/commands/secrets.js +196 -91
- package/dist/commands/serve.d.ts +10 -0
- package/dist/commands/serve.js +37 -0
- package/dist/commands/sessions-inject.d.ts +14 -0
- package/dist/commands/sessions-inject.js +111 -0
- package/dist/commands/sessions-picker.d.ts +2 -0
- package/dist/commands/sessions-picker.js +24 -3
- package/dist/commands/sessions-resume.d.ts +2 -0
- package/dist/commands/sessions-resume.js +221 -0
- package/dist/commands/sessions-tail.d.ts +10 -0
- package/dist/commands/sessions-tail.js +11 -0
- package/dist/commands/sessions.d.ts +143 -1
- package/dist/commands/sessions.js +628 -80
- package/dist/commands/setup.js +4 -2
- package/dist/commands/sync.d.ts +3 -1
- package/dist/commands/sync.js +156 -4
- package/dist/commands/teams.js +217 -0
- package/dist/commands/versions.js +2 -4
- package/dist/commands/view.d.ts +3 -0
- package/dist/commands/view.js +19 -8
- package/dist/commands/wallet.d.ts +6 -0
- package/dist/commands/wallet.js +22 -5
- package/dist/commands/watchdog.d.ts +18 -0
- package/dist/commands/watchdog.js +238 -0
- package/dist/index.js +82 -32
- package/dist/lib/agents.js +30 -9
- package/dist/lib/audit/log.d.ts +92 -0
- package/dist/lib/audit/log.js +177 -0
- package/dist/lib/auto-pull.js +2 -1
- package/dist/lib/browser/cdp.js +5 -1
- package/dist/lib/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +18 -7
- package/dist/lib/browser/drivers/ssh.d.ts +8 -0
- package/dist/lib/browser/drivers/ssh.js +77 -16
- package/dist/lib/browser/har.d.ts +84 -0
- package/dist/lib/browser/har.js +77 -0
- package/dist/lib/browser/ipc.d.ts +3 -0
- package/dist/lib/browser/ipc.js +37 -12
- package/dist/lib/browser/profiles.d.ts +1 -1
- package/dist/lib/browser/profiles.js +8 -10
- package/dist/lib/browser/refs.d.ts +65 -0
- package/dist/lib/browser/refs.js +73 -1
- package/dist/lib/browser/runtime-state.js +1 -0
- package/dist/lib/browser/service.d.ts +38 -2
- package/dist/lib/browser/service.js +183 -23
- package/dist/lib/browser/types.d.ts +14 -1
- package/dist/lib/budget/live-cloud.d.ts +42 -0
- package/dist/lib/budget/live-cloud.js +79 -0
- package/dist/lib/budget/live-team.d.ts +31 -0
- package/dist/lib/budget/live-team.js +115 -0
- package/dist/lib/cloud/codex.js +4 -0
- package/dist/lib/cloud/rush.d.ts +12 -1
- package/dist/lib/cloud/rush.js +13 -3
- package/dist/lib/cloud/types.d.ts +9 -0
- package/dist/lib/computer/dispatch.d.ts +8 -0
- package/dist/lib/computer/dispatch.js +125 -0
- package/dist/lib/computer/loop.d.ts +62 -0
- package/dist/lib/computer/loop.js +98 -0
- package/dist/lib/computer/model.d.ts +44 -0
- package/dist/lib/computer/model.js +157 -0
- package/dist/lib/concurrency.d.ts +19 -0
- package/dist/lib/concurrency.js +33 -0
- package/dist/lib/daemon.d.ts +92 -0
- package/dist/lib/daemon.js +222 -18
- package/dist/lib/devices/connect.d.ts +3 -2
- package/dist/lib/devices/connect.js +5 -3
- package/dist/lib/devices/registry.d.ts +7 -0
- package/dist/lib/devices/registry.js +24 -0
- package/dist/lib/devices/tailscale.js +1 -1
- package/dist/lib/drift.d.ts +52 -0
- package/dist/lib/drift.js +112 -0
- package/dist/lib/events.d.ts +10 -3
- package/dist/lib/events.js +101 -24
- package/dist/lib/exec.d.ts +17 -0
- package/dist/lib/exec.js +100 -12
- package/dist/lib/git.d.ts +27 -0
- package/dist/lib/git.js +56 -1
- package/dist/lib/hooks/cache.d.ts +6 -0
- package/dist/lib/hooks/cache.js +54 -12
- package/dist/lib/hooks.d.ts +27 -0
- package/dist/lib/hooks.js +135 -10
- package/dist/lib/hosts/dispatch.d.ts +15 -0
- package/dist/lib/hosts/dispatch.js +45 -13
- package/dist/lib/hosts/logs.d.ts +16 -0
- package/dist/lib/hosts/logs.js +74 -0
- package/dist/lib/hosts/option.js +1 -1
- package/dist/lib/hosts/passthrough.js +3 -1
- package/dist/lib/hosts/progress.d.ts +66 -0
- package/dist/lib/hosts/progress.js +125 -17
- package/dist/lib/hosts/ready.d.ts +51 -7
- package/dist/lib/hosts/ready.js +98 -25
- package/dist/lib/hosts/reconcile.d.ts +53 -0
- package/dist/lib/hosts/reconcile.js +81 -0
- package/dist/lib/hosts/registry.d.ts +19 -2
- package/dist/lib/hosts/registry.js +58 -2
- package/dist/lib/hosts/remote-cmd.d.ts +62 -1
- package/dist/lib/hosts/remote-cmd.js +70 -1
- package/dist/lib/hosts/remote-os.d.ts +17 -0
- package/dist/lib/hosts/remote-os.js +30 -0
- package/dist/lib/hosts/session-index.d.ts +34 -0
- package/dist/lib/hosts/session-index.js +56 -0
- package/dist/lib/hosts/tasks.d.ts +22 -0
- package/dist/lib/hosts/tasks.js +29 -0
- package/dist/lib/lock.d.ts +93 -0
- package/dist/lib/lock.js +207 -0
- package/dist/lib/loop.js +16 -1
- package/dist/lib/machine-id.d.ts +21 -0
- package/dist/lib/machine-id.js +26 -0
- package/dist/lib/mailbox-target.d.ts +36 -0
- package/dist/lib/mailbox-target.js +45 -0
- package/dist/lib/mailbox.d.ts +47 -0
- package/dist/lib/mailbox.js +194 -0
- package/dist/lib/mcp.d.ts +5 -0
- package/dist/lib/mcp.js +24 -8
- package/dist/lib/migrate.d.ts +19 -0
- package/dist/lib/migrate.js +134 -26
- package/dist/lib/overdue.js +3 -0
- package/dist/lib/permissions.d.ts +4 -0
- package/dist/lib/permissions.js +35 -0
- package/dist/lib/picker.d.ts +26 -0
- package/dist/lib/picker.js +127 -0
- package/dist/lib/platform/exec.d.ts +46 -0
- package/dist/lib/platform/exec.js +74 -0
- package/dist/lib/platform/process.d.ts +31 -0
- package/dist/lib/platform/process.js +34 -1
- package/dist/lib/platform/winpath.js +2 -0
- package/dist/lib/plugin-marketplace.d.ts +30 -0
- package/dist/lib/plugin-marketplace.js +215 -2
- package/dist/lib/plugins.d.ts +5 -0
- package/dist/lib/plugins.js +61 -10
- package/dist/lib/profiles.d.ts +25 -0
- package/dist/lib/profiles.js +22 -6
- package/dist/lib/pty-client.js +2 -1
- package/dist/lib/rotate.d.ts +61 -0
- package/dist/lib/rotate.js +52 -0
- package/dist/lib/routines.d.ts +40 -2
- package/dist/lib/routines.js +66 -8
- package/dist/lib/runner.d.ts +11 -2
- package/dist/lib/runner.js +49 -7
- package/dist/lib/scheduler.js +6 -1
- package/dist/lib/secrets/bundles.d.ts +60 -4
- package/dist/lib/secrets/bundles.js +131 -12
- package/dist/lib/secrets/filestore.d.ts +3 -0
- package/dist/lib/secrets/filestore.js +42 -16
- package/dist/lib/secrets/index.d.ts +43 -2
- package/dist/lib/secrets/index.js +102 -3
- package/dist/lib/secrets/mcp.d.ts +93 -0
- package/dist/lib/secrets/mcp.js +205 -0
- package/dist/lib/secrets/remote.js +12 -5
- package/dist/lib/secrets/sync.js +83 -4
- package/dist/lib/secrets/windows.js +14 -3
- package/dist/lib/serve/data.d.ts +81 -0
- package/dist/lib/serve/data.js +91 -0
- package/dist/lib/serve/page.d.ts +7 -0
- package/dist/lib/serve/page.js +140 -0
- package/dist/lib/serve/server.d.ts +46 -0
- package/dist/lib/serve/server.js +115 -0
- package/dist/lib/session/active.d.ts +61 -0
- package/dist/lib/session/active.js +196 -17
- package/dist/lib/session/discover.d.ts +37 -0
- package/dist/lib/session/discover.js +204 -27
- package/dist/lib/session/inject.d.ts +18 -0
- package/dist/lib/session/inject.js +21 -0
- package/dist/lib/session/parse.d.ts +6 -0
- package/dist/lib/session/parse.js +286 -2
- package/dist/lib/session/pid-registry.d.ts +22 -0
- package/dist/lib/session/pid-registry.js +104 -0
- package/dist/lib/session/provenance.d.ts +14 -2
- package/dist/lib/session/provenance.js +39 -8
- package/dist/lib/session/remote-active.d.ts +26 -0
- package/dist/lib/session/remote-active.js +153 -0
- package/dist/lib/session/remote-list.d.ts +51 -0
- package/dist/lib/session/remote-list.js +213 -0
- package/dist/lib/session/remote.d.ts +7 -1
- package/dist/lib/session/remote.js +18 -8
- package/dist/lib/session/render.js +1 -1
- package/dist/lib/session/sync/config.d.ts +1 -7
- package/dist/lib/session/sync/config.js +4 -11
- package/dist/lib/session/types.d.ts +18 -1
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +44 -6
- package/dist/lib/shims.js +143 -57
- package/dist/lib/ssh-exec.d.ts +26 -3
- package/dist/lib/ssh-exec.js +47 -3
- package/dist/lib/ssh-tunnel.d.ts +24 -5
- package/dist/lib/ssh-tunnel.js +62 -63
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +8 -0
- package/dist/lib/startup/command-registry.js +17 -1
- package/dist/lib/state.d.ts +13 -0
- package/dist/lib/state.js +103 -9
- package/dist/lib/sync-umbrella.d.ts +14 -7
- package/dist/lib/sync-umbrella.js +17 -9
- package/dist/lib/teams/forEach.d.ts +110 -0
- package/dist/lib/teams/forEach.js +186 -0
- package/dist/lib/teams/index.d.ts +1 -0
- package/dist/lib/teams/index.js +1 -0
- package/dist/lib/teams/pr-watch.d.ts +226 -0
- package/dist/lib/teams/pr-watch.js +371 -0
- package/dist/lib/teams/registry.js +25 -9
- package/dist/lib/teams/supervisor.d.ts +14 -1
- package/dist/lib/teams/supervisor.js +19 -0
- package/dist/lib/teams/worktree.d.ts +9 -0
- package/dist/lib/teams/worktree.js +32 -0
- package/dist/lib/terminal/backends/ghostty.d.ts +6 -0
- package/dist/lib/terminal/backends/ghostty.js +69 -0
- package/dist/lib/terminal/backends/index.d.ts +19 -0
- package/dist/lib/terminal/backends/index.js +31 -0
- package/dist/lib/terminal/backends/iterm.d.ts +6 -0
- package/dist/lib/terminal/backends/iterm.js +62 -0
- package/dist/lib/terminal/backends/tmux.d.ts +14 -0
- package/dist/lib/terminal/backends/tmux.js +23 -0
- package/dist/lib/terminal/backends/vscodium-agent.d.ts +31 -0
- package/dist/lib/terminal/backends/vscodium-agent.js +72 -0
- package/dist/lib/terminal/engine.d.ts +39 -0
- package/dist/lib/terminal/engine.js +54 -0
- package/dist/lib/terminal/index.d.ts +17 -0
- package/dist/lib/terminal/index.js +8 -0
- package/dist/lib/terminal/inject.d.ts +204 -0
- package/dist/lib/terminal/inject.js +247 -0
- package/dist/lib/terminal/policy.d.ts +11 -0
- package/dist/lib/terminal/policy.js +11 -0
- package/dist/lib/terminal/quote.d.ts +11 -0
- package/dist/lib/terminal/quote.js +13 -0
- package/dist/lib/terminal/resolve.d.ts +64 -0
- package/dist/lib/terminal/resolve.js +90 -0
- package/dist/lib/terminal/shell.d.ts +6 -0
- package/dist/lib/terminal/shell.js +23 -0
- package/dist/lib/terminal/transport.d.ts +15 -0
- package/dist/lib/terminal/transport.js +40 -0
- package/dist/lib/terminal/types.d.ts +59 -0
- package/dist/lib/terminal/types.js +13 -0
- package/dist/lib/triggers/webhook.d.ts +85 -0
- package/dist/lib/triggers/webhook.js +141 -0
- package/dist/lib/types.d.ts +7 -0
- package/dist/lib/versions.d.ts +30 -3
- package/dist/lib/versions.js +129 -18
- package/dist/lib/watchdog/index.d.ts +3 -0
- package/dist/lib/watchdog/index.js +5 -0
- package/dist/lib/watchdog/read.d.ts +35 -0
- package/dist/lib/watchdog/read.js +149 -0
- package/dist/lib/watchdog/runner.d.ts +127 -0
- package/dist/lib/watchdog/runner.js +322 -0
- package/dist/lib/watchdog/watchdog.d.ts +40 -0
- package/dist/lib/watchdog/watchdog.js +166 -0
- package/dist/lib/watchdog/watchdogTail.d.ts +5 -0
- package/dist/lib/watchdog/watchdogTail.js +154 -0
- package/dist/lib/whats-new.d.ts +9 -0
- package/dist/lib/whats-new.js +35 -0
- package/dist/lib/workflows.d.ts +166 -0
- package/dist/lib/workflows.js +193 -0
- package/package.json +5 -4
package/dist/commands/browser.js
CHANGED
|
@@ -10,6 +10,8 @@ import { BrowserDaemonNotRunningError, formatBrowserDaemonNotRunningError, sendI
|
|
|
10
10
|
import { browserTaskPicker } from './browser-picker.js';
|
|
11
11
|
import { isInteractiveTerminal } from './utils.js';
|
|
12
12
|
import { registerCommandGroups, setHelpSections } from '../lib/help.js';
|
|
13
|
+
import { buildHar } from '../lib/browser/har.js';
|
|
14
|
+
import { getCliVersion } from '../lib/version.js';
|
|
13
15
|
/**
|
|
14
16
|
* Resolve which browser task a command targets. Order:
|
|
15
17
|
* 1. `--task <name>` flag (explicit per-command override)
|
|
@@ -44,7 +46,7 @@ const BROWSER_HELP_GROUPS = [
|
|
|
44
46
|
},
|
|
45
47
|
{
|
|
46
48
|
title: 'Capture evidence',
|
|
47
|
-
names: ['console', 'errors', 'requests', 'responsebody', 'record', 'logs'],
|
|
49
|
+
names: ['console', 'errors', 'requests', 'responsebody', 'record', 'pdf', 'logs'],
|
|
48
50
|
},
|
|
49
51
|
];
|
|
50
52
|
export function registerBrowserCommand(program) {
|
|
@@ -766,6 +768,31 @@ function registerTaskCommands(browser) {
|
|
|
766
768
|
console.error(`Tip: auto-saving to ${dir}. Pass --output <path> to choose a path.`);
|
|
767
769
|
}
|
|
768
770
|
});
|
|
771
|
+
browser
|
|
772
|
+
.command('pdf [output]')
|
|
773
|
+
.description('Export the current tab as PDF via CDP Page.printToPDF — auto-saved under sessions/<task>/ when [output] is omitted')
|
|
774
|
+
.option(TASK_OPTION_FLAG, TASK_OPTION_DESC)
|
|
775
|
+
.option('-t, --tab <tabId>', 'Tab ID (defaults to current)')
|
|
776
|
+
.action(async (output, opts) => {
|
|
777
|
+
const task = resolveTaskName(opts);
|
|
778
|
+
const response = await sendIPCRequest({
|
|
779
|
+
action: 'pdf',
|
|
780
|
+
task,
|
|
781
|
+
tabId: opts.tab,
|
|
782
|
+
path: output,
|
|
783
|
+
});
|
|
784
|
+
if (!response.ok) {
|
|
785
|
+
console.error(response.error);
|
|
786
|
+
process.exit(1);
|
|
787
|
+
}
|
|
788
|
+
console.log(response.path);
|
|
789
|
+
const size = humanizeBytes(response.bytes);
|
|
790
|
+
console.error(`Saved PDF to ${response.path} (${size})`);
|
|
791
|
+
if (!output && response.path) {
|
|
792
|
+
const dir = path.dirname(response.path);
|
|
793
|
+
console.error(`Tip: auto-saving to ${dir}. Pass a path argument to choose one.`);
|
|
794
|
+
}
|
|
795
|
+
});
|
|
769
796
|
browser
|
|
770
797
|
.command('evaluate')
|
|
771
798
|
.description('Evaluate JavaScript in current tab')
|
|
@@ -1129,6 +1156,16 @@ function registerTaskCommands(browser) {
|
|
|
1129
1156
|
}
|
|
1130
1157
|
process.exit(1);
|
|
1131
1158
|
}
|
|
1159
|
+
// Warn (on stderr, so it never corrupts --json stdout) when the a11y tree
|
|
1160
|
+
// exposed nothing to click — the caller should fall back to a screenshot
|
|
1161
|
+
// plus coordinate click.
|
|
1162
|
+
const count = response.nodes?.length ?? 0;
|
|
1163
|
+
if (count === 0) {
|
|
1164
|
+
const scope = opts.all ? 'elements' : 'interactive elements';
|
|
1165
|
+
console.error(`No ${scope} found. The page may render its UI on a canvas or in a custom widget ` +
|
|
1166
|
+
`the accessibility tree doesn't expose. Take a screenshot ` +
|
|
1167
|
+
`(\`browser screenshot\`) and click by position with \`browser click --at X,Y\`.`);
|
|
1168
|
+
}
|
|
1132
1169
|
if (opts.json) {
|
|
1133
1170
|
console.log(JSON.stringify(response.nodes ?? [], null, 2));
|
|
1134
1171
|
return;
|
|
@@ -1136,23 +1173,46 @@ function registerTaskCommands(browser) {
|
|
|
1136
1173
|
console.log(response.refs);
|
|
1137
1174
|
});
|
|
1138
1175
|
browser
|
|
1139
|
-
.command('click
|
|
1140
|
-
.description('Click an element by ref')
|
|
1176
|
+
.command('click [ref]')
|
|
1177
|
+
.description('Click an element by ref, or raw coordinates with --at X,Y')
|
|
1141
1178
|
.option(TASK_OPTION_FLAG, TASK_OPTION_DESC)
|
|
1142
1179
|
.option('-t, --tab <tabId>', 'Tab ID (defaults to current)')
|
|
1180
|
+
.option('--at <x,y>', 'Click viewport coordinates (e.g. --at 320,540), bypassing ref resolution')
|
|
1143
1181
|
.action(async (ref, opts) => {
|
|
1144
1182
|
const task = resolveTaskName(opts);
|
|
1183
|
+
let requestExtra;
|
|
1184
|
+
if (opts.at !== undefined) {
|
|
1185
|
+
const parts = String(opts.at).split(',').map((s) => Number(s.trim()));
|
|
1186
|
+
if (parts.length !== 2 || parts.some((n) => !Number.isFinite(n))) {
|
|
1187
|
+
console.error(`Invalid --at value "${opts.at}". Expected X,Y (e.g. --at 320,540).`);
|
|
1188
|
+
process.exit(1);
|
|
1189
|
+
}
|
|
1190
|
+
requestExtra = { atX: parts[0], atY: parts[1] };
|
|
1191
|
+
}
|
|
1192
|
+
else if (ref !== undefined) {
|
|
1193
|
+
const parsed = parseInt(ref, 10);
|
|
1194
|
+
if (!Number.isFinite(parsed)) {
|
|
1195
|
+
console.error(`Invalid ref "${ref}". Pass an integer ref or use --at X,Y.`);
|
|
1196
|
+
process.exit(1);
|
|
1197
|
+
}
|
|
1198
|
+
requestExtra = { ref: parsed };
|
|
1199
|
+
}
|
|
1200
|
+
else {
|
|
1201
|
+
console.error('Provide a ref (e.g. `click 5`) or coordinates (`click --at X,Y`).');
|
|
1202
|
+
process.exit(1);
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1145
1205
|
const response = await sendIPCRequest({
|
|
1146
1206
|
action: 'click',
|
|
1147
1207
|
task,
|
|
1148
1208
|
tabId: opts.tab,
|
|
1149
|
-
|
|
1209
|
+
...requestExtra,
|
|
1150
1210
|
});
|
|
1151
1211
|
if (!response.ok) {
|
|
1152
1212
|
console.error(response.error);
|
|
1153
1213
|
process.exit(1);
|
|
1154
1214
|
}
|
|
1155
|
-
console.log('Clicked');
|
|
1215
|
+
console.log(response.message ? `Clicked (${response.message})` : 'Clicked');
|
|
1156
1216
|
});
|
|
1157
1217
|
browser
|
|
1158
1218
|
.command('type <ref>')
|
|
@@ -1423,13 +1483,18 @@ function registerTaskCommands(browser) {
|
|
|
1423
1483
|
// ─── Network ─────────────────────────────────────────────────────────────────
|
|
1424
1484
|
browser
|
|
1425
1485
|
.command('requests')
|
|
1426
|
-
.description('Read captured network requests')
|
|
1486
|
+
.description('Read captured network requests. --format har emits a HAR 1.2 JSON document.')
|
|
1427
1487
|
.option(TASK_OPTION_FLAG, TASK_OPTION_DESC)
|
|
1428
1488
|
.option('-t, --tab <tabId>', 'Tab ID (defaults to current)')
|
|
1429
1489
|
.option('-f, --filter <text>', 'Filter URLs containing text')
|
|
1430
1490
|
.option('--clear', 'Clear requests after reading')
|
|
1491
|
+
.option('--format <format>', 'Output format: table (default) or har', 'table')
|
|
1431
1492
|
.action(async (opts) => {
|
|
1432
1493
|
const task = resolveTaskName(opts);
|
|
1494
|
+
if (opts.format !== 'table' && opts.format !== 'har') {
|
|
1495
|
+
console.error('--format must be "table" or "har"');
|
|
1496
|
+
process.exit(1);
|
|
1497
|
+
}
|
|
1433
1498
|
const response = await sendIPCRequest({
|
|
1434
1499
|
action: 'requests',
|
|
1435
1500
|
task,
|
|
@@ -1441,13 +1506,22 @@ function registerTaskCommands(browser) {
|
|
|
1441
1506
|
console.error(response.error);
|
|
1442
1507
|
process.exit(1);
|
|
1443
1508
|
}
|
|
1444
|
-
|
|
1509
|
+
const requests = response.requests ?? [];
|
|
1510
|
+
if (opts.format === 'har') {
|
|
1511
|
+
const har = buildHar(requests, {
|
|
1512
|
+
creatorName: 'agents-cli',
|
|
1513
|
+
creatorVersion: getCliVersion(),
|
|
1514
|
+
});
|
|
1515
|
+
console.log(JSON.stringify(har, null, 2));
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
if (requests.length === 0) {
|
|
1445
1519
|
console.log('No requests captured');
|
|
1446
1520
|
return;
|
|
1447
1521
|
}
|
|
1448
1522
|
console.log('METHOD'.padEnd(8) + 'STATUS'.padEnd(8) + 'URL');
|
|
1449
1523
|
console.log('-'.repeat(72));
|
|
1450
|
-
for (const req of
|
|
1524
|
+
for (const req of requests) {
|
|
1451
1525
|
const status = req.status ? String(req.status) : '...';
|
|
1452
1526
|
console.log(`${req.method.padEnd(8)}${status.padEnd(8)}${req.url.slice(0, 100)}`);
|
|
1453
1527
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents check` — scriptable drift gate for CI.
|
|
3
|
+
*
|
|
4
|
+
* Runs the SAME drift/divergence diagnostic `agents doctor` computes (via the
|
|
5
|
+
* shared `computeDrift` in `../lib/drift.js`) and turns it into an exit code:
|
|
6
|
+
* non-zero when any installed version is stale or never-synced, zero when the
|
|
7
|
+
* whole install is fresh. `agents doctor` is the human report; `agents check`
|
|
8
|
+
* is the machine gate — same engine, different output.
|
|
9
|
+
*
|
|
10
|
+
* Orphans are surfaced informationally but never fail the check: they are a
|
|
11
|
+
* `prune` concern, not sync drift (mirrors the sync-status engine, where an
|
|
12
|
+
* orphan alone never flags needsSync).
|
|
13
|
+
*/
|
|
14
|
+
import type { Command } from 'commander';
|
|
15
|
+
export declare function registerCheckCommand(program: Command): void;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { AGENTS, ALL_AGENT_IDS } from '../lib/agents.js';
|
|
3
|
+
import { setHelpSections } from '../lib/help.js';
|
|
4
|
+
import { computeDrift } from '../lib/drift.js';
|
|
5
|
+
const AGENT_NAMES = Object.fromEntries(ALL_AGENT_IDS.map((id) => [id, AGENTS[id].name]));
|
|
6
|
+
function label(row) {
|
|
7
|
+
return `${AGENT_NAMES[row.agent] || row.agent}@${row.version}`;
|
|
8
|
+
}
|
|
9
|
+
export function registerCheckCommand(program) {
|
|
10
|
+
const checkCmd = program
|
|
11
|
+
.command('check')
|
|
12
|
+
.description('CI drift gate: exit non-zero when any installed version is out of sync (stale or never-synced), zero when clean.')
|
|
13
|
+
.option('--json', 'Output machine-readable JSON')
|
|
14
|
+
.option('-q, --quiet', 'Suppress per-version lines; print only the one-line verdict')
|
|
15
|
+
.option('--cwd <path>', 'Resolution cwd for project layer detection (default: process.cwd())');
|
|
16
|
+
setHelpSections(checkCmd, {
|
|
17
|
+
examples: `
|
|
18
|
+
# Fail the build if anything drifted (exit 1), pass if clean (exit 0)
|
|
19
|
+
agents check
|
|
20
|
+
|
|
21
|
+
# Just the verdict line, nothing per-version
|
|
22
|
+
agents check --quiet
|
|
23
|
+
|
|
24
|
+
# Machine-readable, for scripting
|
|
25
|
+
agents check --json
|
|
26
|
+
|
|
27
|
+
# Gate in a CI step
|
|
28
|
+
agents check || { echo "resources drifted — run 'agents doctor --fix'"; exit 1; }
|
|
29
|
+
`,
|
|
30
|
+
});
|
|
31
|
+
checkCmd.action((opts) => {
|
|
32
|
+
const cwd = opts.cwd ? opts.cwd : process.cwd();
|
|
33
|
+
const drift = computeDrift(cwd);
|
|
34
|
+
if (opts.json) {
|
|
35
|
+
console.log(JSON.stringify({
|
|
36
|
+
hasDrift: drift.hasDrift,
|
|
37
|
+
stale: drift.staleCount,
|
|
38
|
+
neverSynced: drift.neverSyncedCount,
|
|
39
|
+
orphanVersions: drift.orphanVersionCount,
|
|
40
|
+
versions: drift.syncRows.map((r) => ({
|
|
41
|
+
agent: r.agent,
|
|
42
|
+
version: r.version,
|
|
43
|
+
status: r.status,
|
|
44
|
+
isDefault: r.isDefault,
|
|
45
|
+
divergence: r.divergence ?? [],
|
|
46
|
+
})),
|
|
47
|
+
}, null, 2));
|
|
48
|
+
process.exit(drift.hasDrift ? 1 : 0);
|
|
49
|
+
}
|
|
50
|
+
if (drift.syncRows.length === 0) {
|
|
51
|
+
// Nothing installed is a clean state, not a failure — CI on a fresh
|
|
52
|
+
// checkout with no versions should pass, not error.
|
|
53
|
+
console.log(chalk.gray('check: no installed versions — nothing to verify'));
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
if (!drift.hasDrift) {
|
|
57
|
+
const orphanNote = drift.orphanVersionCount > 0
|
|
58
|
+
? chalk.gray(` (${drift.orphanVersionCount} version(s) carry orphans — run \`agents prune cleanup\`)`)
|
|
59
|
+
: '';
|
|
60
|
+
console.log(`${chalk.green('ok')} ${drift.syncRows.length} version(s) in sync${orphanNote}`);
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
// Drift: one-line verdict always, per-version detail unless --quiet.
|
|
64
|
+
const parts = [];
|
|
65
|
+
if (drift.staleCount > 0)
|
|
66
|
+
parts.push(`${drift.staleCount} stale`);
|
|
67
|
+
if (drift.neverSyncedCount > 0)
|
|
68
|
+
parts.push(`${drift.neverSyncedCount} never-synced`);
|
|
69
|
+
console.error(`${chalk.red('drift')} ${parts.join(', ')} of ${drift.syncRows.length} version(s)`);
|
|
70
|
+
if (!opts.quiet) {
|
|
71
|
+
for (const row of drift.syncRows) {
|
|
72
|
+
if (row.status === 'fresh')
|
|
73
|
+
continue;
|
|
74
|
+
const tag = row.status === 'stale' ? chalk.yellow('stale') : chalk.gray('cold ');
|
|
75
|
+
console.error(` ${tag} ${label(row)}`);
|
|
76
|
+
for (const line of row.divergence ?? []) {
|
|
77
|
+
console.error(chalk.gray(` ${line}`));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
console.error(chalk.gray('\nReconcile with `agents doctor --fix` (or `agents doctor <agent>@<version> --fix`).'));
|
|
81
|
+
}
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
84
|
+
}
|
package/dist/commands/cloud.js
CHANGED
|
@@ -1,10 +1,39 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
3
4
|
import ora from 'ora';
|
|
4
5
|
import { resolveProvider, getAllProviders, getDefaultProviderId } from '../lib/cloud/registry.js';
|
|
5
6
|
import { insertTask, updateTaskStatus, getTaskById, listTasks as listStoredTasks, listActiveTasks } from '../lib/cloud/store.js';
|
|
6
7
|
import { renderStream } from '../lib/cloud/stream.js';
|
|
7
|
-
import { MissingTargetError } from '../lib/cloud/types.js';
|
|
8
|
+
import { MissingTargetError, MAX_IMAGES_PER_DISPATCH } from '../lib/cloud/types.js';
|
|
9
|
+
import { normalizeTriggerEvent, validateTrigger, writeJob, jobExists, GITHUB_TRIGGER_EVENTS } from '../lib/routines.js';
|
|
10
|
+
/** Map a supported image file extension to its wire mimeType. Rejects anything else. */
|
|
11
|
+
function imageMimeFromPath(file) {
|
|
12
|
+
const ext = path.extname(file).toLowerCase();
|
|
13
|
+
if (ext === '.png')
|
|
14
|
+
return 'image/png';
|
|
15
|
+
if (ext === '.jpg' || ext === '.jpeg')
|
|
16
|
+
return 'image/jpeg';
|
|
17
|
+
if (ext === '.webp')
|
|
18
|
+
return 'image/webp';
|
|
19
|
+
die(`Unsupported image type ${JSON.stringify(ext || file)}. Use .png, .jpg/.jpeg, or .webp.`);
|
|
20
|
+
}
|
|
21
|
+
/** Read one image file into a base64 ImageAttachment, dying with a clear error if it's missing. */
|
|
22
|
+
function readImageAttachment(file) {
|
|
23
|
+
if (!fs.existsSync(file) || !fs.statSync(file).isFile()) {
|
|
24
|
+
die(`Image not found: ${file}`);
|
|
25
|
+
}
|
|
26
|
+
const mimeType = imageMimeFromPath(file);
|
|
27
|
+
return { data: fs.readFileSync(file).toString('base64'), mimeType };
|
|
28
|
+
}
|
|
29
|
+
/** Parse a `--skill <id>` value (`id` or `id@version`) into a SkillRef. */
|
|
30
|
+
function parseSkillRef(raw) {
|
|
31
|
+
const at = raw.lastIndexOf('@');
|
|
32
|
+
if (at > 0) {
|
|
33
|
+
return { id: raw.slice(0, at), version: raw.slice(at + 1) };
|
|
34
|
+
}
|
|
35
|
+
return { id: raw };
|
|
36
|
+
}
|
|
8
37
|
/** Print an error message to stderr and exit. */
|
|
9
38
|
function die(msg, code = 1) {
|
|
10
39
|
console.error(chalk.red(msg));
|
|
@@ -146,6 +175,8 @@ Examples:
|
|
|
146
175
|
return acc;
|
|
147
176
|
})
|
|
148
177
|
.option('--branch <name>', 'Target git branch')
|
|
178
|
+
.option('--on <event>', 'Register this run as an event trigger instead of dispatching now: pull_request (pr), push, issue_comment, workflow_run. Persists a trigger-bound routine.')
|
|
179
|
+
.option('--name <name>', 'Routine name to register under (with --on). Defaults to a generated name.')
|
|
149
180
|
.option('-p, --prompt <text>', 'Inline prompt (alternative to positional argument)')
|
|
150
181
|
.option('--timeout <duration>', 'Kill after duration (e.g., 30m, 2h)')
|
|
151
182
|
.option('--model <model>', 'Model override')
|
|
@@ -153,6 +184,16 @@ Examples:
|
|
|
153
184
|
.option('--computer <name>', 'Factory/Droid computer target')
|
|
154
185
|
.option('--autonomy <level>', 'Factory/Droid autonomy: low, medium, high (default high)')
|
|
155
186
|
.option('--mode <mode>', 'Execution mode (e.g., plan, edit, full)')
|
|
187
|
+
.option('--image <path>', `Attach an image (.png/.jpg/.webp) for vision dispatch. Repeatable, up to ${MAX_IMAGES_PER_DISPATCH} (Rush Cloud only).`, (value, previous) => {
|
|
188
|
+
const acc = Array.isArray(previous) ? previous : [];
|
|
189
|
+
acc.push(value);
|
|
190
|
+
return acc;
|
|
191
|
+
})
|
|
192
|
+
.option('--skill <id>', 'Ride-along skill by id (or id@version). Repeatable (Rush Cloud only).', (value, previous) => {
|
|
193
|
+
const acc = Array.isArray(previous) ? previous : [];
|
|
194
|
+
acc.push(value);
|
|
195
|
+
return acc;
|
|
196
|
+
})
|
|
156
197
|
.option('-b, --balanced', 'Shortcut for --strategy balanced. Route the factory run across all healthy accounts.')
|
|
157
198
|
.option('--strategy <strategy>', 'Account selection strategy for the factory: balanced. Sends all healthy accounts so the factory pod rotates between them on rate-limit.')
|
|
158
199
|
.option('--upload-account-tokens', 'Upload Claude OAuth credentials to Rush Cloud on first dispatch (consent recorded for future runs).')
|
|
@@ -224,6 +265,73 @@ Examples:
|
|
|
224
265
|
}
|
|
225
266
|
if (options.uploadAccountTokens)
|
|
226
267
|
dispatchOptions.providerOptions.uploadAccountTokens = true;
|
|
268
|
+
// --on <event>: register this run as an event trigger instead of
|
|
269
|
+
// dispatching now. We parse + validate the event, attach it to
|
|
270
|
+
// dispatchOptions.trigger, and persist a trigger-bound routine so the
|
|
271
|
+
// local webhook receiver can fire it (src/lib/triggers/webhook.ts).
|
|
272
|
+
// Remote firing of the trigger is a follow-up.
|
|
273
|
+
if (options.on) {
|
|
274
|
+
const event = normalizeTriggerEvent(options.on);
|
|
275
|
+
if (!event) {
|
|
276
|
+
die(`Unknown --on event "${options.on}". Use one of: ${GITHUB_TRIGGER_EVENTS.join(', ')} (aliases: pr, comment, workflow).`);
|
|
277
|
+
}
|
|
278
|
+
const trigger = { type: 'github_event', event: event };
|
|
279
|
+
if (repoValues[0])
|
|
280
|
+
trigger.repo = repoValues[0];
|
|
281
|
+
if (options.branch)
|
|
282
|
+
trigger.branch = options.branch;
|
|
283
|
+
const triggerErrors = validateTrigger(trigger);
|
|
284
|
+
if (triggerErrors.length > 0)
|
|
285
|
+
die(`Invalid trigger: ${triggerErrors.join(', ')}`);
|
|
286
|
+
dispatchOptions.trigger = trigger;
|
|
287
|
+
const routineName = options.name
|
|
288
|
+
|| `cloud-${event}-${(repoValues[0] || 'any').replace(/[^a-z0-9]+/gi, '-')}`.toLowerCase();
|
|
289
|
+
if (jobExists(routineName)) {
|
|
290
|
+
die(`A routine named "${routineName}" already exists. Pass --name to register under a different name.`);
|
|
291
|
+
}
|
|
292
|
+
const routine = {
|
|
293
|
+
name: routineName,
|
|
294
|
+
trigger,
|
|
295
|
+
agent: options.agent || 'claude',
|
|
296
|
+
mode: 'plan',
|
|
297
|
+
effort: 'auto',
|
|
298
|
+
timeout: options.timeout || '10m',
|
|
299
|
+
enabled: true,
|
|
300
|
+
prompt,
|
|
301
|
+
};
|
|
302
|
+
if (repoValues[0])
|
|
303
|
+
routine.repo = repoValues[0];
|
|
304
|
+
writeJob(routine);
|
|
305
|
+
if (json) {
|
|
306
|
+
console.log(JSON.stringify({ ok: true, registered: routineName, trigger }, null, 2));
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
console.log(chalk.green(`Registered trigger routine "${routineName}"`));
|
|
310
|
+
console.log(` ${chalk.dim('on:')} ${event}${trigger.repo ? ` (${trigger.repo}${trigger.branch ? `@${trigger.branch}` : ''})` : ''}`);
|
|
311
|
+
console.log(` ${chalk.dim('agent:')} ${routine.agent}`);
|
|
312
|
+
console.log(chalk.dim(`\nFires when a matching GitHub webhook is delivered to the local receiver. Remote firing is a follow-up.`));
|
|
313
|
+
}
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
// Vision attachments + ride-along skills. Only wire them when the resolved
|
|
317
|
+
// provider advertises support — otherwise fail loud rather than silently
|
|
318
|
+
// drop the flags the user passed.
|
|
319
|
+
const imagePaths = Array.isArray(options.image) ? options.image : [];
|
|
320
|
+
const skillIds = Array.isArray(options.skill) ? options.skill : [];
|
|
321
|
+
const caps = provider.capabilities();
|
|
322
|
+
if (imagePaths.length > 0) {
|
|
323
|
+
if (!caps.images)
|
|
324
|
+
die(`${provider.name} does not support image attachments.`);
|
|
325
|
+
if (imagePaths.length > MAX_IMAGES_PER_DISPATCH) {
|
|
326
|
+
die(`Too many images: ${imagePaths.length}. Max is ${MAX_IMAGES_PER_DISPATCH} per dispatch.`);
|
|
327
|
+
}
|
|
328
|
+
dispatchOptions.images = imagePaths.map(readImageAttachment);
|
|
329
|
+
}
|
|
330
|
+
if (skillIds.length > 0) {
|
|
331
|
+
if (!caps.skills)
|
|
332
|
+
die(`${provider.name} does not support ride-along skills.`);
|
|
333
|
+
dispatchOptions.skills = skillIds.map(parseSkillRef);
|
|
334
|
+
}
|
|
227
335
|
// Dispatch. On a missing pre-provisioned target (Codex env / Factory
|
|
228
336
|
// computer), offer an interactive picker instead of a raw error.
|
|
229
337
|
const dispatchOnce = async () => {
|
|
@@ -269,11 +377,29 @@ Examples:
|
|
|
269
377
|
if (options.follow === false)
|
|
270
378
|
return;
|
|
271
379
|
try {
|
|
272
|
-
|
|
380
|
+
// Live budget kill-switch (issue #399). Reuses makeLiveSpendWatcher to
|
|
381
|
+
// feed the provider's `usage` events into a shared watcher; on a cap
|
|
382
|
+
// breach we call provider.cancel(task.id) mid-stream. Dormant (returns
|
|
383
|
+
// null) when no caps are configured, so the raw stream flows unchanged.
|
|
384
|
+
const { wrapStreamWithBudgetGate } = await import('../lib/budget/live-cloud.js');
|
|
385
|
+
const gated = wrapStreamWithBudgetGate({
|
|
386
|
+
provider,
|
|
387
|
+
taskId: task.id,
|
|
388
|
+
project: task.repo ?? task.repos?.[0] ?? process.cwd(),
|
|
389
|
+
agent: task.agent ?? 'cloud',
|
|
390
|
+
cwd: process.cwd(),
|
|
391
|
+
});
|
|
392
|
+
const eventSource = gated ? gated.wrap(provider.stream(task.id)) : provider.stream(task.id);
|
|
393
|
+
const result = await renderStream(eventSource, { json });
|
|
273
394
|
updateTaskStatus(task.id, result.status, {
|
|
274
395
|
summary: result.summary,
|
|
275
396
|
prUrl: result.prUrl,
|
|
276
397
|
});
|
|
398
|
+
if (gated?.gate.breached()) {
|
|
399
|
+
const b = gated.gate.breach();
|
|
400
|
+
process.stderr.write(`[budget] cap ${b?.cap} exceeded — cancelled cloud task ${task.id}\n`);
|
|
401
|
+
process.exitCode = 7; // Mirrors BUDGET_KILL_EXIT_CODE for CI/headless.
|
|
402
|
+
}
|
|
277
403
|
}
|
|
278
404
|
catch (err) {
|
|
279
405
|
// Stream disconnect is OK — task keeps running
|
|
@@ -415,11 +541,25 @@ Examples:
|
|
|
415
541
|
die(`Task ${id} not found locally. Run 'agents cloud list' first.`);
|
|
416
542
|
const provider = resolveProvider(task.provider);
|
|
417
543
|
try {
|
|
418
|
-
|
|
544
|
+
// Live budget kill-switch (issue #399) — wrap the stream so a cap
|
|
545
|
+
// breach mid-stream cancels the task server-side. Dormant when no caps.
|
|
546
|
+
const { wrapStreamWithBudgetGate } = await import('../lib/budget/live-cloud.js');
|
|
547
|
+
const gated = wrapStreamWithBudgetGate({
|
|
548
|
+
provider,
|
|
549
|
+
taskId: id,
|
|
550
|
+
project: task.repo ?? task.repos?.[0] ?? process.cwd(),
|
|
551
|
+
agent: task.agent ?? 'cloud',
|
|
552
|
+
cwd: process.cwd(),
|
|
553
|
+
});
|
|
554
|
+
const eventSource = gated ? gated.wrap(provider.stream(id)) : provider.stream(id);
|
|
555
|
+
const result = await renderStream(eventSource, { json });
|
|
419
556
|
updateTaskStatus(id, result.status, {
|
|
420
557
|
summary: result.summary,
|
|
421
558
|
prUrl: result.prUrl,
|
|
422
559
|
});
|
|
560
|
+
if (gated?.gate.breached()) {
|
|
561
|
+
process.exitCode = 7;
|
|
562
|
+
}
|
|
423
563
|
}
|
|
424
564
|
catch (err) {
|
|
425
565
|
process.stderr.write(chalk.dim(`\nStream ended. ${err.message}\n`));
|
|
@@ -55,4 +55,19 @@ export declare function buildWaitParams(opts: {
|
|
|
55
55
|
};
|
|
56
56
|
export declare function withClient<T>(fn: (client: ComputerClient) => Promise<T>): Promise<T>;
|
|
57
57
|
export declare function unwrap(r: RPCResponse): Record<string, unknown>;
|
|
58
|
+
export declare function resolveTargetPidDecision(client: ComputerClient, opts: {
|
|
59
|
+
pid?: number;
|
|
60
|
+
bundle?: string;
|
|
61
|
+
}, gate?: {
|
|
62
|
+
verb?: string;
|
|
63
|
+
env?: NodeJS.ProcessEnv;
|
|
64
|
+
nowMs?: number;
|
|
65
|
+
}): Promise<{
|
|
66
|
+
ok: true;
|
|
67
|
+
pid: number;
|
|
68
|
+
source: 'pid' | 'list_apps' | 'session_admission';
|
|
69
|
+
} | {
|
|
70
|
+
ok: false;
|
|
71
|
+
error: string;
|
|
72
|
+
}>;
|
|
58
73
|
export declare function registerActionCommands(program: Command): void;
|