@phnx-labs/agents-cli 1.22.116 → 1.22.118
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 +247 -0
- package/dist/bootstrap.js +4 -11
- package/dist/commands/browser.d.ts +52 -70
- package/dist/commands/browser.js +192 -3562
- package/dist/commands/daemon.d.ts +39 -2
- package/dist/commands/daemon.js +72 -73
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.d.ts +18 -0
- package/dist/commands/exec.js +104 -12
- package/dist/commands/repo.js +13 -49
- package/dist/commands/route.js +5 -5
- package/dist/commands/routines.js +39 -12
- package/dist/commands/sessions-render.d.ts +1 -1
- package/dist/commands/sessions-render.js +0 -1
- package/dist/commands/sessions.test-fixture.d.ts +0 -1
- package/dist/commands/sessions.test-fixture.js +0 -37
- package/dist/commands/setup-browser.d.ts +17 -12
- package/dist/commands/setup-browser.js +52 -87
- package/dist/commands/setup-preferences.d.ts +0 -17
- package/dist/commands/setup-preferences.js +18 -49
- package/dist/commands/setup-term.d.ts +4 -3
- package/dist/commands/setup-term.js +5 -4
- package/dist/commands/setup.js +11 -11
- package/dist/commands/ssh.js +10 -1
- package/dist/commands/sync.js +6 -44
- package/dist/commands/teams.js +0 -1
- package/dist/commands/update.d.ts +19 -0
- package/dist/commands/update.js +34 -6
- package/dist/commands/view.d.ts +12 -1
- package/dist/commands/view.js +47 -2
- package/dist/index.js +107 -26
- package/dist/lib/account-capabilities.js +0 -2
- package/dist/lib/account-catalog.d.ts +38 -0
- package/dist/lib/account-catalog.js +110 -26
- package/dist/lib/account-provider-registry.js +1 -1
- package/dist/lib/account-registry.d.ts +1 -1
- package/dist/lib/account-registry.js +36 -2
- package/dist/lib/accounting/rotate.js +22 -8
- package/dist/lib/accounting/usage-ingest.d.ts +6 -1
- package/dist/lib/accounting/usage-ingest.js +115 -45
- package/dist/lib/accounting/usage-sync.d.ts +134 -31
- package/dist/lib/accounting/usage-sync.js +227 -66
- package/dist/lib/accounting/usage.d.ts +16 -3
- package/dist/lib/accounting/usage.js +26 -8
- package/dist/lib/accounts/slots.d.ts +8 -0
- package/dist/lib/accounts/slots.js +17 -0
- package/dist/lib/add-dir.js +0 -1
- package/dist/lib/agent-cli-commands.js +0 -1
- package/dist/lib/agent-spec/agents.d.ts +6 -7
- package/dist/lib/agent-spec/agents.js +6 -60
- package/dist/lib/auth-health.d.ts +69 -1
- package/dist/lib/auth-health.js +133 -4
- package/dist/lib/browser/context.d.ts +82 -0
- package/dist/lib/browser/context.js +88 -0
- package/dist/lib/browser/paths.d.ts +25 -0
- package/dist/lib/browser/paths.js +51 -0
- package/dist/lib/browser/record.d.ts +38 -0
- package/dist/lib/browser/record.js +57 -0
- package/dist/lib/browser/sessions-list.js +1 -2
- package/dist/lib/browser-client.d.ts +158 -0
- package/dist/lib/browser-client.js +234 -0
- package/dist/lib/channels/owner-sink.d.ts +1 -18
- package/dist/lib/channels/owner-sink.js +29 -82
- package/dist/lib/channels/providers/rush.d.ts +7 -7
- package/dist/lib/channels/providers/rush.js +127 -57
- package/dist/lib/claude-statusline.d.ts +1 -1
- package/dist/lib/claude-statusline.js +15 -3
- package/dist/lib/commands.js +1 -1
- package/dist/lib/crabbox/lease.js +1 -1
- package/dist/lib/crabbox/runtimes.js +1 -3
- package/dist/lib/daemon/account-state-daemon-service.js +1 -0
- package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
- package/dist/lib/daemon/auth-sync-service.js +37 -43
- package/dist/lib/daemon/daemon.d.ts +25 -14
- package/dist/lib/daemon/daemon.js +92 -154
- package/dist/lib/daemon/runner.d.ts +2 -2
- package/dist/lib/daemon/runner.js +32 -9
- package/dist/lib/daemon/self-update-service.d.ts +35 -34
- package/dist/lib/daemon/self-update-service.js +40 -36
- package/dist/lib/daemon/service.d.ts +12 -8
- package/dist/lib/daemon/supervisor.d.ts +65 -54
- package/dist/lib/daemon/supervisor.js +136 -171
- package/dist/lib/daemon/testdata/process-view-start.js +5 -2
- package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
- package/dist/lib/daemon/usage-sync-service.js +46 -78
- package/dist/lib/daemon-health.d.ts +22 -5
- package/dist/lib/daemon-health.js +47 -3
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +0 -10
- package/dist/lib/daemon-ticks.js +4 -2
- package/dist/lib/devices/doctor-findings.js +8 -11
- package/dist/lib/devices/harness-inventory.js +14 -5
- package/dist/lib/exec-account-home.js +5 -0
- package/dist/lib/exec.js +0 -11
- package/dist/lib/feed/events.js +0 -1
- package/dist/lib/feed/tool-activity.js +5 -2
- package/dist/lib/fleet-shared-state.d.ts +45 -0
- package/dist/lib/fleet-shared-state.js +78 -9
- package/dist/lib/fs-atomic.js +25 -0
- package/dist/lib/harness-auth-capabilities.js +0 -1
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/hooks/cache.d.ts +7 -0
- package/dist/lib/hooks/cache.js +27 -2
- package/dist/lib/hooks/install.js +7 -1
- package/dist/lib/hosts/ready.d.ts +38 -8
- package/dist/lib/hosts/ready.js +62 -14
- package/dist/lib/hosts/remote-cmd.js +1 -0
- package/dist/lib/installations/active-check.d.ts +27 -0
- package/dist/lib/installations/active-check.js +61 -0
- package/dist/lib/installations/migrate.js +6 -3
- package/dist/lib/installations/shims.js +9 -7
- package/dist/lib/installations/update.js +21 -15
- package/dist/lib/installations/versions.js +12 -1
- package/dist/lib/manifest.js +1 -1
- package/dist/lib/models.js +0 -2
- package/dist/lib/open-url.js +28 -54
- package/dist/lib/placement.d.ts +2 -0
- package/dist/lib/placement.js +5 -0
- package/dist/lib/profiles.js +0 -2
- package/dist/lib/refresh.js +1 -1
- package/dist/lib/routine-readiness.d.ts +35 -0
- package/dist/lib/routine-readiness.js +74 -16
- package/dist/lib/rules/compile.js +6 -0
- package/dist/lib/sandbox.js +0 -2
- package/dist/lib/secrets-policy.d.ts +89 -11
- package/dist/lib/secrets-policy.js +175 -28
- package/dist/lib/session/db.d.ts +1 -1
- package/dist/lib/session/db.js +65 -18
- package/dist/lib/session/discover.js +1 -283
- package/dist/lib/session/mirror.d.ts +4 -3
- package/dist/lib/session/mirror.js +4 -3
- package/dist/lib/session/parse.d.ts +0 -2
- package/dist/lib/session/parse.js +1 -138
- package/dist/lib/session/throughput.d.ts +1 -3
- package/dist/lib/session/throughput.js +0 -20
- package/dist/lib/session/types.d.ts +2 -2
- package/dist/lib/session/types.js +2 -2
- package/dist/lib/sessions-client.d.ts +60 -1
- package/dist/lib/sessions-client.js +227 -4
- package/dist/lib/setup-tool-status.js +8 -12
- package/dist/lib/signin-badge.d.ts +3 -2
- package/dist/lib/signin-badge.js +6 -3
- package/dist/lib/smart-launch.js +4 -1
- package/dist/lib/staleness/detectors/commands.d.ts +1 -1
- package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
- package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
- package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
- package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
- package/dist/lib/staleness/detectors/rules.d.ts +1 -1
- package/dist/lib/staleness/detectors/skills.d.ts +1 -1
- package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
- package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
- package/dist/lib/staleness/writers/commands.d.ts +1 -1
- package/dist/lib/staleness/writers/hooks.d.ts +1 -1
- package/dist/lib/staleness/writers/mcp.d.ts +1 -1
- package/dist/lib/staleness/writers/permissions.d.ts +1 -1
- package/dist/lib/staleness/writers/plugins.d.ts +1 -1
- package/dist/lib/staleness/writers/rules.d.ts +1 -1
- package/dist/lib/staleness/writers/skills.d.ts +1 -1
- package/dist/lib/staleness/writers/sources.js +5 -0
- package/dist/lib/staleness/writers/subagents.d.ts +1 -1
- package/dist/lib/staleness/writers/workflows.d.ts +1 -1
- package/dist/lib/state.d.ts +6 -4
- package/dist/lib/state.js +7 -17
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/parsers.js +1 -136
- package/dist/lib/teams/placement-probe.js +50 -15
- package/dist/lib/teams/scheduler.d.ts +7 -0
- package/dist/lib/types.d.ts +10 -1
- package/dist/lib/types.js +1 -1
- package/dist/lib/view-types.d.ts +32 -0
- package/dist/lib/watchdog/read.js +0 -1
- package/dist/lib/watchdog/rotate.js +1 -2
- package/dist/lib/watchdog/runner.js +2 -2
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdogTail.js +0 -31
- package/package.json +1 -1
- package/scripts/postinstall.js +8 -8
- package/dist/browser.d.ts +0 -2
- package/dist/browser.js +0 -17
- package/dist/commands/browser-picker.d.ts +0 -18
- package/dist/commands/browser-picker.js +0 -97
- package/dist/lib/browser/arc-discovery.d.ts +0 -52
- package/dist/lib/browser/arc-discovery.js +0 -197
- package/dist/lib/browser/arc-dom.d.ts +0 -14
- package/dist/lib/browser/arc-dom.js +0 -121
- package/dist/lib/browser/caller-identity.d.ts +0 -40
- package/dist/lib/browser/caller-identity.js +0 -175
- package/dist/lib/browser/cdp.d.ts +0 -51
- package/dist/lib/browser/cdp.js +0 -254
- package/dist/lib/browser/chrome.d.ts +0 -141
- package/dist/lib/browser/chrome.js +0 -704
- package/dist/lib/browser/chromium-discovery.d.ts +0 -28
- package/dist/lib/browser/chromium-discovery.js +0 -96
- package/dist/lib/browser/devices.d.ts +0 -34
- package/dist/lib/browser/devices.js +0 -61
- package/dist/lib/browser/domain-skills.d.ts +0 -71
- package/dist/lib/browser/domain-skills.js +0 -195
- package/dist/lib/browser/drivers/arc.d.ts +0 -40
- package/dist/lib/browser/drivers/arc.js +0 -276
- package/dist/lib/browser/drivers/firefox.d.ts +0 -99
- package/dist/lib/browser/drivers/firefox.js +0 -377
- package/dist/lib/browser/drivers/local.d.ts +0 -55
- package/dist/lib/browser/drivers/local.js +0 -266
- package/dist/lib/browser/drivers/ssh.d.ts +0 -120
- package/dist/lib/browser/drivers/ssh.js +0 -467
- package/dist/lib/browser/editor.d.ts +0 -3
- package/dist/lib/browser/editor.js +0 -50
- package/dist/lib/browser/ffmpeg.d.ts +0 -12
- package/dist/lib/browser/ffmpeg.js +0 -184
- package/dist/lib/browser/firefox-discovery.d.ts +0 -69
- package/dist/lib/browser/firefox-discovery.js +0 -162
- package/dist/lib/browser/har.d.ts +0 -85
- package/dist/lib/browser/har.js +0 -77
- package/dist/lib/browser/hygiene.d.ts +0 -97
- package/dist/lib/browser/hygiene.js +0 -153
- package/dist/lib/browser/index.d.ts +0 -5
- package/dist/lib/browser/index.js +0 -5
- package/dist/lib/browser/input.d.ts +0 -7
- package/dist/lib/browser/input.js +0 -92
- package/dist/lib/browser/ipc.d.ts +0 -204
- package/dist/lib/browser/ipc.js +0 -1453
- package/dist/lib/browser/login-detection.d.ts +0 -87
- package/dist/lib/browser/login-detection.js +0 -274
- package/dist/lib/browser/profiles.d.ts +0 -422
- package/dist/lib/browser/profiles.js +0 -1157
- package/dist/lib/browser/refs.d.ts +0 -89
- package/dist/lib/browser/refs.js +0 -191
- package/dist/lib/browser/registry.d.ts +0 -70
- package/dist/lib/browser/registry.js +0 -252
- package/dist/lib/browser/remote-control.d.ts +0 -59
- package/dist/lib/browser/remote-control.js +0 -85
- package/dist/lib/browser/resolve-target.d.ts +0 -66
- package/dist/lib/browser/resolve-target.js +0 -264
- package/dist/lib/browser/runtime-state.d.ts +0 -291
- package/dist/lib/browser/runtime-state.js +0 -584
- package/dist/lib/browser/secret-ref.d.ts +0 -10
- package/dist/lib/browser/secret-ref.js +0 -14
- package/dist/lib/browser/service.d.ts +0 -812
- package/dist/lib/browser/service.js +0 -4693
- package/dist/lib/browser/stream.d.ts +0 -17
- package/dist/lib/browser/stream.js +0 -72
- package/dist/lib/browser/task-index.d.ts +0 -78
- package/dist/lib/browser/task-index.js +0 -199
- package/dist/lib/browser/types.d.ts +0 -647
- package/dist/lib/browser/types.js +0 -61
- package/dist/lib/browser/upload.d.ts +0 -29
- package/dist/lib/browser/upload.js +0 -298
- package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
- package/dist/lib/daemon/browser-ipc-service.js +0 -46
- package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
- package/dist/lib/daemon/browser-task-reap-service.js +0 -26
- package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
- package/dist/lib/fleet-shared-repo-sync.js +0 -490
package/dist/commands/exec.js
CHANGED
|
@@ -17,6 +17,7 @@ import { parseLoopInterval } from '../lib/loop.js';
|
|
|
17
17
|
import { AGENTS, resolveAgentName, isAgentHardDeprecated, hardDeprecationError } from '../lib/agents.js';
|
|
18
18
|
import { parseAgentVersionSpec } from '../lib/agent-spec/agents.js';
|
|
19
19
|
import { recordDispatchedRun } from '../lib/audit/log.js';
|
|
20
|
+
import { recordRunAuthOutcome } from '../lib/auth-health.js';
|
|
20
21
|
import { maybeShowStarNudge } from '../lib/star-nudge.js';
|
|
21
22
|
import { warnUnpushedWork, shouldWarnUnpushed } from '../lib/warn-unpushed.js';
|
|
22
23
|
import { warnOrphanedOpenPr } from '../lib/pr-land-detach.js';
|
|
@@ -90,6 +91,24 @@ export function parseRunPickerMarkers(agentSpec) {
|
|
|
90
91
|
export function hostTargetGiven(options) {
|
|
91
92
|
return [options.host, options.device, options.on, options.computer].filter((v) => !!v);
|
|
92
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Turn a host target that names THIS machine into an explicit local pin: the
|
|
96
|
+
* host-family flags are cleared and `local` is set, so no dispatch path
|
|
97
|
+
* self-SSHes and the bare-interactive default (`bareInteractiveRunDefaultsToDeviceAuto`)
|
|
98
|
+
* sees a decided placement. Returns true when the pin was applied. Pure over
|
|
99
|
+
* the `isSelf` predicate so the rule is testable without a device registry.
|
|
100
|
+
*/
|
|
101
|
+
export function pinLocalWhenTargetIsSelf(options, isSelf) {
|
|
102
|
+
const targets = hostTargetGiven(options);
|
|
103
|
+
if (targets.length === 0 || !targets.every(isSelf))
|
|
104
|
+
return false;
|
|
105
|
+
options.host = undefined;
|
|
106
|
+
options.device = undefined;
|
|
107
|
+
options.on = undefined;
|
|
108
|
+
options.computer = undefined;
|
|
109
|
+
options.local = true;
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
93
112
|
/**
|
|
94
113
|
* Return every option whose selection semantics conflict with an account
|
|
95
114
|
* choice (`agent#`). Device routing is deliberately absent: the marker rides
|
|
@@ -118,6 +137,8 @@ export function runAccountPickerConflicts(options) {
|
|
|
118
137
|
*/
|
|
119
138
|
export function runDevicePickerConflicts(options) {
|
|
120
139
|
const conflicts = hostTargetGiven(options).map((h) => `--device ${h}`);
|
|
140
|
+
if (options.local)
|
|
141
|
+
conflicts.push('--local');
|
|
121
142
|
if (options.lease)
|
|
122
143
|
conflicts.push('--lease');
|
|
123
144
|
if (options.box)
|
|
@@ -145,6 +166,11 @@ export { RUN_AUTO_KEYWORD };
|
|
|
145
166
|
* fleet. Pure so the pinning matrix is unit-testable.
|
|
146
167
|
*/
|
|
147
168
|
export function runAutoDefaultsToAffinity(options, env = process.env) {
|
|
169
|
+
// An explicit local pin (--local, --where local, --device <this machine>)
|
|
170
|
+
// is a decided host layer with an empty host set; read it here so the
|
|
171
|
+
// `run auto` and bare-interactive defaults share one rule.
|
|
172
|
+
if (options.local)
|
|
173
|
+
return false;
|
|
148
174
|
if (hostTargetGiven(options).length > 0)
|
|
149
175
|
return false;
|
|
150
176
|
if (env.AGENTS_RUN_AUTO_HOST_RESOLVED === '1')
|
|
@@ -639,7 +665,8 @@ export function registerRunCommand(program) {
|
|
|
639
665
|
.option('--until <signal>', 'Loop stop condition. `signal` reads <runDir>/loop-signal.json {continue,reason} each iteration; absent or continue:false stops (fail-closed). Loop only.')
|
|
640
666
|
.option('--interval <dur>', 'Loop delay between iterations ("0" back-to-back, "30m" paces). Loop only.')
|
|
641
667
|
.option('--where <spec>', 'Where this run\'s body executes (one placement door): local | device:<name> | auto | lease[:backend] | cloud[:provider]. Expands to --device/--lease/--cloud. Do not combine with those flags. See docs/00-concepts.md#placement.')
|
|
642
|
-
.option('
|
|
668
|
+
.option('--local', 'Run on this machine. A bare interactive run otherwise places itself like --device auto; --local pins it here. Same as --where local or --device <this machine>.')
|
|
669
|
+
.option('-D, --device <name>', 'Offload this run onto another machine over SSH — a registered device, or user@host. Pass "auto" to pick the least-loaded reachable device where the requested agent is installed and signed in, keeping the run local when no remote is better, or "interactive" for the machine pinned as interactive.host (the box a human is sitting at). Naming this machine runs locally, no SSH. Same as --where device:<name>. See `agents devices`.')
|
|
643
670
|
.option('--remote-cwd <dir>', "Explicit device working directory for --device runs, used VERBATIM (overrides --cwd; usually --cwd suffices — it re-roots a local-home path onto the remote home). Pass a single-quoted '$HOME/…' or a valid remote absolute path; a local ~ expands here and won't exist there (/Users/you vs /home/you).")
|
|
644
671
|
.option('--no-follow', 'With --device, dispatch detached and return immediately (track via `agents hosts ps/logs`).')
|
|
645
672
|
.option('--any', 'With --device <cap> (a capability tag), pick any matching device instead of erroring when several match.')
|
|
@@ -680,9 +707,9 @@ export function registerRunCommand(program) {
|
|
|
680
707
|
agents run claude "fix lint errors in src/" --mode edit
|
|
681
708
|
|
|
682
709
|
# Interactive (TUI): a bare run places itself like --device auto (a fleet
|
|
683
|
-
# worker, TUI forwarded over SSH);
|
|
710
|
+
# worker, TUI forwarded over SSH); --local keeps it on this machine
|
|
684
711
|
agents run claude
|
|
685
|
-
agents run claude --
|
|
712
|
+
agents run claude --local # stay local (or pick this machine in claude@)
|
|
686
713
|
|
|
687
714
|
# Pick a signed-in account/version for only this run (# = account picker)
|
|
688
715
|
agents run claude#
|
|
@@ -790,10 +817,10 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
790
817
|
Interactive placement: a bare 'agents run <harness>' (no prompt, real TTY)
|
|
791
818
|
places itself like --device auto — a fleet worker runs it, with the TUI
|
|
792
819
|
forwarded over SSH. Headless runs (any prompt, --json, no TTY) are
|
|
793
|
-
unchanged: they run in place. To stay on this machine, pass
|
|
794
|
-
--device <this machine
|
|
795
|
-
'<harness>@' device picker. When placement finds no
|
|
796
|
-
run fails loud and names the local
|
|
820
|
+
unchanged: they run in place. To stay on this machine, pass --local
|
|
821
|
+
(--device <this machine> means the same), or pick this machine (listed
|
|
822
|
+
first) in the '<harness>@' device picker. When placement finds no
|
|
823
|
+
healthy device the run fails loud and names the local spelling.
|
|
797
824
|
|
|
798
825
|
Fallback: --fallback codex,antigravity retries on rate-limit failure via /continue handoff. Each entry accepts @version.
|
|
799
826
|
|
|
@@ -891,6 +918,11 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
891
918
|
const { placementFromRunFlags, expandPlacementToRunFlags, PlacementError } = await import('../lib/placement.js');
|
|
892
919
|
try {
|
|
893
920
|
const placement = placementFromRunFlags(options);
|
|
921
|
+
// An explicit local placement (--local, --where local) must outlive
|
|
922
|
+
// this block: expansion yields no host flag, and the bare-interactive
|
|
923
|
+
// default below reads an empty host set as "decide for me".
|
|
924
|
+
if (placement.kind === 'local' && placement.source !== 'default')
|
|
925
|
+
options.local = true;
|
|
894
926
|
if (options.where) {
|
|
895
927
|
const expanded = expandPlacementToRunFlags(placement);
|
|
896
928
|
if (expanded.host !== undefined)
|
|
@@ -918,6 +950,16 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
918
950
|
throw err;
|
|
919
951
|
}
|
|
920
952
|
}
|
|
953
|
+
// --device naming this machine (short id, MagicDNS name, loopback, or the
|
|
954
|
+
// `interactive` sentinel pinned here) is a local run, not a self-SSH: the
|
|
955
|
+
// hop probes its own login shell and times out under load. Pinned here,
|
|
956
|
+
// ahead of every placement gate, so the bare-interactive default sees an
|
|
957
|
+
// explicit local choice rather than an empty host set.
|
|
958
|
+
{
|
|
959
|
+
const { isSelfHost } = await import('../lib/devices/self-host.js');
|
|
960
|
+
const { isDeviceInteractive, resolveInteractiveDevice } = await import('../lib/devices/interactive-host.js');
|
|
961
|
+
pinLocalWhenTargetIsSelf(options, (name) => isSelfHost(isDeviceInteractive(name) ? resolveInteractiveDevice() ?? name : name));
|
|
962
|
+
}
|
|
921
963
|
// Cloud refinement flags without the placement are a typo, not a no-op.
|
|
922
964
|
if (!options.cloud) {
|
|
923
965
|
const { cloudFlagsWithoutCloud } = await import('./run-cloud.js');
|
|
@@ -1101,13 +1143,13 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1101
1143
|
const { sessionRecoveryDestinationMatches, sessionRecoveryPeer, } = await import('../lib/session/recovery.js');
|
|
1102
1144
|
const sourceMachine = resolvedResumeSource.machine;
|
|
1103
1145
|
const sourcePeer = sessionRecoveryPeer(resolvedResumeSource);
|
|
1104
|
-
const explicitPlacement = hostTargetGiven(options).length > 0;
|
|
1146
|
+
const explicitPlacement = hostTargetGiven(options).length > 0 || options.local === true;
|
|
1105
1147
|
if (sourcePeer && !explicitPlacement) {
|
|
1106
1148
|
options.host = sourcePeer;
|
|
1107
1149
|
}
|
|
1108
1150
|
else if (sourcePeer && explicitPlacement && !hostTargetGiven(options).some((host) => sessionRecoveryDestinationMatches(resolvedResumeSource, host))) {
|
|
1109
1151
|
console.error(chalk.red(`Session ${resolvedResumeSource.shortId} must recover on ${sourcePeer}, where its conversation state is stored; ` +
|
|
1110
|
-
`the requested device was ${hostTargetGiven(options).join(', ')}.`));
|
|
1152
|
+
`the requested device was ${hostTargetGiven(options).join(', ') || 'this machine'}.`));
|
|
1111
1153
|
process.exit(1);
|
|
1112
1154
|
}
|
|
1113
1155
|
// Recovery is resolved on the device that owns the transcript. A remote
|
|
@@ -1246,8 +1288,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1246
1288
|
if (!defaultPlacement)
|
|
1247
1289
|
throw err;
|
|
1248
1290
|
console.error(chalk.red(err.message));
|
|
1249
|
-
|
|
1250
|
-
console.error(chalk.gray(`Run here instead: agents run ${runBaseAgentName} --device ${machineId()}`));
|
|
1291
|
+
console.error(chalk.gray(`Run here instead: agents run ${runBaseAgentName} --local`));
|
|
1251
1292
|
process.exit(1);
|
|
1252
1293
|
}
|
|
1253
1294
|
if (!options.quiet && result.banner) {
|
|
@@ -2641,6 +2682,37 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2641
2682
|
// the only `continue` is the preflight exhaustion handoff below, which
|
|
2642
2683
|
// consumes one entry of the `--fallback` spec before retrying as that
|
|
2643
2684
|
// alternate. The spec is finite, so this terminates (PHNX-3999 F19).
|
|
2685
|
+
// A balanced pick or the no-fresh-usage picker returns a CANDIDATE. Since
|
|
2686
|
+
// PHNX-3940 a native candidate is a registered account SLOT, not the
|
|
2687
|
+
// version home the executable lives in, so the spawn must resolve that
|
|
2688
|
+
// slot (home + durable env) through the canonical local launch resolver.
|
|
2689
|
+
// Without this the run launched the version home and whichever login it
|
|
2690
|
+
// held: on yosemite-m0 (2026-09-23) the picker chose trp and Claude
|
|
2691
|
+
// started as a different account in its first-run wizard. A remote run
|
|
2692
|
+
// re-resolves on the peer, which runs this same code locally.
|
|
2693
|
+
const applyPickedCandidate = async (candidate) => {
|
|
2694
|
+
if (!candidate.nativeAccount || options.host || options.device)
|
|
2695
|
+
return;
|
|
2696
|
+
const { resolveLocalAccountLaunch } = await import('../lib/accounting/account-launch.js');
|
|
2697
|
+
try {
|
|
2698
|
+
launchAccount = await resolveLocalAccountLaunch({
|
|
2699
|
+
agent,
|
|
2700
|
+
executableVersion: version ?? candidate.version,
|
|
2701
|
+
candidate,
|
|
2702
|
+
useDefault: false,
|
|
2703
|
+
});
|
|
2704
|
+
}
|
|
2705
|
+
catch (err) {
|
|
2706
|
+
console.error(chalk.red(err.message));
|
|
2707
|
+
process.exit(1);
|
|
2708
|
+
}
|
|
2709
|
+
if (launchAccount.execHome)
|
|
2710
|
+
execHome = launchAccount.execHome;
|
|
2711
|
+
if (launchAccount.configVersion)
|
|
2712
|
+
accountConfigVersion = launchAccount.configVersion;
|
|
2713
|
+
if (Object.keys(launchAccount.env).length > 0)
|
|
2714
|
+
accountEnv = { ...accountEnv, ...launchAccount.env };
|
|
2715
|
+
};
|
|
2644
2716
|
preflight: for (;;) {
|
|
2645
2717
|
if (!accountPickerRequested && !configuredAccount && (!version || strategy !== 'pinned' || options.balanced || explicitStrategy)) {
|
|
2646
2718
|
if (version) {
|
|
@@ -2778,6 +2850,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2778
2850
|
// Keep the rotation so mid-run failover can still cascade across
|
|
2779
2851
|
// the other (stale) healthy accounts after a real rejection.
|
|
2780
2852
|
rotationResult = resolved.rotation;
|
|
2853
|
+
await applyPickedCandidate(selected);
|
|
2781
2854
|
if (!options.quiet) {
|
|
2782
2855
|
const identity = selected.accountLabel || 'signed-in account';
|
|
2783
2856
|
process.stderr.write(chalk.gray(`[agents] no fresh usage for any ${agent} account — you picked ${identity} · ${agent}@${selected.version}\n`));
|
|
@@ -2796,12 +2869,13 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2796
2869
|
if (resolved.rotation) {
|
|
2797
2870
|
launchSignedIn = resolved.rotation.picked.signedIn;
|
|
2798
2871
|
launchEmail = resolved.rotation.picked.email;
|
|
2872
|
+
await applyPickedCandidate(resolved.rotation.picked);
|
|
2799
2873
|
}
|
|
2800
2874
|
// A balanced/available pick of a PROVIDER account (setup-token /
|
|
2801
2875
|
// API-key) carries `providerAccount`. Resolve its env through the
|
|
2802
2876
|
// same `resolveSpawnAccount` path an explicit `--account` uses, so
|
|
2803
2877
|
// exec injects the credential; a native pick has no providerAccount
|
|
2804
|
-
// and
|
|
2878
|
+
// and resolved its slot home in applyPickedCandidate above.
|
|
2805
2879
|
const pickedProviderAccount = resolved.rotation?.picked.providerAccount;
|
|
2806
2880
|
if (pickedProviderAccount) {
|
|
2807
2881
|
try {
|
|
@@ -3521,6 +3595,24 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
3521
3595
|
// Governance chokepoint (#347): every dispatched run finalizes here.
|
|
3522
3596
|
// ONE tamper-evident audit record per run — non-fatal by contract.
|
|
3523
3597
|
recordDispatchedRun({ agent: ranAgent, version: ranVersion ?? 'unknown', mode, cwd, exitCode });
|
|
3598
|
+
// A clean headless exit is real auth evidence — the account authenticated
|
|
3599
|
+
// and produced a result. Record it as the per-account FACT `agents view`
|
|
3600
|
+
// renders (`last used ok`), keyed to the slot the run used so it lands on
|
|
3601
|
+
// the right row and clears any stale failure fact (PHNX-4116). A non-zero
|
|
3602
|
+
// exit is NOT recorded as an auth failure: the exit code alone does not
|
|
3603
|
+
// prove the token was rejected (a task can fail for a hundred reasons), and
|
|
3604
|
+
// a lying "auth failure" fact is worse than none. Real auth failures are
|
|
3605
|
+
// recorded where they are actually detected (the routine runner's
|
|
3606
|
+
// isAuthFailureFromLog sites) and by the daemon probe.
|
|
3607
|
+
if (exitCode === 0) {
|
|
3608
|
+
recordRunAuthOutcome({
|
|
3609
|
+
agent: ranAgent,
|
|
3610
|
+
accountId: execOptions.accountId ?? null,
|
|
3611
|
+
version: ranVersion ?? null,
|
|
3612
|
+
home: execHome ?? null,
|
|
3613
|
+
outcome: { ok: true },
|
|
3614
|
+
});
|
|
3615
|
+
}
|
|
3524
3616
|
// First-successful-run star nudge (one-time, non-nagging). Only on a
|
|
3525
3617
|
// clean run, and never when output is machine-readable/quiet.
|
|
3526
3618
|
if (exitCode === 0)
|
package/dist/commands/repo.js
CHANGED
|
@@ -55,34 +55,13 @@ function syncMarketplacesForDefaults() {
|
|
|
55
55
|
catch { /* best-effort */ }
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const { syncReservedAuthBundle } = await import('../lib/secrets-policy.js');
|
|
66
|
-
const auth = await syncReservedAuthBundle();
|
|
67
|
-
if (auth.pushed.length > 0)
|
|
68
|
-
console.log(chalk.gray(`Auth bundle: pushed to ${auth.pushed.join(', ')}`));
|
|
69
|
-
for (const err of auth.errors)
|
|
70
|
-
console.error(chalk.yellow(`Auth bundle: ${err.device}: ${err.message}`));
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
const { publishReservedAuthVerdict } = await import('../lib/secrets-policy.js');
|
|
74
|
-
const auth = await publishReservedAuthVerdict();
|
|
75
|
-
if (auth.error)
|
|
76
|
-
console.error(chalk.yellow(`Auth verdict: ${auth.device}: ${auth.error}`));
|
|
77
|
-
}
|
|
78
|
-
/** Consume freshly pulled usage/auth metadata from the local user-repo checkout. */
|
|
79
|
-
async function consumeUserRepoAccountState() {
|
|
80
|
-
const { consumeUsageSnapshotsFromSharedStore } = await import('../lib/accounting/usage-sync.js');
|
|
81
|
-
const usage = consumeUsageSnapshotsFromSharedStore();
|
|
82
|
-
if (usage.merged > 0)
|
|
83
|
-
console.log(chalk.gray(`Usage snapshot: merged ${usage.merged} row(s) from ${usage.sources.join(', ')}`));
|
|
84
|
-
for (const err of usage.errors)
|
|
85
|
-
console.error(chalk.yellow(`Usage snapshot: ${err.device}: ${err.message}`));
|
|
58
|
+
/**
|
|
59
|
+
* Push the reserved auth bundle to peers whose last-received verdict says
|
|
60
|
+
* `missing`. Daemon state (usage, verdicts, session digests) no longer rides the
|
|
61
|
+
* user repo — it moves over SSH on the usage-sync tick (PHNX-4116) — so a repo
|
|
62
|
+
* push/sync of human-authored resources only runs this credential half.
|
|
63
|
+
*/
|
|
64
|
+
async function syncUserRepoAuthBundle() {
|
|
86
65
|
const { syncReservedAuthBundle } = await import('../lib/secrets-policy.js');
|
|
87
66
|
const auth = await syncReservedAuthBundle();
|
|
88
67
|
if (auth.pushed.length > 0)
|
|
@@ -1085,7 +1064,7 @@ export function registerRepoCommands(program) {
|
|
|
1085
1064
|
].filter(Boolean);
|
|
1086
1065
|
if (parts.length > 0)
|
|
1087
1066
|
console.log(chalk.gray(`Device policy: ${parts.join(' · ')}`));
|
|
1088
|
-
await
|
|
1067
|
+
await syncUserRepoAuthBundle();
|
|
1089
1068
|
}
|
|
1090
1069
|
});
|
|
1091
1070
|
repoCmd
|
|
@@ -1121,10 +1100,8 @@ export function registerRepoCommands(program) {
|
|
|
1121
1100
|
return;
|
|
1122
1101
|
}
|
|
1123
1102
|
for (const t of pushable) {
|
|
1124
|
-
// Account snapshots must exist BEFORE commitAndPush or this push ships
|
|
1125
|
-
// the previous tick's state and delays propagation by one repo cycle.
|
|
1126
1103
|
if (t.alias === 'user')
|
|
1127
|
-
await
|
|
1104
|
+
await syncUserRepoAuthBundle();
|
|
1128
1105
|
const spinner = interruptibleSpinner(`Pushing ${formatRepoTarget(t.alias, t.dir)}...`).start();
|
|
1129
1106
|
const result = await commitAndPush(t.dir, options.message);
|
|
1130
1107
|
if (result.success) {
|
|
@@ -1193,23 +1170,10 @@ Examples:
|
|
|
1193
1170
|
}
|
|
1194
1171
|
}
|
|
1195
1172
|
const push = t.alias !== 'system';
|
|
1196
|
-
//
|
|
1197
|
-
//
|
|
1198
|
-
if (t.alias === 'user')
|
|
1199
|
-
await publishUserRepoAccountState(false);
|
|
1173
|
+
// A plain pull/rebase (+ push) of human-authored resources for every
|
|
1174
|
+
// alias, the user repo included: daemon state left git (PHNX-4116).
|
|
1200
1175
|
const spinner = interruptibleSpinner(`Syncing ${formatRepoTarget(t.alias, t.dir)}...`).start();
|
|
1201
|
-
const result = t.
|
|
1202
|
-
? await (async () => {
|
|
1203
|
-
const { syncFleetSharedStateRepo } = await import('../lib/fleet-shared-repo-sync.js');
|
|
1204
|
-
const synced = await syncFleetSharedStateRepo({ userAgentsDir: t.dir });
|
|
1205
|
-
return {
|
|
1206
|
-
success: synced.success,
|
|
1207
|
-
commit: synced.commit ?? '',
|
|
1208
|
-
pushed: synced.pushed,
|
|
1209
|
-
error: synced.error ?? synced.skipped ?? undefined,
|
|
1210
|
-
};
|
|
1211
|
-
})()
|
|
1212
|
-
: await syncRepoGit(t.dir, { push });
|
|
1176
|
+
const result = await syncRepoGit(t.dir, { push });
|
|
1213
1177
|
if (result.success) {
|
|
1214
1178
|
const pushed = result.pushed ? ' (pushed)' : '';
|
|
1215
1179
|
spinner.succeed(`${formatRepoTarget(t.alias, t.dir)}: ${result.commit}${pushed}`);
|
|
@@ -1219,7 +1183,7 @@ Examples:
|
|
|
1219
1183
|
recordUserRepoRemote(t.dir, u);
|
|
1220
1184
|
const { reconcileDeviceDiscoveryPolicies } = await import('../lib/devices/discovery-policy.js');
|
|
1221
1185
|
await reconcileDeviceDiscoveryPolicies();
|
|
1222
|
-
await
|
|
1186
|
+
await syncUserRepoAuthBundle();
|
|
1223
1187
|
}
|
|
1224
1188
|
}
|
|
1225
1189
|
else {
|
package/dist/commands/route.js
CHANGED
|
@@ -95,13 +95,13 @@ export function registerRouteCommands(program) {
|
|
|
95
95
|
setHelpSections(routeCmd, {
|
|
96
96
|
examples: `
|
|
97
97
|
# Create a router scoped to two harnesses, capped at a tier
|
|
98
|
-
agents route add research --harness
|
|
98
|
+
agents route add research --harness grok,kimi --tier cheap,default
|
|
99
99
|
|
|
100
100
|
# Narrow one harness's model set
|
|
101
101
|
agents route allow research kimi kimi-k2
|
|
102
102
|
|
|
103
103
|
# Link accounts so routing under this router only ever picks them
|
|
104
|
-
agents route link-account research
|
|
104
|
+
agents route link-account research grok personal
|
|
105
105
|
agents route link-account research kimi work
|
|
106
106
|
|
|
107
107
|
# Inspect, rename, remove it
|
|
@@ -126,7 +126,7 @@ export function registerRouteCommands(program) {
|
|
|
126
126
|
.command('add <name>')
|
|
127
127
|
.alias('create')
|
|
128
128
|
.description('Create a named router with an initial harness + tier allowlist.')
|
|
129
|
-
.option('--harness <list>', 'Comma-separated harness ids to allow, e.g.
|
|
129
|
+
.option('--harness <list>', 'Comma-separated harness ids to allow, e.g. grok,kimi')
|
|
130
130
|
.option('--tier <list>', 'Comma-separated tier tokens applied to every listed harness, e.g. cheap,default')
|
|
131
131
|
.option('--task <type>', 'Free-text task type this router serves, e.g. research')
|
|
132
132
|
.action((name, opts) => {
|
|
@@ -134,7 +134,7 @@ export function registerRouteCommands(program) {
|
|
|
134
134
|
die(`Router '${name}' already exists. Use 'agents route allow ${name} <harness> <model|tier>...' to edit it.`);
|
|
135
135
|
}
|
|
136
136
|
if (!opts.harness) {
|
|
137
|
-
die("Missing --harness. Example: agents route add research --harness
|
|
137
|
+
die("Missing --harness. Example: agents route add research --harness grok,kimi --tier cheap,default");
|
|
138
138
|
}
|
|
139
139
|
const harnesses = opts.harness.split(',').map((h) => h.trim()).filter(Boolean);
|
|
140
140
|
const tiers = (opts.tier ?? '').split(',').map((t) => t.trim()).filter(Boolean);
|
|
@@ -170,7 +170,7 @@ export function registerRouteCommands(program) {
|
|
|
170
170
|
}
|
|
171
171
|
if (routers.length === 0) {
|
|
172
172
|
console.log(chalk.gray('No routers configured.'));
|
|
173
|
-
console.log(chalk.gray('Try: agents route add research --harness
|
|
173
|
+
console.log(chalk.gray('Try: agents route add research --harness grok,kimi --tier cheap,default'));
|
|
174
174
|
return;
|
|
175
175
|
}
|
|
176
176
|
for (const router of routers)
|
|
@@ -246,7 +246,7 @@ function renderRoutineRows({ jobs, scheduler, overdueSet, link, now, local = tru
|
|
|
246
246
|
: lastStatus === 'missed' ? chalk.magenta
|
|
247
247
|
: chalk.gray;
|
|
248
248
|
const overdueTag = overdueSet.has(job.name) ? chalk.yellow(' (overdue)') : '';
|
|
249
|
-
// Append the concrete reason a routine did not complete (auth_failed,
|
|
249
|
+
// Append the concrete reason a routine did not complete (auth_failed,
|
|
250
250
|
// blocked, missed) right in the Last Status cell, so the list answers "why"
|
|
251
251
|
// without a drill-in. Only for non-completed local runs; peer rows stay blank.
|
|
252
252
|
const reason = latestRun ? runFailureReason(latestRun) : null;
|
|
@@ -650,12 +650,29 @@ function routineMatchesQuery(job, q) {
|
|
|
650
650
|
.toLowerCase()
|
|
651
651
|
.includes(q);
|
|
652
652
|
}
|
|
653
|
-
/**
|
|
653
|
+
/**
|
|
654
|
+
* Friendly one-liners for the claim a `skipped` run lost — the reasons whose
|
|
655
|
+
* message is static. `active_run` is dynamic (it names the live run that owns
|
|
656
|
+
* the slot and when it started), built in {@link runFailureReason}: since
|
|
657
|
+
* PHNX-4116 there is no "wedged" daemon state, so an overlapping routine reads
|
|
658
|
+
* as `blocked` on the live run, not "wedged".
|
|
659
|
+
*/
|
|
654
660
|
const SKIP_REASON_LABEL = {
|
|
655
|
-
active_run: 'wedged: a prior run is still active',
|
|
656
661
|
duplicate_slot: 'duplicate slot (already fired)',
|
|
657
662
|
wrong_owner: 'pinned to another device',
|
|
658
663
|
};
|
|
664
|
+
/**
|
|
665
|
+
* A run id is an ISO timestamp with `:`/`.` replaced by `-` (`slotRunId` /
|
|
666
|
+
* `generateRunId`), so it reverses losslessly to the run's start time. Returns
|
|
667
|
+
* the ISO string, or null when the id is not that shape (a hand-set id).
|
|
668
|
+
*/
|
|
669
|
+
function runIdToStartIso(runId) {
|
|
670
|
+
const m = /^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/.exec(runId);
|
|
671
|
+
if (!m)
|
|
672
|
+
return null;
|
|
673
|
+
const iso = `${m[1]}T${m[2]}:${m[3]}:${m[4]}.${m[5]}Z`;
|
|
674
|
+
return Number.isFinite(Date.parse(iso)) ? iso : null;
|
|
675
|
+
}
|
|
659
676
|
/**
|
|
660
677
|
* The short, human reason a run did not simply complete — for inline display in
|
|
661
678
|
* the list/detail so "why did it fail" needs no dig into the run dir. Prefers the
|
|
@@ -670,6 +687,21 @@ export function runFailureReason(run) {
|
|
|
670
687
|
const one = s.replace(/\s+/g, ' ').trim();
|
|
671
688
|
return one.length > 80 ? one.slice(0, 79) + '…' : one;
|
|
672
689
|
};
|
|
690
|
+
// An overlap skip names the live run it lost to (PHNX-4116): `blocked: run
|
|
691
|
+
// <id> active since <t>`, not the removed "wedged" wording. The structured
|
|
692
|
+
// `activeRunId` is preferred; a legacy record without it falls back to the
|
|
693
|
+
// errorMessage (which embeds the same id) so nothing regresses to vaguer text.
|
|
694
|
+
if (run.skipReason === 'active_run') {
|
|
695
|
+
if (run.activeRunId) {
|
|
696
|
+
const since = runIdToStartIso(run.activeRunId);
|
|
697
|
+
return since
|
|
698
|
+
? `blocked: run ${run.activeRunId} active since ${since}`
|
|
699
|
+
: `blocked: run ${run.activeRunId} still active`;
|
|
700
|
+
}
|
|
701
|
+
if (run.errorMessage)
|
|
702
|
+
return compact(run.errorMessage);
|
|
703
|
+
return 'blocked: a prior run is still active';
|
|
704
|
+
}
|
|
673
705
|
if (run.errorMessage)
|
|
674
706
|
return compact(run.errorMessage);
|
|
675
707
|
if (run.status === 'blocked' && run.readiness) {
|
|
@@ -751,8 +783,8 @@ function buildRoutineDetail(job, scheduler, now) {
|
|
|
751
783
|
: chalk.yellow(run.status);
|
|
752
784
|
const dur = run.completedAt ? ` ${formatRunDuration(run.startedAt, run.completedAt)}` : '';
|
|
753
785
|
// Surface WHY a run did not complete, inline, so "looking at status" does not
|
|
754
|
-
// require digging into the run dir. auth_failed / OAuth-revoked,
|
|
755
|
-
// active-run skip, or a readiness block all live on the RunMeta already.
|
|
786
|
+
// require digging into the run dir. auth_failed / OAuth-revoked, an
|
|
787
|
+
// active-run overlap skip, or a readiness block all live on the RunMeta already.
|
|
756
788
|
const reason = runFailureReason(run);
|
|
757
789
|
const why = reason ? chalk.gray(` — ${reason}`) : '';
|
|
758
790
|
lines.push(` ${run.startedAt} ${status}${dur}${why}`);
|
|
@@ -1941,7 +1973,7 @@ export function registerRoutinesCommands(program) {
|
|
|
1941
1973
|
chalk.gray(` ${run.startedAt}`) +
|
|
1942
1974
|
chalk.gray(formatRunDuration(run.startedAt, run.completedAt)) +
|
|
1943
1975
|
(run.exitCode !== null && run.exitCode !== undefined ? chalk.gray(` exit ${run.exitCode}`) : ''));
|
|
1944
|
-
// The structured reason (auth_failed, blocked readiness,
|
|
1976
|
+
// The structured reason (auth_failed, blocked readiness, overlap skip) — the
|
|
1945
1977
|
// report/stdout tail below often buries or omits it, so name it up front.
|
|
1946
1978
|
const logsReason = runFailureReason(run);
|
|
1947
1979
|
if (logsReason)
|
|
@@ -2300,9 +2332,7 @@ export function registerRoutinesCommands(program) {
|
|
|
2300
2332
|
console.log(chalk.bold('Scheduler\n'));
|
|
2301
2333
|
const stateLabel = schedulerState === 'running'
|
|
2302
2334
|
? chalk.green('running')
|
|
2303
|
-
:
|
|
2304
|
-
? chalk.red('wedged')
|
|
2305
|
-
: chalk.gray('stopped');
|
|
2335
|
+
: chalk.gray('stopped');
|
|
2306
2336
|
console.log(` Status: ${stateLabel}`);
|
|
2307
2337
|
console.log(` Daemon: ${status.running ? chalk.green('running') : chalk.gray('stopped')}`);
|
|
2308
2338
|
if (status.pid)
|
|
@@ -2316,9 +2346,6 @@ export function registerRoutinesCommands(program) {
|
|
|
2316
2346
|
const jobs = listAllJobs();
|
|
2317
2347
|
const enabled = jobs.filter((j) => j.enabled);
|
|
2318
2348
|
console.log(` Routines: ${enabled.length} enabled / ${jobs.length} total`);
|
|
2319
|
-
if (status.state === 'wedged') {
|
|
2320
|
-
console.log(chalk.red('\n The shared daemon is wedged (heartbeat stale). Restart deliberately with: agents daemon restart'));
|
|
2321
|
-
}
|
|
2322
2349
|
if (schedulerState === 'running' && enabled.length > 0) {
|
|
2323
2350
|
const scheduler = new JobScheduler(async () => { });
|
|
2324
2351
|
scheduler.loadAll();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import type { SessionMeta } from '../lib/session/types.js';
|
|
3
|
-
export declare const MARKDOWN_RENDER_AGENTS: readonly ["claude", "codex", "
|
|
3
|
+
export declare const MARKDOWN_RENDER_AGENTS: readonly ["claude", "codex", "antigravity", "opencode", "grok", "rush", "hermes", "kimi", "droid", "cursor"];
|
|
4
4
|
export type ReasoningMode = 'omit' | 'fold' | 'include';
|
|
5
5
|
/** Build one shareable Markdown document from the canonical preview and event model. */
|
|
6
6
|
export declare function renderSessionMarkdownDocument(session: SessionMeta, options?: {
|
|
@@ -15,7 +15,6 @@ export declare const tsxLoaderUrl: string;
|
|
|
15
15
|
export declare function writeUpdateCache(tempHome: string): void;
|
|
16
16
|
export declare function writeClaudeSession(tempHome: string, projectKey: string, sessionId: string, cwd: string, content: string, timestamp: string): void;
|
|
17
17
|
export declare function writeCodexSession(tempHome: string, sessionId: string, cwd: string, prompt: string, timestamp: string): void;
|
|
18
|
-
export declare function writeGeminiSession(tempHome: string, sessionId: string, cwd: string, prompt: string, timestamp: string, version?: string): void;
|
|
19
18
|
export declare function writeOpenClawSetup(tempHome: string, version?: string): string;
|
|
20
19
|
export declare function runAgents(args: string[], cwd: string, home: string, envOverrides?: Record<string, string>): import("node:child_process").SpawnSyncReturns<string>;
|
|
21
20
|
export declare function outputOf(result: {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { describe } from 'vitest';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import * as crypto from 'crypto';
|
|
5
4
|
import { spawnSync } from 'child_process';
|
|
6
5
|
import { createRequire } from 'node:module';
|
|
7
6
|
import { pathToFileURL } from 'node:url';
|
|
@@ -121,42 +120,6 @@ export function writeCodexSession(tempHome, sessionId, cwd, prompt, timestamp) {
|
|
|
121
120
|
];
|
|
122
121
|
fs.writeFileSync(filePath, lines.join('\n') + '\n', 'utf-8');
|
|
123
122
|
}
|
|
124
|
-
export function writeGeminiSession(tempHome, sessionId, cwd, prompt, timestamp, version = '0.29.5') {
|
|
125
|
-
const versionHome = path.join(tempHome, '.agents', '.history', 'versions', 'gemini', version, 'home');
|
|
126
|
-
const geminiHome = path.join(versionHome, '.gemini');
|
|
127
|
-
const projectHash = crypto.createHash('sha256').update(cwd).digest('hex');
|
|
128
|
-
const chatsDir = path.join(geminiHome, 'tmp', projectHash, 'chats');
|
|
129
|
-
fs.mkdirSync(cwd, { recursive: true });
|
|
130
|
-
fs.mkdirSync(chatsDir, { recursive: true });
|
|
131
|
-
fs.mkdirSync(path.dirname(path.join(tempHome, '.gemini')), { recursive: true });
|
|
132
|
-
const activeGeminiHome = path.join(tempHome, '.gemini');
|
|
133
|
-
if (!fs.existsSync(activeGeminiHome)) {
|
|
134
|
-
fs.symlinkSync(geminiHome, activeGeminiHome);
|
|
135
|
-
}
|
|
136
|
-
fs.writeFileSync(path.join(geminiHome, 'projects.json'), JSON.stringify({ projects: [cwd] }), 'utf-8');
|
|
137
|
-
fs.writeFileSync(path.join(chatsDir, `session-${timestamp.replace(/[:.]/g, '-')}-${sessionId.slice(0, 8)}.json`), JSON.stringify({
|
|
138
|
-
sessionId,
|
|
139
|
-
projectHash,
|
|
140
|
-
startTime: timestamp,
|
|
141
|
-
lastUpdated: timestamp,
|
|
142
|
-
messages: [
|
|
143
|
-
{
|
|
144
|
-
id: `${sessionId}-user`,
|
|
145
|
-
timestamp,
|
|
146
|
-
type: 'user',
|
|
147
|
-
content: [{ text: prompt }],
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
id: `${sessionId}-assistant`,
|
|
151
|
-
timestamp,
|
|
152
|
-
type: 'gemini',
|
|
153
|
-
content: 'Investigating now.',
|
|
154
|
-
model: 'gemini-3-flash-preview',
|
|
155
|
-
tokens: { total: 1234 },
|
|
156
|
-
},
|
|
157
|
-
],
|
|
158
|
-
}, null, 2), 'utf-8');
|
|
159
|
-
}
|
|
160
123
|
export function writeOpenClawSetup(tempHome, version = '2026.3.8') {
|
|
161
124
|
const managedHome = path.join(tempHome, '.agents', '.history', 'versions', 'openclaw', version, 'home', '.openclaw');
|
|
162
125
|
fs.mkdirSync(managedHome, { recursive: true });
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `agents setup browser` —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* `agents setup browser` — get `agents browser` working on a fresh machine:
|
|
3
|
+
* install the standalone `browser` CLI (@phnx-labs/browser-cli), let it detect
|
|
4
|
+
* installed browsers and create machine-local profiles (`browser profiles seed`),
|
|
5
|
+
* then pick this machine's default (`browser use`).
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* The engine owns profile declarations and browser detection now (PHNX-4101), so
|
|
8
|
+
* this wizard DELEGATES to it rather than crafting profiles itself. What stays
|
|
9
|
+
* agents-cli's is the onboarding flow and the readiness check over the shared
|
|
10
|
+
* `browser.profile` config key both CLIs read/write.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent: `profiles seed` leaves existing profiles untouched, and `use`
|
|
13
|
+
* re-points the device default.
|
|
9
14
|
*/
|
|
10
15
|
import type { Command } from 'commander';
|
|
11
16
|
/**
|
|
12
|
-
* Interactive browser setup. Returns true if a
|
|
13
|
-
* afterwards, false if none
|
|
14
|
-
*
|
|
15
|
-
* silently minting a logged-out
|
|
16
|
-
*
|
|
17
|
-
*
|
|
17
|
+
* Interactive browser setup. Returns true if a default profile is configured
|
|
18
|
+
* afterwards, false if none is / the user backed out. Non-interactively it only
|
|
19
|
+
* RECOGNIZES an existing default — it never creates one (PHNX-3296), since
|
|
20
|
+
* silently minting a logged-out profile on a headless box is the exact bug that
|
|
21
|
+
* removed; a headless box gets its browser from the fleet hub. Never throws on
|
|
22
|
+
* cancel — the `agents setup` hub relies on that.
|
|
18
23
|
*/
|
|
19
24
|
export declare function runBrowserWizard(): Promise<boolean>;
|
|
20
25
|
/** Register `agents setup browser` under the parent `setup` command. */
|