@phnx-labs/agents-cli 1.22.117 → 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 +235 -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.js +53 -1
- 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.js +9 -9
- 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/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/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 +5 -10
- 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
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* `agents daemon` — runtime, hosted services, and failure visibility for the
|
|
3
3
|
* always-on daemon (RUSH-2354).
|
|
4
4
|
*
|
|
5
|
-
* The daemon holds the routines scheduler, the
|
|
5
|
+
* The daemon holds the routines scheduler, the session/usage sync, and the
|
|
6
6
|
* watchdog pass — but until this command group existed it had no user-facing
|
|
7
7
|
* surface: no way to see it, restart it, or turn it off. The secrets broker
|
|
8
8
|
* moved out of this daemon entirely with the standalone `secrets` engine
|
|
9
|
-
* (PHNX-3989 OWN-1)
|
|
9
|
+
* (PHNX-3989 OWN-1), and the browser IPC server with the standalone `browser`
|
|
10
|
+
* CLI (PHNX-4101); `agents daemon status` only probes the broker's reachability.
|
|
10
11
|
* `daemon.ts` (the runtime) has always implemented every mechanism this file
|
|
11
12
|
* wires up; nothing here is new machinery, only the missing CLI surface.
|
|
12
13
|
*
|
|
@@ -17,6 +18,42 @@
|
|
|
17
18
|
* duplicating it.
|
|
18
19
|
*/
|
|
19
20
|
import type { Command } from 'commander';
|
|
21
|
+
import { type SubsystemHealth } from '../lib/daemon-health.js';
|
|
20
22
|
/** Elapsed wall-clock seconds since `pid` started, or null if unavailable (best-effort, POSIX only). */
|
|
21
23
|
export declare function uptimeSeconds(pid: number): number | null;
|
|
24
|
+
interface SecretsBrokerHealth {
|
|
25
|
+
reachable: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* True when this box routes `keychain`-backend items to the standalone's
|
|
28
|
+
* encrypted file store (a headless worker with no reachable keyring). The
|
|
29
|
+
* broker exists only to hold an UNLOCKED keychain across reads without a
|
|
30
|
+
* Touch ID prompt — a file-backed box reads every secret one-shot with no
|
|
31
|
+
* broker at all, so an absent broker there is normal, not a fault (PHNX-4116).
|
|
32
|
+
*/
|
|
33
|
+
fileBacked: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Always `null` — the broker's socket now lives entirely inside the
|
|
36
|
+
* standalone `secrets` engine's own process (PHNX-3989 OWN-1); the daemon
|
|
37
|
+
* neither hosts it nor knows its transport details. Kept in the shape for
|
|
38
|
+
* `--json` compatibility.
|
|
39
|
+
*/
|
|
40
|
+
socketPath: string | null;
|
|
41
|
+
heldBundles: number | null;
|
|
42
|
+
/**
|
|
43
|
+
* Always `null` — no daemon service writes a health record for the broker
|
|
44
|
+
* anymore (the daemon does not host or supervise it). Kept in the shape for
|
|
45
|
+
* `--json` compatibility.
|
|
46
|
+
*/
|
|
47
|
+
record: SubsystemHealth | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The `secrets broker` health line. Reachable → healthy. Unreachable is a FAULT
|
|
51
|
+
* (`down`) only when the store is keychain-backed — there the broker is what
|
|
52
|
+
* holds the unlocked keychain across reads. On a file-backed box (every
|
|
53
|
+
* headless worker) no broker is needed: secrets read one-shot, so an absent
|
|
54
|
+
* broker is expected and reads as INFO, not `down` (PHNX-4116). Pure, so the
|
|
55
|
+
* three cases are unit-tested with no live daemon.
|
|
56
|
+
*/
|
|
57
|
+
export declare function secretsBrokerHealthLine(secrets: SecretsBrokerHealth): string;
|
|
22
58
|
export declare function registerDaemonCommand(program: Command): void;
|
|
59
|
+
export {};
|
package/dist/commands/daemon.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* `agents daemon` — runtime, hosted services, and failure visibility for the
|
|
3
3
|
* always-on daemon (RUSH-2354).
|
|
4
4
|
*
|
|
5
|
-
* The daemon holds the routines scheduler, the
|
|
5
|
+
* The daemon holds the routines scheduler, the session/usage sync, and the
|
|
6
6
|
* watchdog pass — but until this command group existed it had no user-facing
|
|
7
7
|
* surface: no way to see it, restart it, or turn it off. The secrets broker
|
|
8
8
|
* moved out of this daemon entirely with the standalone `secrets` engine
|
|
9
|
-
* (PHNX-3989 OWN-1)
|
|
9
|
+
* (PHNX-3989 OWN-1), and the browser IPC server with the standalone `browser`
|
|
10
|
+
* CLI (PHNX-4101); `agents daemon status` only probes the broker's reachability.
|
|
10
11
|
* `daemon.ts` (the runtime) has always implemented every mechanism this file
|
|
11
12
|
* wires up; nothing here is new machinery, only the missing CLI surface.
|
|
12
13
|
*
|
|
@@ -21,10 +22,10 @@ import { execFileSync } from 'child_process';
|
|
|
21
22
|
import * as fs from 'fs';
|
|
22
23
|
import * as path from 'path';
|
|
23
24
|
import { setHelpSections } from '../lib/help.js';
|
|
24
|
-
import { getDaemonStatus, isDaemonRunning,
|
|
25
|
+
import { getDaemonStatus, isDaemonRunning, readDaemonLog, startDaemon, stopDaemon, signalDaemonReload, findSurvivingStateDirDaemons, getDaemonLogPath, isDaemonAutostartCircuitOpen, RedirectedHomeDaemonError, } from '../lib/daemon/daemon.js';
|
|
25
26
|
import { listDaemonRunProcesses } from '../lib/daemon/leaked-daemons.js';
|
|
26
27
|
import { getConfigValue, setConfigValue, isDaemonEnabled } from '../lib/device-config.js';
|
|
27
|
-
import { readSubsystemHealth, readAllSubsystemHealth,
|
|
28
|
+
import { readSubsystemHealth, readAllSubsystemHealth, SUBSYSTEM_DAEMON_START, } from '../lib/daemon-health.js';
|
|
28
29
|
import { DAEMON_SERVICE_IDS, listDaemonServiceStates, setDaemonServiceEnabled, getDaemonServicesConfigPath, queueDaemonServiceRestart, } from '../lib/daemon-services.js';
|
|
29
30
|
import { listJobs, getLatestRun } from '../lib/scheduling/routines.js';
|
|
30
31
|
import { JobScheduler } from '../lib/scheduler.js';
|
|
@@ -293,25 +294,24 @@ function humanDuration(seconds) {
|
|
|
293
294
|
}
|
|
294
295
|
/** Reachability probe only — the daemon does not host, supervise, or take over the broker (OWN-1). */
|
|
295
296
|
async function probeSecretsBroker() {
|
|
297
|
+
const { agentPing, agentStatus, keychainUsesFileFallback } = await import('../lib/secrets-client.js');
|
|
296
298
|
try {
|
|
297
|
-
const { agentPing, agentStatus } = await import('../lib/secrets-client.js');
|
|
298
299
|
const ping = await agentPing();
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
300
|
+
if (ping.reachable) {
|
|
301
|
+
const entries = await agentStatus();
|
|
302
|
+
return { reachable: true, fileBacked: false, socketPath: null, heldBundles: entries.length, record: null };
|
|
303
|
+
}
|
|
303
304
|
}
|
|
304
|
-
catch {
|
|
305
|
-
|
|
305
|
+
catch { /* fall through to the unreachable classification below */ }
|
|
306
|
+
// Unreachable: distinguish "broker needed and absent" (a keychain-backed box)
|
|
307
|
+
// from "no broker needed" (a file-backed worker reads secrets one-shot). An
|
|
308
|
+
// unknown store defaults to broker-needed, the louder verdict.
|
|
309
|
+
let fileBacked = false;
|
|
310
|
+
try {
|
|
311
|
+
fileBacked = await keychainUsesFileFallback();
|
|
306
312
|
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const record = readSubsystemHealth(SUBSYSTEM_BROWSER_IPC);
|
|
310
|
-
const { isBrowserServiceReachable, getSocketPath } = await import('../lib/browser/ipc.js');
|
|
311
|
-
const { listAllProfileSnapshots } = await import('../lib/browser/runtime-state.js');
|
|
312
|
-
const bound = await isBrowserServiceReachable();
|
|
313
|
-
const sessionCount = listAllProfileSnapshots().filter((s) => s.pidAlive && s.daemonAlive).length;
|
|
314
|
-
return { bound, socketPath: getSocketPath(), sessionCount, record };
|
|
313
|
+
catch { /* unknown → not file-backed */ }
|
|
314
|
+
return { reachable: false, fileBacked, socketPath: null, heldBundles: null, record: null };
|
|
315
315
|
}
|
|
316
316
|
function schedulerSummary() {
|
|
317
317
|
const jobs = listJobs();
|
|
@@ -357,6 +357,23 @@ function healthLine(label, live, record) {
|
|
|
357
357
|
: '';
|
|
358
358
|
return ` ${chalk.red('down')} ${label} ${detail}`;
|
|
359
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* The `secrets broker` health line. Reachable → healthy. Unreachable is a FAULT
|
|
362
|
+
* (`down`) only when the store is keychain-backed — there the broker is what
|
|
363
|
+
* holds the unlocked keychain across reads. On a file-backed box (every
|
|
364
|
+
* headless worker) no broker is needed: secrets read one-shot, so an absent
|
|
365
|
+
* broker is expected and reads as INFO, not `down` (PHNX-4116). Pure, so the
|
|
366
|
+
* three cases are unit-tested with no live daemon.
|
|
367
|
+
*/
|
|
368
|
+
export function secretsBrokerHealthLine(secrets) {
|
|
369
|
+
if (secrets.reachable) {
|
|
370
|
+
return healthLine(`secrets broker (${secrets.socketPath}, ${secrets.heldBundles} bundle(s) held)`, true, secrets.record);
|
|
371
|
+
}
|
|
372
|
+
if (secrets.fileBacked) {
|
|
373
|
+
return ` ${chalk.cyan('info')} secrets agent not running (file-backed stores)`;
|
|
374
|
+
}
|
|
375
|
+
return healthLine('secrets broker (unreachable)', false, secrets.record);
|
|
376
|
+
}
|
|
360
377
|
async function runStatus(opts) {
|
|
361
378
|
const status = getDaemonStatus();
|
|
362
379
|
const enabled = isDaemonEnabled();
|
|
@@ -378,7 +395,7 @@ async function runStatus(opts) {
|
|
|
378
395
|
const staleTiers = staleDaemons(processes, pid ?? null, registeredPids);
|
|
379
396
|
const stale = staleTiers.visible;
|
|
380
397
|
const ownerEntryGone = owner ? entryIsGone(owner) : false;
|
|
381
|
-
const
|
|
398
|
+
const secrets = await probeSecretsBroker();
|
|
382
399
|
const scheduler = schedulerSummary();
|
|
383
400
|
if (opts.json) {
|
|
384
401
|
console.log(JSON.stringify({
|
|
@@ -386,6 +403,9 @@ async function runStatus(opts) {
|
|
|
386
403
|
pid,
|
|
387
404
|
uptimeSeconds: uptime,
|
|
388
405
|
heartbeatAgeMs,
|
|
406
|
+
restarts24h: status.restarts24h,
|
|
407
|
+
lastRestartCause: status.lastRestartCause,
|
|
408
|
+
lastRestartAt: status.lastRestartAt,
|
|
389
409
|
logPath: status.logPath,
|
|
390
410
|
binaryPath: owner?.entry ?? status.binaryPath,
|
|
391
411
|
binaryVersion: owner?.version ?? null,
|
|
@@ -409,12 +429,6 @@ async function runStatus(opts) {
|
|
|
409
429
|
heldBundles: secrets.heldBundles,
|
|
410
430
|
health: secrets.record,
|
|
411
431
|
},
|
|
412
|
-
browserIpc: {
|
|
413
|
-
bound: browserIpc.bound,
|
|
414
|
-
socketPath: browserIpc.socketPath,
|
|
415
|
-
sessionCount: browserIpc.sessionCount,
|
|
416
|
-
health: browserIpc.record,
|
|
417
|
-
},
|
|
418
432
|
},
|
|
419
433
|
scheduler: {
|
|
420
434
|
enabled: getConfigValue('scheduler.enabled').value !== false,
|
|
@@ -427,9 +441,8 @@ async function runStatus(opts) {
|
|
|
427
441
|
return;
|
|
428
442
|
}
|
|
429
443
|
const stateLabel = state === 'running' ? chalk.green('running')
|
|
430
|
-
: state === '
|
|
431
|
-
:
|
|
432
|
-
: chalk.gray('stopped');
|
|
444
|
+
: state === 'disabled' ? chalk.yellow('disabled')
|
|
445
|
+
: chalk.gray('stopped');
|
|
433
446
|
console.log(chalk.bold('Identity\n'));
|
|
434
447
|
console.log(` State: ${stateLabel}`);
|
|
435
448
|
if (pid)
|
|
@@ -438,6 +451,12 @@ async function runStatus(opts) {
|
|
|
438
451
|
console.log(` Uptime: ${humanDuration(uptime)}`);
|
|
439
452
|
if (heartbeatAgeMs !== null)
|
|
440
453
|
console.log(` Heartbeat: ${Math.round(heartbeatAgeMs / 1000)}s ago`);
|
|
454
|
+
if (status.restarts24h > 0) {
|
|
455
|
+
const when = status.lastRestartAt ? ` (last ${new Date(status.lastRestartAt).toLocaleString()})` : '';
|
|
456
|
+
console.log(` Restarts: ${status.restarts24h} in the last 24h${when}`);
|
|
457
|
+
if (status.lastRestartCause)
|
|
458
|
+
console.log(` Last cause: ${chalk.gray(status.lastRestartCause)}`);
|
|
459
|
+
}
|
|
441
460
|
const binaryLabel = owner?.entry ?? status.binaryPath ?? 'unknown';
|
|
442
461
|
console.log(` Binary: ${ownerEntryGone ? chalk.red(`${binaryLabel} (MISSING from disk)`) : chalk.gray(binaryLabel)}`);
|
|
443
462
|
console.log(` Version: ${chalk.gray(owner?.version ?? 'unknown')}`);
|
|
@@ -476,17 +495,13 @@ async function runStatus(opts) {
|
|
|
476
495
|
console.log(chalk.gray('\n Only one install should own the daemon. Stop the stray(s): kill <pid>'));
|
|
477
496
|
}
|
|
478
497
|
console.log(chalk.bold('\nHealth\n'));
|
|
479
|
-
console.log(
|
|
480
|
-
console.log(healthLine(`browser IPC ${browserIpc.bound ? `(${browserIpc.socketPath}, ${browserIpc.sessionCount} session(s))` : '(unbound)'}`, browserIpc.bound, browserIpc.record));
|
|
498
|
+
console.log(secretsBrokerHealthLine(secrets));
|
|
481
499
|
const schedulerEnabled = getConfigValue('scheduler.enabled').value !== false;
|
|
482
500
|
console.log(` ${schedulerEnabled ? chalk.green('enabled') : chalk.yellow('disabled')} scheduler — ${scheduler.enabledCount}/${scheduler.routineCount} routine(s) enabled` +
|
|
483
501
|
(scheduler.nextFire ? `, next ${scheduler.nextFire.toLocaleString()}` : ''));
|
|
484
502
|
if (scheduler.failingCount > 0) {
|
|
485
503
|
console.log(chalk.red(` ${scheduler.failingCount} routine(s) failing their last run — see: agents routines stats`));
|
|
486
504
|
}
|
|
487
|
-
if (state === 'wedged') {
|
|
488
|
-
console.log(chalk.red('\nThe daemon is wedged (heartbeat stale). Restart: agents daemon restart'));
|
|
489
|
-
}
|
|
490
505
|
}
|
|
491
506
|
/**
|
|
492
507
|
* Combine the persisted enable/disable toggles with whatever health the
|
|
@@ -503,8 +518,8 @@ function buildServiceRows(daemonRunning) {
|
|
|
503
518
|
// A persisted supervisor state (health.json) is only trustworthy while the
|
|
504
519
|
// daemon is actually up. If it is not running (crash / kill -9 / never
|
|
505
520
|
// started), every service is stopped no matter what the last-written record
|
|
506
|
-
// says — trusting a stale 'running'/'
|
|
507
|
-
//
|
|
521
|
+
// says — trusting a stale 'running'/'idle' here would print a report that
|
|
522
|
+
// contradicts the live-probed socket rows below (RUSH-2368).
|
|
508
523
|
const state = daemonRunning
|
|
509
524
|
? (h?.state ?? (s.enabled ? 'running (unsupervised)' : 'stopped'))
|
|
510
525
|
: 'stopped';
|
|
@@ -524,8 +539,6 @@ function buildServiceRows(daemonRunning) {
|
|
|
524
539
|
function serviceStateLabel(state) {
|
|
525
540
|
if (state === 'running')
|
|
526
541
|
return chalk.green('running');
|
|
527
|
-
if (state === 'parked')
|
|
528
|
-
return chalk.red('parked');
|
|
529
542
|
if (state === 'stopped')
|
|
530
543
|
return chalk.gray('stopped');
|
|
531
544
|
if (state === 'idle')
|
|
@@ -533,13 +546,12 @@ function serviceStateLabel(state) {
|
|
|
533
546
|
return chalk.yellow(state); // 'running (unsupervised)' or any future label
|
|
534
547
|
}
|
|
535
548
|
async function runServices(opts) {
|
|
536
|
-
const
|
|
549
|
+
const secrets = await probeSecretsBroker();
|
|
537
550
|
const rows = buildServiceRows(isDaemonRunning());
|
|
538
551
|
if (opts.json) {
|
|
539
552
|
console.log(JSON.stringify({
|
|
540
553
|
// Existing fields — unchanged shape, agents/CI consume these directly.
|
|
541
554
|
secretsBroker: { reachable: secrets.reachable, socketPath: secrets.socketPath, heldBundles: secrets.heldBundles, health: secrets.record },
|
|
542
|
-
browserIpc: { bound: browserIpc.bound, socketPath: browserIpc.socketPath, sessionCount: browserIpc.sessionCount, health: browserIpc.record },
|
|
543
555
|
// Additive: every registered service, supervised or legacy.
|
|
544
556
|
services: rows,
|
|
545
557
|
}, null, 2));
|
|
@@ -554,8 +566,7 @@ async function runServices(opts) {
|
|
|
554
566
|
console.log(chalk.gray(` last-error: ${row.lastError}`));
|
|
555
567
|
}
|
|
556
568
|
console.log(chalk.bold('\nHosted sockets\n'));
|
|
557
|
-
console.log(
|
|
558
|
-
console.log(healthLine(`browser IPC ${browserIpc.bound ? `(${browserIpc.socketPath}, ${browserIpc.sessionCount} session(s))` : '(unbound)'}`, browserIpc.bound, browserIpc.record));
|
|
569
|
+
console.log(secretsBrokerHealthLine(secrets));
|
|
559
570
|
console.log(chalk.gray('\nScheduled routines run through `agents routines` — see: agents routines stats'));
|
|
560
571
|
console.log(chalk.gray('agents daemon services enable|disable|restart <id> apply live for supervised services.'));
|
|
561
572
|
}
|
|
@@ -633,8 +644,6 @@ async function runDoctor(opts) {
|
|
|
633
644
|
const problems = [];
|
|
634
645
|
if (!status.running && enabled)
|
|
635
646
|
problems.push('Daemon is not running. Start it: agents daemon start');
|
|
636
|
-
if (status.running && isDaemonWedged())
|
|
637
|
-
problems.push('Daemon is wedged (heartbeat stale). Restart: agents daemon restart');
|
|
638
647
|
// RUSH-2418: an open auto-start circuit breaker is the FIRST thing to report
|
|
639
648
|
// for a stopped daemon — otherwise the only advice is "agents daemon start",
|
|
640
649
|
// which is the exact action the breaker just refused, with no hint that a
|
|
@@ -672,12 +681,6 @@ async function runDoctor(opts) {
|
|
|
672
681
|
if (secrets.record && secrets.record.consecutiveFailures > 0) {
|
|
673
682
|
problems.push(`Secrets broker has ${secrets.record.consecutiveFailures} consecutive failure(s): ${secrets.record.lastError}`);
|
|
674
683
|
}
|
|
675
|
-
const browserIpc = await probeBrowserIPC();
|
|
676
|
-
if (!browserIpc.bound)
|
|
677
|
-
problems.push('Browser IPC is unbound.');
|
|
678
|
-
if (browserIpc.record && browserIpc.record.consecutiveFailures > 0) {
|
|
679
|
-
problems.push(`Browser IPC has ${browserIpc.record.consecutiveFailures} consecutive failure(s): ${browserIpc.record.lastError}`);
|
|
680
|
-
}
|
|
681
684
|
const scheduler = schedulerSummary();
|
|
682
685
|
if (scheduler.failingCount > 0) {
|
|
683
686
|
problems.push(`${scheduler.failingCount} routine(s) failing their last run. See: agents routines stats`);
|
|
@@ -841,7 +844,7 @@ function runWebhooksList(json) {
|
|
|
841
844
|
export function registerDaemonCommand(program) {
|
|
842
845
|
const cmd = program
|
|
843
846
|
.command('daemon')
|
|
844
|
-
.description('The always-on daemon:
|
|
847
|
+
.description('The always-on daemon: watchdog, session/usage sync, and the routines scheduler. Bare `agents daemon` shows status.')
|
|
845
848
|
.option('--json', 'Emit as JSON')
|
|
846
849
|
.action(async (opts, command) => {
|
|
847
850
|
await runStatus({ json: command.optsWithGlobals().json === true });
|
|
@@ -870,12 +873,12 @@ export function registerDaemonCommand(program) {
|
|
|
870
873
|
agents daemon services
|
|
871
874
|
|
|
872
875
|
# Toggle or restart a service live — applies without a daemon restart
|
|
873
|
-
# for supervisor-managed services (
|
|
874
|
-
#
|
|
875
|
-
#
|
|
876
|
-
agents daemon services disable
|
|
877
|
-
agents daemon services enable
|
|
878
|
-
agents daemon services restart
|
|
876
|
+
# for supervisor-managed services (account-state, session-index,
|
|
877
|
+
# monitors' off-transition, watchdog, device-probe, self-heal,
|
|
878
|
+
# state-dir-check)
|
|
879
|
+
agents daemon services disable account-state
|
|
880
|
+
agents daemon services enable account-state
|
|
881
|
+
agents daemon services restart account-state
|
|
879
882
|
|
|
880
883
|
# Host a signed webhook receiver here, supervised and restarted on crash
|
|
881
884
|
agents daemon webhooks add --secrets-bundle linear-webhook
|
|
@@ -912,12 +915,10 @@ export function registerDaemonCommand(program) {
|
|
|
912
915
|
|
|
913
916
|
'agents daemon services enable|disable|restart <id>' applies live (no
|
|
914
917
|
daemon restart) for supervisor-managed services that were registered at
|
|
915
|
-
boot.
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
operator 'agents daemon restart'. 'agents daemon services' names which
|
|
920
|
-
case you're in per row.
|
|
918
|
+
boot. The inline scheduler re-evaluates its toggle on every reload as
|
|
919
|
+
well. A boot-disabled service other than the monitor on-transition and
|
|
920
|
+
webhook-receiver still needs an operator 'agents daemon restart'. 'agents
|
|
921
|
+
daemon services' names which case you're in per row.
|
|
921
922
|
`,
|
|
922
923
|
});
|
|
923
924
|
cmd.command('status')
|
|
@@ -1033,16 +1034,15 @@ export function registerDaemonCommand(program) {
|
|
|
1033
1034
|
agents daemon services list
|
|
1034
1035
|
|
|
1035
1036
|
# Toggle or restart live — no daemon restart for a supervisor-managed
|
|
1036
|
-
# service (
|
|
1037
|
-
agents daemon services disable
|
|
1038
|
-
agents daemon services enable
|
|
1039
|
-
agents daemon services restart
|
|
1037
|
+
# service (account-state, session-index)
|
|
1038
|
+
agents daemon services disable account-state
|
|
1039
|
+
agents daemon services enable account-state
|
|
1040
|
+
agents daemon services restart account-state
|
|
1040
1041
|
`,
|
|
1041
1042
|
notes: `
|
|
1042
1043
|
A service disabled at daemon boot is normally not registered on the
|
|
1043
|
-
supervisor, so enabling it needs an operator restart.
|
|
1044
|
-
|
|
1045
|
-
scheduler also applies enable/disable on reload; webhook-receiver and the
|
|
1044
|
+
supervisor, so enabling it needs an operator restart. The inline
|
|
1045
|
+
scheduler applies enable/disable on reload; webhook-receiver and the
|
|
1046
1046
|
monitor on-transition still require 'agents daemon restart'. Each row in
|
|
1047
1047
|
the plain-text view names which case it is; 'supervised: true/false' does
|
|
1048
1048
|
the same in --json.
|
|
@@ -1077,9 +1077,8 @@ export function registerDaemonCommand(program) {
|
|
|
1077
1077
|
* Apply an enable/disable toggle live (RUSH-3193 P4): persist it, then signal
|
|
1078
1078
|
* the running daemon to reload — its handler diffs the toggle and drives
|
|
1079
1079
|
* `supervisor.start/stop(id)` for a supervised service, so no restart is
|
|
1080
|
-
* needed for supervisor-managed services registered at boot.
|
|
1081
|
-
*
|
|
1082
|
-
* scheduler also re-evaluates its toggle on reload. webhook-receiver,
|
|
1080
|
+
* needed for supervisor-managed services registered at boot. The inline
|
|
1081
|
+
* scheduler re-evaluates its toggle on reload. webhook-receiver,
|
|
1083
1082
|
* monitors' on-transition, and other boot-disabled services still need a
|
|
1084
1083
|
* deliberate operator restart — the daemon's own reload log says so.
|
|
1085
1084
|
*/
|
package/dist/commands/events.js
CHANGED
|
@@ -203,7 +203,7 @@ export function addEventsReadOptions(command) {
|
|
|
203
203
|
.option('--command <path>', 'Only this command path — prefix match (e.g. "teams create")')
|
|
204
204
|
.option('--event <type>', 'Only this typed event (repeatable, e.g. secrets.get, run.dispatched, pr.opened)', collect, [])
|
|
205
205
|
.option('--agent <name>', 'Only events tagged with this agent')
|
|
206
|
-
.option('--caller <kind>', 'Only this caller kind (claude-code, codex,
|
|
206
|
+
.option('--caller <kind>', 'Only this caller kind (claude-code, codex, cursor, terminal, script)')
|
|
207
207
|
.option('--level <level>', 'Only this level: audit, warn, info, debug')
|
|
208
208
|
.option('--session <id>', 'Only events from this session (the provenance sessionId) — e.g. trace which session read a secret')
|
|
209
209
|
.option('--bundle <name>', 'Only events carrying this bundle in their payload — e.g. `--module secrets --bundle share` for every read of the share bundle')
|
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';
|
|
@@ -2681,6 +2682,37 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2681
2682
|
// the only `continue` is the preflight exhaustion handoff below, which
|
|
2682
2683
|
// consumes one entry of the `--fallback` spec before retrying as that
|
|
2683
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
|
+
};
|
|
2684
2716
|
preflight: for (;;) {
|
|
2685
2717
|
if (!accountPickerRequested && !configuredAccount && (!version || strategy !== 'pinned' || options.balanced || explicitStrategy)) {
|
|
2686
2718
|
if (version) {
|
|
@@ -2818,6 +2850,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2818
2850
|
// Keep the rotation so mid-run failover can still cascade across
|
|
2819
2851
|
// the other (stale) healthy accounts after a real rejection.
|
|
2820
2852
|
rotationResult = resolved.rotation;
|
|
2853
|
+
await applyPickedCandidate(selected);
|
|
2821
2854
|
if (!options.quiet) {
|
|
2822
2855
|
const identity = selected.accountLabel || 'signed-in account';
|
|
2823
2856
|
process.stderr.write(chalk.gray(`[agents] no fresh usage for any ${agent} account — you picked ${identity} · ${agent}@${selected.version}\n`));
|
|
@@ -2836,12 +2869,13 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2836
2869
|
if (resolved.rotation) {
|
|
2837
2870
|
launchSignedIn = resolved.rotation.picked.signedIn;
|
|
2838
2871
|
launchEmail = resolved.rotation.picked.email;
|
|
2872
|
+
await applyPickedCandidate(resolved.rotation.picked);
|
|
2839
2873
|
}
|
|
2840
2874
|
// A balanced/available pick of a PROVIDER account (setup-token /
|
|
2841
2875
|
// API-key) carries `providerAccount`. Resolve its env through the
|
|
2842
2876
|
// same `resolveSpawnAccount` path an explicit `--account` uses, so
|
|
2843
2877
|
// exec injects the credential; a native pick has no providerAccount
|
|
2844
|
-
// and
|
|
2878
|
+
// and resolved its slot home in applyPickedCandidate above.
|
|
2845
2879
|
const pickedProviderAccount = resolved.rotation?.picked.providerAccount;
|
|
2846
2880
|
if (pickedProviderAccount) {
|
|
2847
2881
|
try {
|
|
@@ -3561,6 +3595,24 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
3561
3595
|
// Governance chokepoint (#347): every dispatched run finalizes here.
|
|
3562
3596
|
// ONE tamper-evident audit record per run — non-fatal by contract.
|
|
3563
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
|
+
}
|
|
3564
3616
|
// First-successful-run star nudge (one-time, non-nagging). Only on a
|
|
3565
3617
|
// clean run, and never when output is machine-readable/quiet.
|
|
3566
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)
|