@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
|
@@ -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. */
|
|
@@ -1,124 +1,89 @@
|
|
|
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 { openSetupTerminal } from './setup-terminal.js';
|
|
11
16
|
import chalk from 'chalk';
|
|
12
|
-
import {
|
|
13
|
-
import { listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
14
|
-
import { DEFAULT_BROWSER_PROFILE_NAME, getAutoDetectedProfile, createProfile, findFreeProfilePort, getConfiguredDefaultProfileName, } from '../lib/browser/profiles.js';
|
|
15
|
-
import { DEFAULT_VIEWPORT } from '../lib/browser/devices.js';
|
|
17
|
+
import { getConfigValue } from '../lib/device-config.js';
|
|
16
18
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
17
|
-
import { defaultBrowserChoice } from './setup-preferences.js';
|
|
18
19
|
import { installSetupTool } from '../lib/setup-tool-install.js';
|
|
19
20
|
import { refreshToolSetup } from '../lib/setup-tool-status.js';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
import { browserInstalled, runBrowser } from '../lib/browser-client.js';
|
|
22
|
+
import { buildBrowserContext } from '../lib/browser/context.js';
|
|
23
|
+
/** The device-local default profile, as both agents-cli and browser-cli see it. */
|
|
24
|
+
function configuredDefaultProfile() {
|
|
25
|
+
return getConfigValue('browser.profile').value || undefined;
|
|
26
|
+
}
|
|
27
|
+
/** Run one standalone `browser` verb, inheriting the terminal, and return its exit code. */
|
|
28
|
+
async function runBrowserVerb(argv) {
|
|
29
|
+
const { exitCode } = await runBrowser({ argv, context: await buildBrowserContext() });
|
|
30
|
+
return exitCode;
|
|
31
|
+
}
|
|
23
32
|
/**
|
|
24
|
-
* Interactive browser setup. Returns true if a
|
|
25
|
-
* afterwards, false if none
|
|
26
|
-
*
|
|
27
|
-
* silently minting a logged-out
|
|
28
|
-
*
|
|
29
|
-
*
|
|
33
|
+
* Interactive browser setup. Returns true if a default profile is configured
|
|
34
|
+
* afterwards, false if none is / the user backed out. Non-interactively it only
|
|
35
|
+
* RECOGNIZES an existing default — it never creates one (PHNX-3296), since
|
|
36
|
+
* silently minting a logged-out profile on a headless box is the exact bug that
|
|
37
|
+
* removed; a headless box gets its browser from the fleet hub. Never throws on
|
|
38
|
+
* cancel — the `agents setup` hub relies on that.
|
|
30
39
|
*/
|
|
31
40
|
export async function runBrowserWizard() {
|
|
32
41
|
if (!isInteractiveTerminal()) {
|
|
33
|
-
|
|
34
|
-
// one. Silently auto-detecting an installed browser and creating an
|
|
35
|
-
// `auto-chrome` here is exactly the bug PHNX-3296 removed — a headless box
|
|
36
|
-
// gets its browser from the fleet hub (`browser.device`), or from an
|
|
37
|
-
// interactive pick later.
|
|
38
|
-
const existing = await getAutoDetectedProfile();
|
|
42
|
+
const existing = configuredDefaultProfile();
|
|
39
43
|
if (existing) {
|
|
40
|
-
console.log(chalk.dim(`
|
|
44
|
+
console.log(chalk.dim(`Default browser profile "${existing}" already configured on this machine.`));
|
|
41
45
|
return true;
|
|
42
46
|
}
|
|
43
47
|
console.log(chalk.dim('No default browser profile on this machine. Re-run `agents setup browser` in an ' +
|
|
44
48
|
'interactive terminal to pick one, or use the fleet hub: agents config set browser.device <host>.'));
|
|
45
49
|
return false;
|
|
46
50
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
const { confirm, select } = await import('@inquirer/prompts');
|
|
54
|
-
// If a default profile already exists, this is a reconfigure.
|
|
55
|
-
const existing = await getAutoDetectedProfile();
|
|
56
|
-
if (existing) {
|
|
57
|
-
console.log(chalk.dim(`Browser profile "${existing.name}" already exists (${existing.browser}${existing.binary ? ` · ${existing.binary}` : ''}).`));
|
|
58
|
-
const change = await confirm({ message: 'Re-create it (e.g. to pin a different browser)?', default: false });
|
|
59
|
-
if (!change) {
|
|
60
|
-
await maybeSetDeviceDefault(existing.name, confirm);
|
|
61
|
-
printOnboardingNextStep(existing.name);
|
|
62
|
-
return true;
|
|
51
|
+
if (!browserInstalled()) {
|
|
52
|
+
console.log(chalk.dim('Installing the standalone Browser CLI (@phnx-labs/browser-cli)…'));
|
|
53
|
+
if (!(await installSetupTool('browser'))) {
|
|
54
|
+
console.error(chalk.red('Could not install @phnx-labs/browser-cli. Install it manually with `npm i -g @phnx-labs/browser-cli`, then re-run.'));
|
|
55
|
+
return false;
|
|
63
56
|
}
|
|
64
|
-
// Re-create: drop the old one so createProfile doesn't collide.
|
|
65
|
-
const { deleteProfile } = await import('../lib/browser/profiles.js');
|
|
66
|
-
await deleteProfile(existing.name);
|
|
67
57
|
}
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
message: 'Which browser should the default profile use?',
|
|
75
|
-
default: defaultBrowserChoice(installed) ?? undefined,
|
|
76
|
-
choices: installed.map((b) => ({ name: `${b.browserType} ${chalk.dim(b.binary)}`, value: b.browserType })),
|
|
77
|
-
});
|
|
78
|
-
chosen = installed.find((b) => b.browserType === value) ?? installed[0];
|
|
58
|
+
// Let the engine detect installed browsers and create a machine-local profile
|
|
59
|
+
// for each (idempotent), then open its own picker to set this machine's default.
|
|
60
|
+
const seedCode = await runBrowserVerb(['profiles', 'seed']);
|
|
61
|
+
if (seedCode !== 0) {
|
|
62
|
+
console.error(chalk.red('`browser profiles seed` failed — see the output above.'));
|
|
63
|
+
return false;
|
|
79
64
|
}
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
binary: chosen.binary,
|
|
86
|
-
endpoints: [`cdp://127.0.0.1:${freePort}`],
|
|
87
|
-
viewport: { width: DEFAULT_VIEWPORT.width, height: DEFAULT_VIEWPORT.height },
|
|
88
|
-
};
|
|
89
|
-
await createProfile(profile);
|
|
90
|
-
console.log(chalk.green(`\nCreated browser profile "${profile.name}" → ${chosen.browserType} (CDP 127.0.0.1:${freePort}).`));
|
|
91
|
-
await maybeSetDeviceDefault(profile.name, confirm);
|
|
92
|
-
printOnboardingNextStep(profile.name);
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
/** Offer to make `name` this machine's default browser profile (device-local). */
|
|
96
|
-
async function maybeSetDeviceDefault(name, confirm) {
|
|
97
|
-
const current = getConfiguredDefaultProfileName();
|
|
98
|
-
if (current === name)
|
|
99
|
-
return; // already the device default
|
|
100
|
-
const set = await confirm({
|
|
101
|
-
message: `Make "${name}" this machine's default browser profile?`,
|
|
102
|
-
default: true,
|
|
103
|
-
});
|
|
104
|
-
if (set) {
|
|
105
|
-
setConfigValue('browser.profile', name);
|
|
106
|
-
console.log(chalk.dim(`Bare \`agents browser start\` will now use "${name}" on this machine.`));
|
|
65
|
+
await runBrowserVerb(['use']);
|
|
66
|
+
const chosen = configuredDefaultProfile();
|
|
67
|
+
if (!chosen) {
|
|
68
|
+
console.log(chalk.dim('No default profile chosen. Run `agents browser use <name>` later to set one.'));
|
|
69
|
+
return false;
|
|
107
70
|
}
|
|
71
|
+
printOnboardingNextStep(chosen);
|
|
72
|
+
return true;
|
|
108
73
|
}
|
|
109
|
-
/** The one step we can't automate:
|
|
74
|
+
/** The one step we can't automate: the browser's first-run + your own sign-in. */
|
|
110
75
|
function printOnboardingNextStep(name) {
|
|
111
76
|
console.log(chalk.bold('\nOne manual step left:'));
|
|
112
77
|
console.log(' ' +
|
|
113
78
|
chalk.cyan(`agents browser start --profile ${name}`) +
|
|
114
|
-
chalk.dim(' # finish
|
|
79
|
+
chalk.dim(' # finish the browser first-run + sign in to any sites you want automated'));
|
|
115
80
|
console.log(chalk.dim(` Then check it's ready: agents browser profiles doctor ${name}`));
|
|
116
81
|
}
|
|
117
82
|
/** Register `agents setup browser` under the parent `setup` command. */
|
|
118
83
|
export function registerSetupBrowserCommand(setupCmd) {
|
|
119
84
|
setupCmd
|
|
120
85
|
.command('browser')
|
|
121
|
-
.description('Set up `agents browser` —
|
|
86
|
+
.description('Set up `agents browser` — install the Browser CLI, seed profiles, and pick this machine\'s default.')
|
|
122
87
|
.option('--install-only', 'Install the standalone Browser CLI without changing profiles or starting a browser')
|
|
123
88
|
.option('--terminal [backend]', 'Open interactive setup in a detected or selected terminal')
|
|
124
89
|
.action(async (options) => {
|
|
@@ -21,14 +21,6 @@ export declare function macDeviceNames(reg: DeviceRegistry): string[];
|
|
|
21
21
|
* first candidate. null when there are no candidates.
|
|
22
22
|
*/
|
|
23
23
|
export declare function defaultInteractiveHostChoice(candidates: string[], self?: string): string | null;
|
|
24
|
-
/**
|
|
25
|
-
* The browser picker's highlighted default — the same browser auto-detect
|
|
26
|
-
* would win, since `listInstalledBrowsers` returns platform priority order
|
|
27
|
-
* (macOS: chrome first). null when nothing is installed.
|
|
28
|
-
*/
|
|
29
|
-
export declare function defaultBrowserChoice<T extends {
|
|
30
|
-
browserType: string;
|
|
31
|
-
}>(installed: T[]): T['browserType'] | null;
|
|
32
24
|
/**
|
|
33
25
|
* Offer to set the interactive host when none is configured and the registry
|
|
34
26
|
* has more than one macOS device. Returns true when a host was set. Silent
|
|
@@ -53,15 +45,6 @@ export declare function maybePickInteractiveHost(): Promise<boolean>;
|
|
|
53
45
|
export declare function maybePickBrowserProfile(deps?: {
|
|
54
46
|
/** Force interactivity in a test; defaults to a real TTY probe. */
|
|
55
47
|
interactive?: boolean;
|
|
56
|
-
/** Inject the prompt so the pick is testable without a TTY. */
|
|
57
|
-
select?: (config: {
|
|
58
|
-
message: string;
|
|
59
|
-
default?: string;
|
|
60
|
-
choices: Array<{
|
|
61
|
-
name: string;
|
|
62
|
-
value: string;
|
|
63
|
-
}>;
|
|
64
|
-
}) => Promise<string>;
|
|
65
48
|
}): Promise<boolean>;
|
|
66
49
|
/**
|
|
67
50
|
* The bare-`agents setup` preferences step: interactive host, then browser.
|
|
@@ -13,9 +13,8 @@
|
|
|
13
13
|
* partial state.
|
|
14
14
|
*/
|
|
15
15
|
import chalk from 'chalk';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { DEFAULT_VIEWPORT } from '../lib/browser/devices.js';
|
|
16
|
+
import { browserInstalled, runBrowser } from '../lib/browser-client.js';
|
|
17
|
+
import { buildBrowserContext } from '../lib/browser/context.js';
|
|
19
18
|
import { getConfigValue, setConfigValue } from '../lib/device-config.js';
|
|
20
19
|
import { loadDevices } from '../lib/devices/registry.js';
|
|
21
20
|
import { machineId } from '../lib/machine-id.js';
|
|
@@ -38,14 +37,6 @@ export function defaultInteractiveHostChoice(candidates, self = machineId()) {
|
|
|
38
37
|
return null;
|
|
39
38
|
return candidates.includes(self) ? self : candidates[0];
|
|
40
39
|
}
|
|
41
|
-
/**
|
|
42
|
-
* The browser picker's highlighted default — the same browser auto-detect
|
|
43
|
-
* would win, since `listInstalledBrowsers` returns platform priority order
|
|
44
|
-
* (macOS: chrome first). null when nothing is installed.
|
|
45
|
-
*/
|
|
46
|
-
export function defaultBrowserChoice(installed) {
|
|
47
|
-
return installed.length > 0 ? installed[0].browserType : null;
|
|
48
|
-
}
|
|
49
40
|
/**
|
|
50
41
|
* Offer to set the interactive host when none is configured and the registry
|
|
51
42
|
* has more than one macOS device. Returns true when a host was set. Silent
|
|
@@ -94,48 +85,26 @@ export async function maybePickBrowserProfile(deps = {}) {
|
|
|
94
85
|
const interactive = deps.interactive ?? isInteractiveTerminal();
|
|
95
86
|
if (!interactive)
|
|
96
87
|
return false;
|
|
97
|
-
|
|
88
|
+
// Already chosen (by an earlier setup, `agents browser use`, or browser-cli
|
|
89
|
+
// directly) — never re-pin behind the user's back.
|
|
90
|
+
if (getConfigValue('browser.profile').value)
|
|
98
91
|
return false;
|
|
99
|
-
|
|
92
|
+
// The engine owns detection and profile creation (PHNX-4101); it is optional,
|
|
93
|
+
// so skip the pick quietly when it is not installed rather than nagging.
|
|
94
|
+
if (!browserInstalled())
|
|
100
95
|
return false;
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
// Let the engine detect installed browsers and create a machine-local profile
|
|
97
|
+
// for each (idempotent), then open its own picker to set this machine's default.
|
|
98
|
+
const context = await buildBrowserContext();
|
|
99
|
+
const seed = await runBrowser({ argv: ['profiles', 'seed'], context });
|
|
100
|
+
if (seed.exitCode !== 0)
|
|
103
101
|
return false;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
choices: [
|
|
108
|
-
...installed.map((b) => ({ name: `${b.browserType} ${chalk.dim(b.binary)}`, value: b.browserType })),
|
|
109
|
-
{ name: `None ${chalk.dim('— this box uses the fleet hub (agents config set browser.device <host>)')}`, value: SKIP },
|
|
110
|
-
],
|
|
111
|
-
};
|
|
112
|
-
// The prompt is the only non-testable boundary here, so it is injectable —
|
|
113
|
-
// everything after (createProfile, setConfigValue) runs for real.
|
|
114
|
-
let picked;
|
|
115
|
-
if (deps.select) {
|
|
116
|
-
picked = await deps.select(config);
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
const { select } = await import('@inquirer/prompts');
|
|
120
|
-
picked = await select(config);
|
|
121
|
-
}
|
|
122
|
-
if (picked === SKIP)
|
|
102
|
+
await runBrowser({ argv: ['use'], context });
|
|
103
|
+
const chosen = getConfigValue('browser.profile').value;
|
|
104
|
+
if (!chosen)
|
|
123
105
|
return false;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const profile = {
|
|
127
|
-
name: DEFAULT_BROWSER_PROFILE_NAME,
|
|
128
|
-
description: `${chosen.browserType} profile (chosen during setup)`,
|
|
129
|
-
browser: chosen.browserType,
|
|
130
|
-
binary: chosen.binary,
|
|
131
|
-
endpoints: [`cdp://127.0.0.1:${freePort}`],
|
|
132
|
-
viewport: { width: DEFAULT_VIEWPORT.width, height: DEFAULT_VIEWPORT.height },
|
|
133
|
-
};
|
|
134
|
-
await createProfile(profile);
|
|
135
|
-
// The same key `agents browser use` writes (device-local).
|
|
136
|
-
setConfigValue('browser.profile', profile.name);
|
|
137
|
-
console.log(chalk.green(`Browser: '${chosen.browserType}'`) +
|
|
138
|
-
chalk.dim(` — profile "${profile.name}" is this machine's default (agents browser use to change).`));
|
|
106
|
+
console.log(chalk.green(`Browser: '${chosen}'`) +
|
|
107
|
+
chalk.dim(' — this machine\'s default (agents browser use to change).'));
|
|
139
108
|
return true;
|
|
140
109
|
}
|
|
141
110
|
/**
|
package/dist/commands/setup.js
CHANGED
|
@@ -31,8 +31,7 @@ import { registerAliasCommand } from './alias.js';
|
|
|
31
31
|
import { registerBetaCommands } from './beta.js';
|
|
32
32
|
import { addUrlSchemeSubcommands } from './open.js';
|
|
33
33
|
import { runPreferencesStep } from './setup-preferences.js';
|
|
34
|
-
import {
|
|
35
|
-
import { listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
34
|
+
import { browserInstalled } from '../lib/browser-client.js';
|
|
36
35
|
import { probeComputerTrust } from './computer.js';
|
|
37
36
|
import { loadDevices } from '../lib/devices/registry.js';
|
|
38
37
|
import { getConfigValue } from '../lib/device-config.js';
|
|
@@ -269,12 +268,13 @@ export async function ensureInitialized(program) {
|
|
|
269
268
|
await runSetup(program, { suppressFooter: true });
|
|
270
269
|
}
|
|
271
270
|
export async function getSetupStatus() {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const
|
|
277
|
-
const
|
|
271
|
+
// Browser readiness is now config + standalone presence (PHNX-4101): the engine
|
|
272
|
+
// (@phnx-labs/browser-cli) owns profile declarations and launchability, so
|
|
273
|
+
// agents-cli reads the shared default-profile key it and browser-cli both write
|
|
274
|
+
// rather than resolving a profile through a deleted in-repo engine.
|
|
275
|
+
const browserCliInstalled = browserInstalled();
|
|
276
|
+
const configuredBrowserProfile = getConfigValue('browser.profile').value;
|
|
277
|
+
const browserReady = browserCliInstalled && !!configuredBrowserProfile;
|
|
278
278
|
const computerState = process.platform === 'darwin' ? (await probeComputerTrust() ? 'ready' : 'missing') : 'n/a';
|
|
279
279
|
const devices = await loadDevices();
|
|
280
280
|
const coreReady = isGitRepo(getAgentsDir());
|
|
@@ -286,7 +286,7 @@ export async function getSetupStatus() {
|
|
|
286
286
|
const defaultBrowser = getConfigValue('browser.profile').value;
|
|
287
287
|
return [
|
|
288
288
|
{ phase: 'core', state: coreReady ? 'ready' : 'missing', detail: coreReady ? 'system repo ready' : 'system repo missing' },
|
|
289
|
-
{ phase: 'browser', state: browserReady ? 'ready' : 'missing', detail: browserReady ? `profile ${
|
|
289
|
+
{ phase: 'browser', state: browserReady ? 'ready' : 'missing', detail: browserReady ? `profile ${configuredBrowserProfile}` : !browserCliInstalled ? 'browser CLI not installed (npm i -g @phnx-labs/browser-cli)' : 'no default profile (agents setup browser)' },
|
|
290
290
|
{ phase: 'computer', state: computerState, detail: computerState === 'ready' ? 'helper trusted' : computerState === 'n/a' ? 'macOS local setup only' : 'helper not running or not trusted' },
|
|
291
291
|
{ phase: 'secrets', state: secretsReady ? 'ready' : 'missing', detail: secretsReady ? 'defaults chosen' : 'defaults not chosen' },
|
|
292
292
|
{ phase: 'term', state: termReady ? 'ready' : 'missing', detail: termReady ? 'installed' : 'not installed (accounts add/login spawn it)' },
|
package/dist/commands/ssh.js
CHANGED
|
@@ -789,7 +789,16 @@ async function runFleetPing(opts) {
|
|
|
789
789
|
if (opts.local) {
|
|
790
790
|
const { authRows: rows } = await refreshLocalFleetAuthState({ force: true });
|
|
791
791
|
if (opts.json) {
|
|
792
|
-
|
|
792
|
+
// Per-agent launchability from the SAME run-router path the local readiness
|
|
793
|
+
// check uses (`collectRunCandidates`/`isLaunchableSignedIn`), so a consumer
|
|
794
|
+
// can tell a box that simply could not probe — a worker's setup-token box,
|
|
795
|
+
// or a headed box that spent its usage-endpoint budget, both DROP their
|
|
796
|
+
// `no_evidence` row — from a box with no credential at all. From `rows`
|
|
797
|
+
// alone the two are indistinguishable, which broke `--host` routine
|
|
798
|
+
// readiness on every worker (PHNX-4116).
|
|
799
|
+
const { collectRunCandidates } = await import('../lib/accounting/rotate.js');
|
|
800
|
+
const launchable = (await Promise.all(ALL_AGENT_IDS.map(async (agent) => (await collectRunCandidates(agent)).some((candidate) => candidate.signedIn) ? agent : null))).filter((agent) => agent !== null);
|
|
801
|
+
console.log(JSON.stringify({ host: self, rows, launchable }));
|
|
793
802
|
}
|
|
794
803
|
else {
|
|
795
804
|
for (const line of renderAuthMatrix([{ host: self, rows }], { verbose: opts.verbose }))
|