@phnx-labs/agents-cli 1.22.114 → 1.22.116
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 +245 -0
- package/README.md +22 -102
- package/dist/browser.js +0 -0
- package/dist/cli/command-registry.js +0 -7
- package/dist/commands/accounts.d.ts +3 -77
- package/dist/commands/accounts.js +13 -366
- package/dist/commands/audit.js +1 -1
- package/dist/commands/auth.js +0 -2
- package/dist/commands/browser.d.ts +53 -0
- package/dist/commands/browser.js +210 -9
- package/dist/commands/doctor.d.ts +2 -3
- package/dist/commands/doctor.js +0 -11
- package/dist/commands/events.d.ts +1 -3
- package/dist/commands/events.js +4 -7
- package/dist/commands/exec.d.ts +0 -1
- package/dist/commands/exec.js +214 -148
- package/dist/commands/feed-watch.js +52 -8
- package/dist/commands/feed.js +65 -14
- package/dist/commands/focus.js +1 -1
- package/dist/commands/go.d.ts +0 -17
- package/dist/commands/go.js +2 -19
- package/dist/commands/logs.js +1 -1
- package/dist/commands/mcp.js +8 -83
- package/dist/commands/memory.js +4 -47
- package/dist/commands/message.js +4 -4
- package/dist/commands/plugins.js +1 -93
- package/dist/commands/repo.js +0 -44
- package/dist/commands/resume.js +4 -1
- package/dist/commands/secrets-passthrough.js +2 -2
- package/dist/commands/send.d.ts +4 -4
- package/dist/commands/send.js +6 -51
- package/dist/commands/sessions-backup-setup.js +1 -1
- package/dist/commands/sessions-picker.d.ts +11 -0
- package/dist/commands/sessions-picker.js +88 -7
- package/dist/commands/sessions-resume.js +0 -1
- package/dist/commands/sessions-share.d.ts +5 -7
- package/dist/commands/sessions-share.js +98 -49
- package/dist/commands/sessions.d.ts +21 -2
- package/dist/commands/sessions.js +158 -11
- package/dist/commands/setup-browser.js +18 -2
- package/dist/commands/setup-computer.js +20 -6
- package/dist/commands/setup-secrets.d.ts +2 -2
- package/dist/commands/setup-secrets.js +24 -4
- package/dist/commands/setup-term.d.ts +24 -0
- package/dist/commands/setup-term.js +70 -0
- package/dist/commands/setup-terminal.d.ts +3 -0
- package/dist/commands/setup-terminal.js +22 -0
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.js +29 -11
- package/dist/commands/skills.js +0 -8
- package/dist/commands/ssh.d.ts +6 -0
- package/dist/commands/ssh.js +93 -302
- package/dist/commands/sync.js +14 -5
- package/dist/commands/teams.js +1 -1
- package/dist/commands/traces.js +1 -1
- package/dist/lib/accounting/rotate.d.ts +63 -1
- package/dist/lib/accounting/rotate.js +56 -0
- package/dist/lib/accounts/add.d.ts +0 -5
- package/dist/lib/accounts/add.js +3 -9
- package/dist/lib/accounts/slots.js +32 -2
- package/dist/lib/answer-router.d.ts +11 -2
- package/dist/lib/answer-router.js +26 -2
- package/dist/lib/artifacts-client.d.ts +20 -0
- package/dist/lib/artifacts-client.js +46 -0
- package/dist/lib/auth-mint.d.ts +5 -4
- package/dist/lib/auth-mint.js +6 -5
- package/dist/lib/browser/drivers/arc.d.ts +1 -1
- package/dist/lib/browser/runtime-state.d.ts +55 -0
- package/dist/lib/browser/runtime-state.js +99 -18
- package/dist/lib/browser/service.d.ts +10 -0
- package/dist/lib/browser/service.js +229 -37
- package/dist/lib/browser/types.d.ts +18 -0
- package/dist/lib/cli-resources.js +3 -1
- package/dist/lib/cloud/dispatch.js +1 -1
- package/dist/lib/cloudflare/creds.d.ts +10 -0
- package/dist/lib/cloudflare/creds.js +46 -0
- package/dist/lib/cloudflare/provision.d.ts +35 -0
- package/dist/lib/cloudflare/provision.js +144 -0
- package/dist/lib/computer/sessions-list.d.ts +55 -0
- package/dist/lib/computer/sessions-list.js +168 -1
- package/dist/lib/config-keys.d.ts +1 -1
- package/dist/lib/config-keys.js +5 -0
- package/dist/lib/daemon/daemon.js +8 -1
- package/dist/lib/daemon/feed-stream-service.d.ts +23 -0
- package/dist/lib/daemon/feed-stream-service.js +40 -0
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/device-config.js +61 -0
- package/dist/lib/devices/connect.d.ts +49 -5
- package/dist/lib/devices/connect.js +169 -21
- package/dist/lib/devices/doctor-findings.js +2 -6
- package/dist/lib/feed/answer.d.ts +153 -4
- package/dist/lib/feed/answer.js +716 -105
- package/dist/lib/feed/envelope.d.ts +79 -0
- package/dist/lib/feed/envelope.js +23 -0
- package/dist/lib/feed/events.d.ts +6 -0
- package/dist/lib/feed/events.js +8 -0
- package/dist/lib/feed/feed.d.ts +61 -1
- package/dist/lib/feed/feed.js +226 -14
- package/dist/lib/feed/hub-server.d.ts +141 -0
- package/dist/lib/feed/hub-server.js +586 -0
- package/dist/lib/feed/hub.d.ts +95 -0
- package/dist/lib/feed/hub.js +255 -0
- package/dist/lib/feed/pr-status.d.ts +8 -0
- package/dist/lib/feed/pr-status.js +9 -1
- package/dist/lib/feed/tool-activity.d.ts +108 -0
- package/dist/lib/feed/tool-activity.js +313 -0
- package/dist/lib/feed/tools.d.ts +198 -0
- package/dist/lib/feed/tools.js +265 -0
- package/dist/lib/feed/watch.d.ts +50 -50
- package/dist/lib/feed/watch.js +147 -16
- package/dist/lib/feed-outcome.d.ts +1 -1
- package/dist/lib/feed-outcome.js +9 -2
- package/dist/lib/feed-policy.js +9 -3
- package/dist/lib/fleet/auth-sync.d.ts +2 -55
- package/dist/lib/fleet/auth-sync.js +2 -89
- package/dist/lib/format.d.ts +1 -1
- package/dist/lib/format.js +1 -1
- package/dist/lib/git.d.ts +0 -16
- package/dist/lib/git.js +0 -58
- package/dist/lib/harness-auth-capabilities.js +7 -2
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/hosts/dispatch.d.ts +20 -1
- package/dist/lib/hosts/dispatch.js +52 -30
- package/dist/lib/hosts/remote-cmd.d.ts +72 -1
- package/dist/lib/hosts/remote-cmd.js +151 -10
- package/dist/lib/hosts/remote-cmd.test-fixture.d.ts +2 -0
- package/dist/lib/hosts/remote-cmd.test-fixture.js +22 -0
- package/dist/lib/mailbox.d.ts +12 -0
- package/dist/lib/mailbox.js +16 -2
- package/dist/lib/mcp.js +17 -11
- package/dist/lib/menubar/snapshot.d.ts +51 -0
- package/dist/lib/menubar/snapshot.js +42 -3
- package/dist/lib/open-url.js +2 -2
- package/dist/lib/probe.d.ts +4 -1
- package/dist/lib/probe.js +5 -2
- package/dist/lib/projects.d.ts +23 -0
- package/dist/lib/projects.js +78 -0
- package/dist/lib/pwsh.d.ts +33 -0
- package/dist/lib/pwsh.js +56 -0
- package/dist/lib/redact.d.ts +8 -0
- package/dist/lib/redact.js +11 -0
- package/dist/lib/refresh.d.ts +6 -2
- package/dist/lib/refresh.js +92 -72
- package/dist/lib/secrets-cli.d.ts +11 -0
- package/dist/lib/secrets-cli.js +30 -0
- package/dist/lib/secrets-client.js +3 -2
- package/dist/lib/session/active.d.ts +1 -0
- package/dist/lib/session/active.js +8 -0
- package/dist/lib/session/db.d.ts +67 -3
- package/dist/lib/session/db.js +381 -126
- package/dist/lib/session/detached.d.ts +7 -0
- package/dist/lib/session/detached.js +29 -0
- package/dist/lib/session/prompt.d.ts +23 -7
- package/dist/lib/session/prompt.js +46 -8
- package/dist/lib/session/remote/peer-stream.d.ts +24 -2
- package/dist/lib/session/remote/peer-stream.js +33 -6
- package/dist/lib/session/remote/remote-list.d.ts +20 -0
- package/dist/lib/session/remote/remote-list.js +22 -6
- package/dist/lib/session/remote/watch.d.ts +12 -0
- package/dist/lib/session/remote/watch.js +9 -0
- package/dist/lib/session/remote-preview-cache.d.ts +29 -0
- package/dist/lib/session/remote-preview-cache.js +373 -0
- package/dist/lib/session/sync/backend.d.ts +3 -3
- package/dist/lib/session/sync/backend.js +3 -3
- package/dist/lib/session/sync/provision.d.ts +1 -1
- package/dist/lib/session/sync/provision.js +2 -2
- package/dist/lib/session/tail.d.ts +50 -0
- package/dist/lib/session/tail.js +219 -0
- package/dist/lib/sessions-client.js +0 -3
- package/dist/lib/setup-tool-install.d.ts +3 -0
- package/dist/lib/setup-tool-install.js +27 -0
- package/dist/lib/setup-tool-status.d.ts +22 -0
- package/dist/lib/setup-tool-status.js +220 -0
- package/dist/lib/share-runtime.d.ts +11 -0
- package/dist/lib/share-runtime.js +63 -0
- package/dist/lib/signin-badge.d.ts +19 -4
- package/dist/lib/signin-badge.js +29 -11
- package/dist/lib/smart-launch.d.ts +1 -5
- package/dist/lib/smart-launch.js +3 -11
- package/dist/lib/ssh-exec.d.ts +44 -0
- package/dist/lib/ssh-exec.js +119 -0
- package/dist/lib/startup/command-registry.d.ts +6 -4
- package/dist/lib/startup/command-registry.js +10 -7
- package/dist/lib/state.js +2 -2
- package/dist/lib/storage/selection.d.ts +2 -2
- package/dist/lib/storage/selection.js +2 -2
- package/dist/lib/sync-umbrella.d.ts +5 -0
- package/dist/lib/sync-umbrella.js +18 -10
- package/dist/lib/term-driver.d.ts +24 -0
- package/dist/lib/term-driver.js +36 -0
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/index.js +1 -1
- package/dist/lib/terminal/inject.d.ts +38 -0
- package/dist/lib/terminal/inject.js +55 -9
- package/dist/lib/terminal/transport.d.ts +15 -5
- package/dist/lib/terminal/transport.js +61 -11
- package/dist/lib/traces/backend.d.ts +1 -2
- package/dist/lib/traces/backend.js +1 -2
- package/dist/lib/traces/provision.d.ts +1 -1
- package/dist/lib/traces/provision.js +2 -2
- package/dist/lib/types.d.ts +8 -6
- package/package.json +2 -3
- package/dist/commands/artifacts-setup.d.ts +0 -53
- package/dist/commands/artifacts-setup.js +0 -161
- package/dist/commands/artifacts.d.ts +0 -18
- package/dist/commands/artifacts.js +0 -58
- package/dist/commands/attach.d.ts +0 -12
- package/dist/commands/attach.js +0 -86
- package/dist/commands/auth-mint.d.ts +0 -12
- package/dist/commands/auth-mint.js +0 -108
- package/dist/commands/reconnect.d.ts +0 -46
- package/dist/commands/reconnect.js +0 -115
- package/dist/commands/share.d.ts +0 -293
- package/dist/commands/share.js +0 -1424
- package/dist/lib/fleet/remote-login.d.ts +0 -170
- package/dist/lib/fleet/remote-login.js +0 -568
- package/dist/lib/share/analytics.d.ts +0 -13
- package/dist/lib/share/analytics.js +0 -45
- package/dist/lib/share/backend.d.ts +0 -120
- package/dist/lib/share/backend.js +0 -176
- package/dist/lib/share/capture.d.ts +0 -31
- package/dist/lib/share/capture.js +0 -174
- package/dist/lib/share/config.d.ts +0 -72
- package/dist/lib/share/config.js +0 -211
- package/dist/lib/share/delete.d.ts +0 -123
- package/dist/lib/share/delete.js +0 -173
- package/dist/lib/share/html.d.ts +0 -20
- package/dist/lib/share/html.js +0 -88
- package/dist/lib/share/http-error.d.ts +0 -53
- package/dist/lib/share/http-error.js +0 -65
- package/dist/lib/share/og.d.ts +0 -26
- package/dist/lib/share/og.js +0 -84
- package/dist/lib/share/provision.d.ts +0 -127
- package/dist/lib/share/provision.js +0 -285
- package/dist/lib/share/publish.d.ts +0 -379
- package/dist/lib/share/publish.js +0 -818
- package/dist/lib/share/worker-template.d.ts +0 -27
- package/dist/lib/share/worker-template.js +0 -2424
- package/dist/lib/storage/index.d.ts +0 -14
- package/dist/lib/storage/index.js +0 -14
- package/dist/lib/storage/visibility.d.ts +0 -82
- package/dist/lib/storage/visibility.js +0 -99
package/dist/commands/sync.js
CHANGED
|
@@ -514,7 +514,8 @@ async function runUmbrella(opts, quiet, outLog, errLog, json = false) {
|
|
|
514
514
|
// into. Any explicit flag, --yes, or --json keeps the non-interactive path.
|
|
515
515
|
// --json is a machine consumer (and the fleet fan-out injects it), so never
|
|
516
516
|
// open a picker under it.
|
|
517
|
-
const
|
|
517
|
+
const kindSelection = parseKindSelection(opts);
|
|
518
|
+
const anyExplicitFlag = !!(opts.repos || opts.secrets || opts.cloud || opts.local || kindSelection);
|
|
518
519
|
if (!quiet && !json && !opts.yes && !anyExplicitFlag && isInteractiveTerminal()) {
|
|
519
520
|
await runInteractiveReconcile(opts, outLog, errLog);
|
|
520
521
|
return;
|
|
@@ -524,7 +525,10 @@ async function runUmbrella(opts, quiet, outLog, errLog, json = false) {
|
|
|
524
525
|
repos: opts.repos,
|
|
525
526
|
secrets: opts.secrets,
|
|
526
527
|
cloud: opts.cloud,
|
|
527
|
-
local
|
|
528
|
+
// A resource selector is a local reconcile unless the caller explicitly
|
|
529
|
+
// asks to fetch repos/secrets first. This keeps replacements such as
|
|
530
|
+
// `sync --mcp demo` scoped like the retired resource verb.
|
|
531
|
+
local: opts.local || (!!kindSelection && !opts.repos && !opts.secrets && !opts.cloud),
|
|
528
532
|
};
|
|
529
533
|
// Same chokepoint as `agents secrets push/pull` — prefers AGENTS_SYNC_PASSPHRASE,
|
|
530
534
|
// falls back to the deprecated master-key name with a single warning.
|
|
@@ -541,6 +545,8 @@ async function runUmbrella(opts, quiet, outLog, errLog, json = false) {
|
|
|
541
545
|
passphrase,
|
|
542
546
|
// quiet under --json so refresh() cannot pollute the JSON stdout the fleet parses.
|
|
543
547
|
quiet: quiet || json,
|
|
548
|
+
selection: kindSelection,
|
|
549
|
+
allowExecSurfaces: !!opts.allowExecSurfaces,
|
|
544
550
|
log: (msg) => { if (!quiet && !json)
|
|
545
551
|
outLog(chalk.gray(` ${msg}`)); },
|
|
546
552
|
});
|
|
@@ -548,20 +554,22 @@ async function runUmbrella(opts, quiet, outLog, errLog, json = false) {
|
|
|
548
554
|
// we act on central as converged by this sync rather than a stale pre-pull
|
|
549
555
|
// copy that could re-claim a profile a peer already migrated. Skipped under
|
|
550
556
|
// --cloud (fetch-only, no local reconcile).
|
|
551
|
-
if (!opts.cloud)
|
|
557
|
+
if (!opts.cloud && !kindSelection)
|
|
552
558
|
evictCentralBrowserProfilesForSync(quiet, json, outLog, errLog);
|
|
553
559
|
// Post-reconcile verification (PHNX-3186): re-read every version the reconcile
|
|
554
560
|
// wrote into and confirm it now matches source. Without this the umbrella
|
|
555
561
|
// printed `✓ sync: reconciled` unconditionally, the exact false-success the
|
|
556
562
|
// ticket reports. Residual drift downgrades the line and sets a non-zero exit.
|
|
557
|
-
const residual = result.reconciled
|
|
563
|
+
const residual = result.reconciled && !kindSelection
|
|
558
564
|
? verifyReconciled(result.reconciledVersions.map((r) => ({ agent: r.agent, version: r.version })), cwd)
|
|
559
565
|
: [];
|
|
560
566
|
// Post-reconcile repair, machine-wide: no agent scope → heal every installed
|
|
561
567
|
// agent, re-wire hooks the diff left behind, and repair managed hook runtime
|
|
562
568
|
// shims. The stale-CLI purge runs ONLY with `--prune-clis` (never automatic).
|
|
563
569
|
// Skipped under --cloud (fetch-only: nothing was reconciled to repair).
|
|
564
|
-
const repair = opts.cloud
|
|
570
|
+
const repair = opts.cloud || kindSelection
|
|
571
|
+
? null
|
|
572
|
+
: await repairAfterSync({ cwd, pruneClis: !!opts.pruneClis });
|
|
565
573
|
// A repair that left something for a human (an unresolvable shim, a failed
|
|
566
574
|
// rewire/purge) is a non-zero outcome, same as the deleted `doctor --fix`.
|
|
567
575
|
const repairFailed = repair !== null && repairHadFailures(repair);
|
|
@@ -578,6 +586,7 @@ async function runUmbrella(opts, quiet, outLog, errLog, json = false) {
|
|
|
578
586
|
secrets: result.secrets,
|
|
579
587
|
devices: result.devices,
|
|
580
588
|
reconciled: result.reconciled,
|
|
589
|
+
selection: kindSelection ?? null,
|
|
581
590
|
declined: result.declined,
|
|
582
591
|
residualDrift: residual,
|
|
583
592
|
repair: repair ? repairAfterSyncJson(repair) : null,
|
package/dist/commands/teams.js
CHANGED
|
@@ -8,7 +8,7 @@ import { mailboxDir, enqueue } from '../lib/mailbox.js';
|
|
|
8
8
|
import { resolveProvider } from '../lib/cloud/registry.js';
|
|
9
9
|
import { emit } from '../lib/feed/events.js';
|
|
10
10
|
import { maybeShowStarNudge } from '../lib/star-nudge.js';
|
|
11
|
-
import { shareRuntimeEnv } from '../lib/share
|
|
11
|
+
import { shareRuntimeEnv } from '../lib/share-runtime.js';
|
|
12
12
|
import { runSupervisor } from '../lib/teams/supervisor.js';
|
|
13
13
|
import { debug } from '../lib/teams/debug.js';
|
|
14
14
|
import { runPrWatch, DEFAULT_MAX_WAVES, } from '../lib/teams/pr-watch.js';
|
package/dist/commands/traces.js
CHANGED
|
@@ -3,7 +3,7 @@ import { setHelpSections } from '../lib/help.js';
|
|
|
3
3
|
import { readSyncLedger, syncTraces } from '../lib/traces/sync.js';
|
|
4
4
|
import { managedTracesBaseUrl, resolveTracesBackend } from '../lib/traces/backend.js';
|
|
5
5
|
import { showUrl } from '../lib/open-url.js';
|
|
6
|
-
import { DEFAULT_CF_BUNDLE, readCloudflareCreds } from '../lib/
|
|
6
|
+
import { DEFAULT_CF_BUNDLE, readCloudflareCreds } from '../lib/cloudflare/creds.js';
|
|
7
7
|
import { PHOENIX_ID_BASE } from '../lib/identity/client.js';
|
|
8
8
|
import { DEFAULT_BUCKET_NAME, DEFAULT_WORKER_NAME } from '../lib/traces/config.js';
|
|
9
9
|
import { DEFAULT_TRACES_DOMAIN } from '../lib/traces/backend.js';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Detects which installed versions have expired credentials and rotates
|
|
5
5
|
* authentication tokens so users maintain active sessions across version switches.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type AgentId, type RunStrategy } from '../types.js';
|
|
8
8
|
import type { FallbackEntry } from '../exec.js';
|
|
9
9
|
import { PROJECTION_HORIZON_MIN, capacityWeight } from './capacity.js';
|
|
10
10
|
import { type AccountInfo, type CredentialPresence } from '../agents.js';
|
|
@@ -549,6 +549,68 @@ export declare const DEFAULT_ROTATION_FAILOVER_LIMIT = 3;
|
|
|
549
549
|
* completely unchanged.
|
|
550
550
|
*/
|
|
551
551
|
export declare function rotationFailoverChain(rotation: RotateResult | null, pickedVersion: string, limit?: number): FallbackEntry[];
|
|
552
|
+
/**
|
|
553
|
+
* Whether a run SHAPE may take the preflight alternate-harness handoff
|
|
554
|
+
* ({@link preflightFallbackHandoff}) — the sibling of
|
|
555
|
+
* {@link shouldArmRotationFailover}, and load-bearing for the same reason.
|
|
556
|
+
*
|
|
557
|
+
* The handoff consumes the entry it uses from the `--fallback` spec, and the
|
|
558
|
+
* canonical `--fallback` validation runs LATER in the run command: it rejects a
|
|
559
|
+
* chain on an interactive, `--acp`, `--loop`, or `--resume-checkpoint` run, and
|
|
560
|
+
* requires a prompt. Handing off first on one of those shapes would empty the
|
|
561
|
+
* spec, leave that guard nothing to reject, and let the run proceed as the
|
|
562
|
+
* alternate harness — an interactive Codex launch from
|
|
563
|
+
* `agents run claude --interactive --fallback codex`, which the CLI refuses
|
|
564
|
+
* today. Gating the shape here keeps the original validation intact: an
|
|
565
|
+
* ineligible shape never switches, so the spec survives and still fails there
|
|
566
|
+
* with its own message.
|
|
567
|
+
*
|
|
568
|
+
* `resume` and `workflowScoped` are excluded for their own reasons: a resume is
|
|
569
|
+
* bound to the session's OWN harness, and a workflow's tool/MCP scoping is
|
|
570
|
+
* claude-only, so switching harness would silently drop the declared sandbox —
|
|
571
|
+
* the fail-open `runWithFallback` only warns about.
|
|
572
|
+
*
|
|
573
|
+
* Pure so the matrix is unit-testable without invoking the run command.
|
|
574
|
+
*/
|
|
575
|
+
export interface PreflightHandoffContext {
|
|
576
|
+
hasPrompt: boolean;
|
|
577
|
+
interactive: boolean;
|
|
578
|
+
acp: boolean;
|
|
579
|
+
loop: boolean;
|
|
580
|
+
resumeCheckpoint: boolean;
|
|
581
|
+
resume: boolean;
|
|
582
|
+
workflowScoped: boolean;
|
|
583
|
+
}
|
|
584
|
+
export declare function preflightHandoffEligible(ctx: PreflightHandoffContext): boolean;
|
|
585
|
+
/**
|
|
586
|
+
* The permitted alternate harness to launch when EVERY account of the primary
|
|
587
|
+
* agent is already exhausted at PREFLIGHT (PHNX-3999 F19).
|
|
588
|
+
*
|
|
589
|
+
* `runWithFallback` only reaches a `--fallback` entry after the primary has
|
|
590
|
+
* actually RUN and hit a rate limit. When the primary's accounts are throttled
|
|
591
|
+
* before the run starts, version resolution reports `exhausted` and the run used
|
|
592
|
+
* to exit — so the alternate a user configured for exactly this case never fired.
|
|
593
|
+
* This resolves the first usable alternate from the same `--fallback` spec, and
|
|
594
|
+
* returns the remainder of the spec so the alternate becomes the primary and is
|
|
595
|
+
* not also listed as its own fallback.
|
|
596
|
+
*
|
|
597
|
+
* Deliberately NOT a second parser: an entry this cannot resolve exactly — a
|
|
598
|
+
* typo, an alias the canonical parse would fuzzy-match, the primary itself —
|
|
599
|
+
* makes the WHOLE spec yield `null`, so the run reaches the one canonical
|
|
600
|
+
* `--fallback` parse and fails (or corrects) there with the message it already
|
|
601
|
+
* has. `--fallback typoo,codex` must never silently launch codex while dropping
|
|
602
|
+
* `typoo`.
|
|
603
|
+
*
|
|
604
|
+
* Returns `null` when there is no spec, when any entry is not an exact agent id
|
|
605
|
+
* or names the primary, or when the primary is only sign-in-recoverable rather
|
|
606
|
+
* than throttled — a logged-out account is fixed by logging in, not by silently
|
|
607
|
+
* running a different harness.
|
|
608
|
+
*/
|
|
609
|
+
export declare function preflightFallbackHandoff(spec: string | undefined, primary: AgentId, exhausted: RotateCandidate[]): {
|
|
610
|
+
agent: AgentId;
|
|
611
|
+
version?: string;
|
|
612
|
+
remainingSpec?: string;
|
|
613
|
+
} | null;
|
|
552
614
|
/**
|
|
553
615
|
* Whether a run is eligible to have a mid-run rate-limit failover chain armed
|
|
554
616
|
* (issue #348). Failover injects synthesized `FallbackEntry`s into the same
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from 'fs';
|
|
8
8
|
import * as path from 'path';
|
|
9
|
+
import { isAgentId } from '../types.js';
|
|
9
10
|
import { PROJECTION_HORIZON_MIN, capacityWeight } from './capacity.js';
|
|
10
11
|
import { accountDisplayLabel, getAccountInfo, credentialPresence, ALL_AGENT_IDS, } from '../agents.js';
|
|
11
12
|
import { readMeta, getHelpersDir } from '../state.js';
|
|
@@ -1206,6 +1207,61 @@ export function rotationFailoverChain(rotation, pickedVersion, limit = DEFAULT_R
|
|
|
1206
1207
|
}
|
|
1207
1208
|
return chain;
|
|
1208
1209
|
}
|
|
1210
|
+
export function preflightHandoffEligible(ctx) {
|
|
1211
|
+
return (ctx.hasPrompt &&
|
|
1212
|
+
!ctx.interactive &&
|
|
1213
|
+
!ctx.acp &&
|
|
1214
|
+
!ctx.loop &&
|
|
1215
|
+
!ctx.resumeCheckpoint &&
|
|
1216
|
+
!ctx.resume &&
|
|
1217
|
+
!ctx.workflowScoped);
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* The permitted alternate harness to launch when EVERY account of the primary
|
|
1221
|
+
* agent is already exhausted at PREFLIGHT (PHNX-3999 F19).
|
|
1222
|
+
*
|
|
1223
|
+
* `runWithFallback` only reaches a `--fallback` entry after the primary has
|
|
1224
|
+
* actually RUN and hit a rate limit. When the primary's accounts are throttled
|
|
1225
|
+
* before the run starts, version resolution reports `exhausted` and the run used
|
|
1226
|
+
* to exit — so the alternate a user configured for exactly this case never fired.
|
|
1227
|
+
* This resolves the first usable alternate from the same `--fallback` spec, and
|
|
1228
|
+
* returns the remainder of the spec so the alternate becomes the primary and is
|
|
1229
|
+
* not also listed as its own fallback.
|
|
1230
|
+
*
|
|
1231
|
+
* Deliberately NOT a second parser: an entry this cannot resolve exactly — a
|
|
1232
|
+
* typo, an alias the canonical parse would fuzzy-match, the primary itself —
|
|
1233
|
+
* makes the WHOLE spec yield `null`, so the run reaches the one canonical
|
|
1234
|
+
* `--fallback` parse and fails (or corrects) there with the message it already
|
|
1235
|
+
* has. `--fallback typoo,codex` must never silently launch codex while dropping
|
|
1236
|
+
* `typoo`.
|
|
1237
|
+
*
|
|
1238
|
+
* Returns `null` when there is no spec, when any entry is not an exact agent id
|
|
1239
|
+
* or names the primary, or when the primary is only sign-in-recoverable rather
|
|
1240
|
+
* than throttled — a logged-out account is fixed by logging in, not by silently
|
|
1241
|
+
* running a different harness.
|
|
1242
|
+
*/
|
|
1243
|
+
export function preflightFallbackHandoff(spec, primary, exhausted) {
|
|
1244
|
+
if (!spec)
|
|
1245
|
+
return null;
|
|
1246
|
+
if (exhausted.length === 0)
|
|
1247
|
+
return null;
|
|
1248
|
+
if (signInRecoverableCandidates(exhausted).length > 0)
|
|
1249
|
+
return null;
|
|
1250
|
+
const entries = spec.split(',').map((e) => e.trim()).filter(Boolean);
|
|
1251
|
+
if (entries.length === 0)
|
|
1252
|
+
return null;
|
|
1253
|
+
const parsed = entries.map((entry) => entry.split('@'));
|
|
1254
|
+
// Every entry has to be exactly resolvable, or this defers entirely.
|
|
1255
|
+
if (parsed.some(([name]) => !isAgentId(name) || name === primary))
|
|
1256
|
+
return null;
|
|
1257
|
+
const [name, version] = parsed[0];
|
|
1258
|
+
const remaining = entries.slice(1);
|
|
1259
|
+
return {
|
|
1260
|
+
agent: name,
|
|
1261
|
+
version: version || undefined,
|
|
1262
|
+
remainingSpec: remaining.length > 0 ? remaining.join(',') : undefined,
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1209
1265
|
export function shouldArmRotationFailover(ctx) {
|
|
1210
1266
|
return (ctx.hasRotation &&
|
|
1211
1267
|
ctx.hasVersion &&
|
|
@@ -131,8 +131,3 @@ export declare function runAdd(agent: AgentId, name: string | undefined, opts: A
|
|
|
131
131
|
* that IS how such a box logs in.
|
|
132
132
|
*/
|
|
133
133
|
export declare function runLogin(agent: AgentId, name: string, opts: AddOptions, runners?: AddRunners): Promise<AddResult>;
|
|
134
|
-
/** Back-compat alias for the retired `connect` module surface (one release). */
|
|
135
|
-
export declare const runConnect: typeof runAdd;
|
|
136
|
-
export declare const connectSupported: typeof addSupported;
|
|
137
|
-
export declare const connectRefusal: typeof addRefusal;
|
|
138
|
-
export declare const connectWorkerRefusal: typeof addWorkerRefusal;
|
package/dist/lib/accounts/add.js
CHANGED
|
@@ -15,8 +15,7 @@ import { ensureSlot, readSlots, recordSlot, slotDir } from './slots.js';
|
|
|
15
15
|
export { LOGIN_INVOCATIONS };
|
|
16
16
|
/**
|
|
17
17
|
* `agents accounts add <harness> [name]` / `agents accounts login <harness>#<name>`
|
|
18
|
-
* — the account onboarding front door (PHNX-3940, track T4
|
|
19
|
-
* `connect` module, kept as the `runConnect` alias for one release).
|
|
18
|
+
* — the account onboarding front door (PHNX-3940, track T4).
|
|
20
19
|
*
|
|
21
20
|
* An account is a credential SLOT, not an installation: the harness has ONE
|
|
22
21
|
* managed installation (`ensureHarnessInstallation`, label `main`); every
|
|
@@ -90,11 +89,11 @@ export function workerProvisioningHint(agent) {
|
|
|
90
89
|
parts.push(env === 'provider' ? 'a provider API key collected by add (--api-key)' : `the ${env} collected by add (--api-key)`);
|
|
91
90
|
}
|
|
92
91
|
else if (kind.startsWith('per-device')) {
|
|
93
|
-
parts.push(`per-device login on each worker (--per-device
|
|
92
|
+
parts.push(`per-device login on each worker (--per-device; run \`agents run ${agent} --device <box>\` there and complete its native login)`);
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
95
|
if (parts.length === 0)
|
|
97
|
-
return `${agent} has no portable credential — it logs in per box (agents
|
|
96
|
+
return `${agent} has no portable credential — it logs in per box (run \`agents run ${agent} --device <box>\` on the worker and complete its native login)`;
|
|
98
97
|
return parts.join(', or ');
|
|
99
98
|
}
|
|
100
99
|
/**
|
|
@@ -542,11 +541,6 @@ async function _runLoginLocked(agent, name, opts, lock, runners) {
|
|
|
542
541
|
warnings,
|
|
543
542
|
};
|
|
544
543
|
}
|
|
545
|
-
/** Back-compat alias for the retired `connect` module surface (one release). */
|
|
546
|
-
export const runConnect = runAdd;
|
|
547
|
-
export const connectSupported = addSupported;
|
|
548
|
-
export const connectRefusal = addRefusal;
|
|
549
|
-
export const connectWorkerRefusal = addWorkerRefusal;
|
|
550
544
|
/** Real runners: the install engine, an inherited-stdio login spawn, identity read, and the mint drive. */
|
|
551
545
|
async function defaultAddRunners() {
|
|
552
546
|
const [store, agentsMod, execMod] = await Promise.all([
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import * as fs from 'node:fs';
|
|
15
15
|
import * as path from 'node:path';
|
|
16
|
-
import { agentConfigDirName } from '../agents.js';
|
|
16
|
+
import { AGENTS, agentConfigDirName } from '../agents.js';
|
|
17
|
+
import { createLink } from '../platform/links.js';
|
|
17
18
|
import { harnessAuth, harnessWorkerIsPerDevice } from '../harness-auth-capabilities.js';
|
|
18
19
|
import { installSessionTrackerHookSync } from '../hooks/install.js';
|
|
19
20
|
import { getGlobalDefault, getVersionHomePath, listInstalledVersions } from '../installations/store.js';
|
|
@@ -64,7 +65,36 @@ function projectResources(harness, version, destHome, fromHome) {
|
|
|
64
65
|
if (!writer)
|
|
65
66
|
continue;
|
|
66
67
|
if (kind === 'rules') {
|
|
67
|
-
|
|
68
|
+
// Symlink to the version home's rules instead of composing a duplicate.
|
|
69
|
+
// Harnesses whose shim pins a separate config dir (e.g. CLAUDE_CONFIG_DIR
|
|
70
|
+
// → account slot) cause the harness to load rules from both the version
|
|
71
|
+
// home (~/.claude/) and the slot, doubling ~5 K tokens per session.
|
|
72
|
+
// A symlink gives both paths the same inode so the harness can dedup.
|
|
73
|
+
const cap = AGENTS[harness].capabilities.rules;
|
|
74
|
+
if (typeof cap !== 'object')
|
|
75
|
+
continue;
|
|
76
|
+
const srcFile = path.join(fromHome, agentConfigDirName(harness), cap.file);
|
|
77
|
+
if (!fs.existsSync(srcFile)) {
|
|
78
|
+
writer.write({ version, versionHome: destHome, selection: { preset: 'default' }, cwd });
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const destFile = path.join(destHome, agentConfigDirName(harness), cap.file);
|
|
82
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
83
|
+
try {
|
|
84
|
+
const st = fs.lstatSync(destFile);
|
|
85
|
+
if (st.isSymbolicLink() || st.isFile())
|
|
86
|
+
fs.unlinkSync(destFile);
|
|
87
|
+
}
|
|
88
|
+
catch { /* did not exist */ }
|
|
89
|
+
try {
|
|
90
|
+
createLink(srcFile, destFile);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (err.code === 'EEXIST') {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
68
98
|
continue;
|
|
69
99
|
}
|
|
70
100
|
const names = getDetector(kind, harness)?.list({ version, versionHome: fromHome, cwd }) ?? [];
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* This module picks the delivery mechanism from (open feed block × session
|
|
11
11
|
* liveness × runtime rail). Pure — unit-testable without a live terminal.
|
|
12
12
|
*/
|
|
13
|
-
import type
|
|
13
|
+
import { type OpenBlock, type BlockOption } from './feed/feed.js';
|
|
14
14
|
import type { ActiveSession } from './session/active.js';
|
|
15
15
|
import type { InjectTarget } from './terminal/index.js';
|
|
16
16
|
export type AnswerRouteKind = 'mailbox' | 'tmux' | 'iterm' | 'resume' | 'refuse';
|
|
@@ -67,7 +67,16 @@ export declare function keystrokesForAnswer(answer: string, options?: Array<Pick
|
|
|
67
67
|
};
|
|
68
68
|
/** True when the session is waiting on user input (parked on a question/plan). */
|
|
69
69
|
export declare function isParkedOnInput(session: ActiveSession | null | undefined): boolean;
|
|
70
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* True when an open feed block still needs an answer.
|
|
72
|
+
*
|
|
73
|
+
* Openness comes from {@link deriveBlockState}, not from `block.answer` being
|
|
74
|
+
* truthy: a PENDING claim records the answer while deliberately leaving the
|
|
75
|
+
* block `open` (feed.ts `recordAnswer`'s two-phase mode), so reading the field
|
|
76
|
+
* directly would call a claimed-but-undelivered block "closed" — and a retry of
|
|
77
|
+
* a stranded claim would then re-route a parked headless agent to the mailbox it
|
|
78
|
+
* will never drain (PHNX-3999).
|
|
79
|
+
*/
|
|
71
80
|
export declare function isOpenQuestionBlock(block: OpenBlock | null | undefined): boolean;
|
|
72
81
|
/**
|
|
73
82
|
* Pick the delivery mechanism for one answer.
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parked-agent answer router (RUSH-1474).
|
|
3
|
+
*
|
|
4
|
+
* `agents message` historically only enqueued into the mailbox (delivered at the
|
|
5
|
+
* next PreToolUse). That steers a *running* agent between tool calls but never
|
|
6
|
+
* unblocks a *parked* agent:
|
|
7
|
+
* - interactive TUI open on AskUserQuestion (needs keystrokes, not context)
|
|
8
|
+
* - headless run waiting on input (no next tool call; needs resume)
|
|
9
|
+
*
|
|
10
|
+
* This module picks the delivery mechanism from (open feed block × session
|
|
11
|
+
* liveness × runtime rail). Pure — unit-testable without a live terminal.
|
|
12
|
+
*/
|
|
13
|
+
import { deriveBlockState } from './feed/feed.js';
|
|
1
14
|
import { addressabilityRecoveryHint } from './terminal/resolve.js';
|
|
2
15
|
import { injectTargetFromReplyRail } from './session/inject.js';
|
|
3
16
|
/** The Escape keystroke as its raw control byte — what a tmux/iterm rail reads as a real Escape. */
|
|
@@ -64,11 +77,22 @@ export function isParkedOnInput(session) {
|
|
|
64
77
|
}
|
|
65
78
|
return false;
|
|
66
79
|
}
|
|
67
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* True when an open feed block still needs an answer.
|
|
82
|
+
*
|
|
83
|
+
* Openness comes from {@link deriveBlockState}, not from `block.answer` being
|
|
84
|
+
* truthy: a PENDING claim records the answer while deliberately leaving the
|
|
85
|
+
* block `open` (feed.ts `recordAnswer`'s two-phase mode), so reading the field
|
|
86
|
+
* directly would call a claimed-but-undelivered block "closed" — and a retry of
|
|
87
|
+
* a stranded claim would then re-route a parked headless agent to the mailbox it
|
|
88
|
+
* will never drain (PHNX-3999).
|
|
89
|
+
*/
|
|
68
90
|
export function isOpenQuestionBlock(block) {
|
|
69
91
|
if (!block)
|
|
70
92
|
return false;
|
|
71
|
-
if (block.
|
|
93
|
+
if (block.parkedAt || block.continuedAt || block.defaultedAt)
|
|
94
|
+
return false;
|
|
95
|
+
if (deriveBlockState(block) !== 'open')
|
|
72
96
|
return false;
|
|
73
97
|
return (block.questions?.length ?? 0) > 0;
|
|
74
98
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const ARTIFACTS_INSTALL_HINT = "npm i -g @phnx-labs/artifacts-cli";
|
|
2
|
+
export declare class ArtifactsClientError extends Error {
|
|
3
|
+
readonly code: 'ARTIFACTS_BIN_MISSING';
|
|
4
|
+
constructor(code: 'ARTIFACTS_BIN_MISSING', message: string);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the standalone `artifacts` executable. `ARTIFACTS_BIN` wins so a dev
|
|
8
|
+
* build can be driven without touching PATH.
|
|
9
|
+
*
|
|
10
|
+
* Resolution uses `findInPath`, which skips `~/.agents/.cache/shims` — the same
|
|
11
|
+
* load-bearing skip the secrets/computer clients rely on, so a leftover alias
|
|
12
|
+
* shim that execs back into this CLI can never be picked as the engine.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveArtifactsBin(): string;
|
|
15
|
+
/** How to invoke the resolved binary: a `.js` entrypoint runs through this
|
|
16
|
+
* runtime, a real executable is spawned directly. */
|
|
17
|
+
export declare function invocation(bin: string): {
|
|
18
|
+
command: string;
|
|
19
|
+
prefix: string[];
|
|
20
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Thin client for the standalone `artifacts` CLI (`@phnx-labs/artifacts-cli`).
|
|
2
|
+
//
|
|
3
|
+
// The artifact render + share ENGINE lives in artifacts-cli, not this repo
|
|
4
|
+
// (PHNX-3992, mirroring the secrets/computer extractions). agents-cli calls it
|
|
5
|
+
// only where a `sessions` convenience wants to publish (`agents sessions share`).
|
|
6
|
+
// A missing executable fails loud with install guidance — there is no fallback
|
|
7
|
+
// to a retired in-repo engine.
|
|
8
|
+
import { findInPath } from './agent-spec/agents.js';
|
|
9
|
+
export const ARTIFACTS_INSTALL_HINT = 'npm i -g @phnx-labs/artifacts-cli';
|
|
10
|
+
export class ArtifactsClientError extends Error {
|
|
11
|
+
code;
|
|
12
|
+
constructor(code, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.name = 'ArtifactsClientError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
let cachedBin = null;
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the standalone `artifacts` executable. `ARTIFACTS_BIN` wins so a dev
|
|
21
|
+
* build can be driven without touching PATH.
|
|
22
|
+
*
|
|
23
|
+
* Resolution uses `findInPath`, which skips `~/.agents/.cache/shims` — the same
|
|
24
|
+
* load-bearing skip the secrets/computer clients rely on, so a leftover alias
|
|
25
|
+
* shim that execs back into this CLI can never be picked as the engine.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveArtifactsBin() {
|
|
28
|
+
if (cachedBin)
|
|
29
|
+
return cachedBin;
|
|
30
|
+
const explicit = process.env.ARTIFACTS_BIN?.trim();
|
|
31
|
+
const resolved = explicit && explicit.length > 0 ? explicit : findInPath('artifacts');
|
|
32
|
+
if (!resolved) {
|
|
33
|
+
throw new ArtifactsClientError('ARTIFACTS_BIN_MISSING', 'The standalone `artifacts` CLI was not found. Install it with:\n' +
|
|
34
|
+
` ${ARTIFACTS_INSTALL_HINT}\n` +
|
|
35
|
+
'or point $ARTIFACTS_BIN at its executable.');
|
|
36
|
+
}
|
|
37
|
+
cachedBin = resolved;
|
|
38
|
+
return resolved;
|
|
39
|
+
}
|
|
40
|
+
/** How to invoke the resolved binary: a `.js` entrypoint runs through this
|
|
41
|
+
* runtime, a real executable is spawned directly. */
|
|
42
|
+
export function invocation(bin) {
|
|
43
|
+
if (/\.[mc]?js$/.test(bin))
|
|
44
|
+
return { command: process.execPath, prefix: [bin] };
|
|
45
|
+
return { command: bin, prefix: [] };
|
|
46
|
+
}
|
package/dist/lib/auth-mint.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* First-class setup-token mint + seed (PHNX-2364).
|
|
3
3
|
*
|
|
4
|
-
* Closes the mint-auth manual recipe: drive `claude setup-token` through
|
|
5
|
-
*
|
|
4
|
+
* Closes the mint-auth manual recipe: drive `claude setup-token` through an
|
|
5
|
+
* injectable term driver (`lib/term-driver.ts`), capture a well-formed
|
|
6
6
|
* `sk-ant-oat01-…` token (the #1767 ANSI-banner guard), and seed BOTH:
|
|
7
7
|
*
|
|
8
8
|
* 1. a named provider account (`agents accounts add` shape, policy never)
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import type { AgentId, Meta } from './types.js';
|
|
17
17
|
import { type CredentialAccount } from './account-registry.js';
|
|
18
18
|
import { type StorableCredentialKind } from './secrets-policy.js';
|
|
19
|
-
import { type DriveOptions, type TermDriver } from './
|
|
19
|
+
import { type DriveOptions, type TermDriver } from './term-driver.js';
|
|
20
20
|
/** Well-formed Claude setup-token as it appears inside a TTY blob. */
|
|
21
21
|
export declare const CLAUDE_SETUP_TOKEN_CAPTURE_RE: RegExp;
|
|
22
22
|
export interface MintFlow {
|
|
@@ -37,7 +37,8 @@ export interface MintFlow {
|
|
|
37
37
|
* key, COLLECTED via `--api-key` or a prompt by `accounts add` (never derived
|
|
38
38
|
* from OAuth). The api-key entries describe collection only — they have no
|
|
39
39
|
* mint argv, and `mintAndSeed` refuses them with the `accounts add` pointer.
|
|
40
|
-
*
|
|
40
|
+
* A token-less harness (kimi/antigravity) has no portable credential and logs
|
|
41
|
+
* in per box (run it there and complete its native login).
|
|
41
42
|
* The apiKeyEnv values are pinned to HARNESS_AUTH's `api-key:<ENV>` worker
|
|
42
43
|
* kinds by auth-mint.test.ts — keep them in lockstep.
|
|
43
44
|
*/
|
package/dist/lib/auth-mint.js
CHANGED
|
@@ -5,7 +5,7 @@ import { bundleBackendSync, isSecretsClientError, bundleExists, bundleExistsSync
|
|
|
5
5
|
import { assertStorableCredentialKind, reservedStoreName, } from './secrets-policy.js';
|
|
6
6
|
import { getBinaryPath, getGlobalDefault, getVersionHomePath, listInstalledVersions } from './installations/versions.js';
|
|
7
7
|
import { shellQuote } from './ssh-exec.js';
|
|
8
|
-
import { defaultTermDriver, } from './
|
|
8
|
+
import { defaultTermDriver, } from './term-driver.js';
|
|
9
9
|
import { showUrl } from './open-url.js';
|
|
10
10
|
import { loadDevices } from './devices/registry.js';
|
|
11
11
|
import { isSelfHost } from './devices/self-host.js';
|
|
@@ -21,7 +21,8 @@ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
21
21
|
* key, COLLECTED via `--api-key` or a prompt by `accounts add` (never derived
|
|
22
22
|
* from OAuth). The api-key entries describe collection only — they have no
|
|
23
23
|
* mint argv, and `mintAndSeed` refuses them with the `accounts add` pointer.
|
|
24
|
-
*
|
|
24
|
+
* A token-less harness (kimi/antigravity) has no portable credential and logs
|
|
25
|
+
* in per box (run it there and complete its native login).
|
|
25
26
|
* The apiKeyEnv values are pinned to HARNESS_AUTH's `api-key:<ENV>` worker
|
|
26
27
|
* kinds by auth-mint.test.ts — keep them in lockstep.
|
|
27
28
|
*/
|
|
@@ -63,7 +64,7 @@ export function unmintableMessage(harness) {
|
|
|
63
64
|
return [
|
|
64
65
|
`Cannot mint a setup-token for '${harness}'.`,
|
|
65
66
|
`Interactive setup-token mint is implemented for: ${listMintableHarnesses().join(', ')}.`,
|
|
66
|
-
`
|
|
67
|
+
`A token-less harness like '${harness}' logs in per box — run it on that device and complete its native login.`,
|
|
67
68
|
`API-key / pasted-token accounts use \`agents accounts add <name> --provider <provider> --auth api-key\`.`,
|
|
68
69
|
].join(' ');
|
|
69
70
|
}
|
|
@@ -534,7 +535,7 @@ async function syncMintedBundles(accountName, device) {
|
|
|
534
535
|
const accountPush = await pushBundleToHost(accountName, device, {
|
|
535
536
|
remoteBackend,
|
|
536
537
|
force: true,
|
|
537
|
-
operation: 'accounts
|
|
538
|
+
operation: 'accounts login --fleet',
|
|
538
539
|
policyNever: true,
|
|
539
540
|
agentOnly: false,
|
|
540
541
|
literalValues: {
|
|
@@ -550,7 +551,7 @@ async function syncMintedBundles(accountName, device) {
|
|
|
550
551
|
const authPush = await pushBundleToHost(AUTH_BUNDLE, device, {
|
|
551
552
|
remoteBackend: 'file',
|
|
552
553
|
force: true,
|
|
553
|
-
operation: 'accounts
|
|
554
|
+
operation: 'accounts login --fleet',
|
|
554
555
|
policyNever: true,
|
|
555
556
|
agentOnly: true,
|
|
556
557
|
});
|
|
@@ -144,10 +144,59 @@ export declare function listAllProfileSnapshots(): ProfileSnapshot[];
|
|
|
144
144
|
* alone — we'd rather leak than wrongly kill a user-owned process that
|
|
145
145
|
* happens to share metadata.
|
|
146
146
|
*/
|
|
147
|
+
/**
|
|
148
|
+
* Clean up what a DEAD daemon genuinely left behind — which is not the browser.
|
|
149
|
+
*
|
|
150
|
+
* A live local browser outliving its daemon is the NORMAL case, not an orphan.
|
|
151
|
+
* `BrowserService.shutdown` deliberately closes only CDP and leaves the browser
|
|
152
|
+
* running, and on macOS the spawn is detached so the daemon's own positive-pid
|
|
153
|
+
* `killTree` reaches the daemon alone. This reaper used to read "the recorded
|
|
154
|
+
* `daemonPid` has exited" as "everything it recorded is garbage" and SIGTERM the
|
|
155
|
+
* browser — so replacing the daemon (a menubar activation, an upgrade, a crash
|
|
156
|
+
* restart) killed a browser the user was looking at, and `clearProfileRuntime`
|
|
157
|
+
* then removed the `port`/meta that {@link readProfileRuntime} needs, so even a
|
|
158
|
+
* survivor could not be re-attached and a second browser would launch beside it.
|
|
159
|
+
*
|
|
160
|
+
* The three cases are now decided independently:
|
|
161
|
+
*
|
|
162
|
+
* - **A live local browser/electron is preserved untouched** — not signalled, not
|
|
163
|
+
* cleared, and its `daemonPid` deliberately NOT rewritten. Ownership is adopted
|
|
164
|
+
* only after an identity-checked attach succeeds (`adoptProfileRuntimeOwner`,
|
|
165
|
+
* called from the reuse branch in `BrowserService.connectProfile`), so nothing
|
|
166
|
+
* claims a browser it has not proven it can talk to. Re-running this is a no-op.
|
|
167
|
+
* - **A dead browser record is cleared**, with no signal sent: there is nothing
|
|
168
|
+
* alive to signal, and the stale pid/port would otherwise mislead the next
|
|
169
|
+
* attach.
|
|
170
|
+
* - **A stale tunnel IS reaped.** A local `ssh -L` cannot outlive its purpose, so
|
|
171
|
+
* a dead owner does make it an orphan. This is the one thing the old code was
|
|
172
|
+
* right about; it just shared a code path with the browser.
|
|
173
|
+
*
|
|
174
|
+
* The accepted trade: a genuinely leaked browser from a long-dead daemon is no
|
|
175
|
+
* longer force-killed here. Idle/abandoned cleanup has its own owner in
|
|
176
|
+
* `browser/hygiene.ts` (session-dead + idle), which the daemon's periodic tick and
|
|
177
|
+
* `agents browser prune` both call — that is the right place for it, because it can
|
|
178
|
+
* tell "idle and abandoned" from "in use right now" and this function cannot.
|
|
179
|
+
*/
|
|
147
180
|
export declare function reapOrphanedProcesses(): {
|
|
148
181
|
reaped: number;
|
|
149
182
|
details: string[];
|
|
150
183
|
};
|
|
184
|
+
/**
|
|
185
|
+
* Take ownership of a runtime record after an identity-checked attach succeeded.
|
|
186
|
+
*
|
|
187
|
+
* Called from the reuse branch in `BrowserService.connectProfile`, which has just
|
|
188
|
+
* probed `/json/version` and run `verifyBrowserIdentity` against the profile's
|
|
189
|
+
* expected browser family — so by this point the process on the recorded port is
|
|
190
|
+
* proven to be ours. Only then may a new service claim it.
|
|
191
|
+
*
|
|
192
|
+
* Rewrites `daemonPid` ALONE. Every other field — `pid`, `port`, `command`,
|
|
193
|
+
* `userDataDir`, `spawnedAt`, `kind`, `tunnelPid` — is the original launch's
|
|
194
|
+
* metadata and stays exactly as recorded: this is an ownership handover, not a
|
|
195
|
+
* re-registration, and rewriting the rest would lose the provenance the reaper and
|
|
196
|
+
* the hygiene pass both read. A record that has since disappeared is left alone
|
|
197
|
+
* rather than recreated from a partial view.
|
|
198
|
+
*/
|
|
199
|
+
export declare function adoptProfileRuntimeOwner(profileName: string, daemonPid?: number): boolean;
|
|
151
200
|
/**
|
|
152
201
|
* True when ANY cache dir belonging to this profile still has a live browser or
|
|
153
202
|
* SSH tunnel, or an open task. The composite naming (`<name>@<endpoint>`) means
|
|
@@ -233,4 +282,10 @@ export declare function buildProfilePrunePlan(): Promise<PrunePlan>;
|
|
|
233
282
|
* went, matching what `--dry-run` printed.
|
|
234
283
|
*/
|
|
235
284
|
export declare function pruneProfiles(plan: PrunePlan): Promise<PrunePlan>;
|
|
285
|
+
/**
|
|
286
|
+
* The executable/image name the live `pid` is running, or null if it can't be
|
|
287
|
+
* determined. The process-listing API differs per OS: Windows has no `ps`, so
|
|
288
|
+
* we query `tasklist` (CSV image name in column 1); POSIX uses `ps -o comm=`.
|
|
289
|
+
*/
|
|
290
|
+
export declare function liveProcessCommand(pid: number): string | null;
|
|
236
291
|
export {};
|