@phnx-labs/agents-cli 1.22.69 → 1.22.71
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 +34 -0
- package/README.md +34 -9
- package/dist/bootstrap.js +4 -4
- package/dist/commands/accounts.js +23 -7
- package/dist/commands/exec.js +2 -2
- package/dist/commands/import.js +2 -2
- package/dist/commands/models.js +2 -2
- package/dist/commands/permissions.js +2 -2
- package/dist/commands/repo.js +2 -2
- package/dist/commands/rules.js +1 -1
- package/dist/commands/send.js +9 -3
- package/dist/commands/sessions-export.d.ts +5 -1
- package/dist/commands/sessions-export.js +100 -24
- package/dist/commands/sessions-import.d.ts +2 -1
- package/dist/commands/sessions-import.js +85 -21
- package/dist/commands/traces.js +1 -1
- package/dist/lib/account-capabilities.js +0 -2
- package/dist/lib/account-registry.d.ts +3 -3
- package/dist/lib/account-registry.js +25 -7
- package/dist/lib/accounting/usage-sync.d.ts +1 -1
- package/dist/lib/accounting/usage-sync.js +3 -3
- package/dist/lib/acp/client.d.ts +1 -1
- package/dist/lib/acp/client.js +12 -1
- package/dist/lib/acp/harnesses.js +1 -1
- package/dist/lib/add-dir.js +0 -2
- package/dist/lib/agent-cli-commands.js +0 -2
- package/dist/lib/agent-spec/agents.d.ts +1 -1
- package/dist/lib/agent-spec/agents.js +2 -83
- package/dist/lib/browser/ipc.d.ts +34 -0
- package/dist/lib/browser/ipc.js +149 -20
- package/dist/lib/browser/remote-control.d.ts +6 -3
- package/dist/lib/browser/remote-control.js +6 -3
- package/dist/lib/browser/service.d.ts +4 -1
- package/dist/lib/browser/service.js +7 -1
- package/dist/lib/browser/types.d.ts +3 -1
- package/dist/lib/channels/registry.d.ts +6 -0
- package/dist/lib/channels/send.d.ts +11 -2
- package/dist/lib/channels/send.js +11 -2
- package/dist/lib/cloud/rush.d.ts +10 -2
- package/dist/lib/cloud/rush.js +15 -9
- package/dist/lib/daemon/auth-sync-service.js +1 -1
- package/dist/lib/daemon/browser-task-reap-service.js +1 -1
- package/dist/lib/daemon/daemon.js +41 -11
- package/dist/lib/daemon/heartbeat-service.js +3 -3
- package/dist/lib/daemon/keychain-reap-service.js +1 -1
- package/dist/lib/daemon/runner.d.ts +18 -1
- package/dist/lib/daemon/runner.js +237 -80
- package/dist/lib/daemon/self-heal-service.js +13 -3
- package/dist/lib/daemon/self-update-service.d.ts +174 -0
- package/dist/lib/daemon/self-update-service.js +353 -0
- package/dist/lib/daemon/state-dir-check-service.js +3 -3
- package/dist/lib/daemon/usage-sync-service.js +1 -1
- package/dist/lib/daemon/watchdog-service.js +4 -4
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/device-config.d.ts +12 -1
- package/dist/lib/device-config.js +63 -13
- package/dist/lib/exec-bounded.d.ts +52 -0
- package/dist/lib/exec-bounded.js +113 -0
- package/dist/lib/exec.d.ts +2 -2
- package/dist/lib/exec.js +3 -34
- package/dist/lib/feed/events.d.ts +22 -14
- package/dist/lib/feed/events.js +84 -44
- package/dist/lib/feed-broadcast.d.ts +12 -29
- package/dist/lib/feed-broadcast.js +28 -27
- package/dist/lib/fleet-shared-state.d.ts +12 -5
- package/dist/lib/fleet-shared-state.js +50 -20
- package/dist/lib/fs-atomic.d.ts +11 -0
- package/dist/lib/fs-atomic.js +60 -0
- package/dist/lib/hooks/install.js +0 -87
- package/dist/lib/hosts/reconcile.d.ts +11 -4
- package/dist/lib/hosts/reconcile.js +31 -5
- package/dist/lib/installations/strategies.js +1 -1
- package/dist/lib/mcp-registry.js +0 -13
- package/dist/lib/mcp.js +2 -2
- package/dist/lib/model-tiers.js +1 -1
- package/dist/lib/models.js +0 -63
- package/dist/lib/notify.d.ts +11 -0
- package/dist/lib/notify.js +17 -4
- package/dist/lib/owner-message.d.ts +46 -3
- package/dist/lib/owner-message.js +26 -6
- package/dist/lib/permissions-registry.d.ts +0 -2
- package/dist/lib/permissions-registry.js +3 -50
- package/dist/lib/permissions.d.ts +3 -17
- package/dist/lib/permissions.js +4 -73
- package/dist/lib/project-resources.d.ts +12 -0
- package/dist/lib/project-resources.js +129 -0
- package/dist/lib/routine-process-cleanup.d.ts +2 -2
- package/dist/lib/routine-process-cleanup.js +45 -34
- package/dist/lib/rush-session.d.ts +19 -0
- package/dist/lib/rush-session.js +24 -0
- package/dist/lib/secrets/drivers/rush.js +2 -1
- package/dist/lib/secrets/reaper.d.ts +2 -2
- package/dist/lib/secrets/reaper.js +13 -10
- package/dist/lib/secrets/reserved-sync.d.ts +1 -1
- package/dist/lib/secrets/reserved-sync.js +4 -4
- package/dist/lib/self-update.d.ts +21 -8
- package/dist/lib/self-update.js +54 -31
- package/dist/lib/session/cloud.js +2 -1
- package/dist/lib/session/sync/backend.d.ts +61 -0
- package/dist/lib/session/sync/backend.js +89 -0
- package/dist/lib/session/sync/managed-config.d.ts +29 -0
- package/dist/lib/session/sync/managed-config.js +23 -0
- package/dist/lib/session/sync/managed-key.d.ts +45 -0
- package/dist/lib/session/sync/managed-key.js +128 -0
- package/dist/lib/session/sync/net-client.d.ts +65 -0
- package/dist/lib/session/sync/net-client.js +117 -0
- package/dist/lib/session/sync/provision.d.ts +19 -0
- package/dist/lib/session/sync/provision.js +38 -0
- package/dist/lib/session/sync/r2.d.ts +5 -2
- package/dist/lib/session/sync/r2.js +5 -2
- package/dist/lib/session/sync/worker-template.d.ts +6 -0
- package/dist/lib/session/sync/worker-template.js +847 -0
- package/dist/lib/sink-format.d.ts +34 -0
- package/dist/lib/sink-format.js +17 -0
- package/dist/lib/staleness/detectors/permissions.js +0 -20
- package/dist/lib/staleness/writers/commands.js +1 -1
- package/dist/lib/staleness/writers/hooks.js +2 -2
- package/dist/lib/subagents-registry.js +2 -12
- package/dist/lib/subagents.d.ts +0 -10
- package/dist/lib/subagents.js +0 -37
- package/dist/lib/tmux/orphan-reap.js +6 -4
- package/dist/lib/tmux/session.js +4 -1
- package/dist/lib/traces/classify.d.ts +8 -1
- package/dist/lib/traces/insights.d.ts +13 -1
- package/dist/lib/traces/insights.js +78 -3
- package/dist/lib/traces/sync.js +8 -3
- package/dist/lib/traces/worker-template.js +9 -5
- package/dist/lib/types.d.ts +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Core agent configuration and detection module.
|
|
3
3
|
*
|
|
4
4
|
* Defines the canonical registry of current and legacy AI coding agents (Claude,
|
|
5
|
-
* Codex, Gemini, Cursor, OpenCode, OpenClaw, Copilot, Amp,
|
|
5
|
+
* Codex, Gemini, Cursor, OpenCode, OpenClaw, Copilot, Amp, Goose, Grok)
|
|
6
6
|
* with their CLI commands, config paths, capability flags, and MCP integration
|
|
7
7
|
* points.
|
|
8
8
|
*
|
|
@@ -413,52 +413,6 @@ export const AGENTS = {
|
|
|
413
413
|
supportsHooks: true,
|
|
414
414
|
capabilities: { hooks: { since: '0.3.130' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '1.1.1' }, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'], interactiveRepl: true },
|
|
415
415
|
},
|
|
416
|
-
// Oh My Pi (`omp`, omp.sh) — a Bun-based, terminal-first coding agent that runs
|
|
417
|
-
// against many model providers (OpenRouter, OpenAI, Anthropic, xAI, DeepSeek,
|
|
418
|
-
// Ollama, LM Studio, …). It is Claude-compatible: it natively discovers
|
|
419
|
-
// `.claude/commands`, `.mcp.json`, and Claude-shaped subagents, and keeps its
|
|
420
|
-
// own native resources under `~/.omp/agent/` (config dir reported by
|
|
421
|
-
// `omp config path`). configDir points AT the agent dir (not `~/.omp`) so the
|
|
422
|
-
// rules file lands at `~/.omp/agent/AGENTS.md`, the user context file omp reads.
|
|
423
|
-
pi: {
|
|
424
|
-
id: 'pi',
|
|
425
|
-
name: 'Pi',
|
|
426
|
-
sessionDir: null,
|
|
427
|
-
sessionFileExt: null,
|
|
428
|
-
versionStdoutMatch: 'semver',
|
|
429
|
-
unmanagedBinary: 'path',
|
|
430
|
-
mcpRegister: 'cli',
|
|
431
|
-
mcpAddHttp: 'transport',
|
|
432
|
-
mcpAddStdio: 'simple',
|
|
433
|
-
mcpConfigWrite: 'json-mcpServers',
|
|
434
|
-
color: 'magenta',
|
|
435
|
-
cliCommand: 'omp',
|
|
436
|
-
npmPackage: '@oh-my-pi/pi-coding-agent',
|
|
437
|
-
configDir: path.join(HOME, '.omp', 'agent'),
|
|
438
|
-
commandsDir: path.join(HOME, '.omp', 'agent', 'commands'),
|
|
439
|
-
commandsSubdir: 'commands',
|
|
440
|
-
skillsDir: path.join(HOME, '.omp', 'agent', 'skills'),
|
|
441
|
-
hooksDir: 'hooks',
|
|
442
|
-
instructionsFile: 'AGENTS.md',
|
|
443
|
-
format: 'markdown',
|
|
444
|
-
variableSyntax: '$ARGUMENTS',
|
|
445
|
-
// omp hooks are per-tool JS/TS extension modules discovered from
|
|
446
|
-
// `~/.omp/agent/hooks/{pre,post}/<tool>.<ext>` (loaded as HookFactory code),
|
|
447
|
-
// NOT the event->shell-command registrations agents-cli's hook sync writes.
|
|
448
|
-
// The two models don't map, so hooks stay off (capabilities.hooks:false).
|
|
449
|
-
supportsHooks: false,
|
|
450
|
-
// MCP: omp reads `.mcp.json` with the Claude `{ "mcpServers": {...} }` schema
|
|
451
|
-
// at user scope (`~/.omp/agent/.mcp.json`) and project scope (`<root>/.mcp.json`),
|
|
452
|
-
// stdio + http + sse with headers. skills (`~/.omp/agent/skills/<name>/SKILL.md`),
|
|
453
|
-
// commands (`~/.omp/agent/commands/*.md`), and subagents (`~/.omp/agent/agents/*.md`,
|
|
454
|
-
// Claude-shaped) are all native. allowlist is OFF: omp gates approval per-TOOL
|
|
455
|
-
// only (`tools.approval` record: allow|prompt|deny) with no command/path/domain
|
|
456
|
-
// patterns, so agents-cli's granular permission format has nothing to map to.
|
|
457
|
-
// plugins are npm packages / TS modules, not the Claude marketplace manifest.
|
|
458
|
-
// interactiveRepl: true — bare `omp` runs the TUI; `omp -p` is the one-shot
|
|
459
|
-
// form that answers a prompt and exits (upstream README, "Four entry points").
|
|
460
|
-
capabilities: { hooks: false, mcp: true, mcpHttp: true, mcpHeaders: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'skip'], interactiveRepl: true },
|
|
461
|
-
},
|
|
462
416
|
openclaw: {
|
|
463
417
|
id: 'openclaw',
|
|
464
418
|
name: 'OpenClaw',
|
|
@@ -560,39 +514,6 @@ export const AGENTS = {
|
|
|
560
514
|
// interactiveRepl: false — amp requires a prompt; bare invocation exits immediately.
|
|
561
515
|
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'], interactiveRepl: false },
|
|
562
516
|
},
|
|
563
|
-
kiro: {
|
|
564
|
-
id: 'kiro',
|
|
565
|
-
name: 'Kiro',
|
|
566
|
-
sessionDir: null,
|
|
567
|
-
sessionFileExt: null,
|
|
568
|
-
versionStdoutMatch: 'semver',
|
|
569
|
-
unmanagedBinary: 'path',
|
|
570
|
-
mcpRegister: 'cli',
|
|
571
|
-
mcpAddHttp: 'transport',
|
|
572
|
-
mcpAddStdio: 'simple',
|
|
573
|
-
mcpConfigWrite: 'json-mcpServers',
|
|
574
|
-
color: 'greenBright',
|
|
575
|
-
cliCommand: 'kiro-cli',
|
|
576
|
-
npmPackage: '',
|
|
577
|
-
installScript: 'brew install --cask kiro-cli',
|
|
578
|
-
configDir: path.join(HOME, '.kiro'),
|
|
579
|
-
commandsDir: path.join(HOME, '.kiro', 'commands'),
|
|
580
|
-
commandsSubdir: 'commands',
|
|
581
|
-
skillsDir: path.join(HOME, '.kiro', 'skills'),
|
|
582
|
-
// Hooks: v3 standalone files under ~/.kiro/hooks/*.json
|
|
583
|
-
// (`{ "version": "v1", "hooks": [...] }`). Fixed PreToolUse/PostToolUse
|
|
584
|
-
// firing in kiro-cli 0.10; fully stable by 2.6.1. Launch always passes
|
|
585
|
-
// --v3 (see AGENT_COMMANDS.kiro) so the standalone files actually load.
|
|
586
|
-
// See registerHooksForKiro.
|
|
587
|
-
hooksDir: 'hooks',
|
|
588
|
-
instructionsFile: 'AGENTS.md',
|
|
589
|
-
format: 'markdown',
|
|
590
|
-
variableSyntax: '$ARGUMENTS',
|
|
591
|
-
supportsHooks: true,
|
|
592
|
-
// interactiveRepl: false — kiro-cli requires a prompt for coding sessions; bare
|
|
593
|
-
// invocation does not open a persistent REPL.
|
|
594
|
-
capabilities: { hooks: { since: '0.10.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '2.8.0' }, skills: true, commands: true, plugins: false, subagents: { since: '1.23.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['edit'], interactiveRepl: false },
|
|
595
|
-
},
|
|
596
517
|
goose: {
|
|
597
518
|
id: 'goose',
|
|
598
519
|
name: 'Goose',
|
|
@@ -1117,7 +1038,7 @@ export const MANAGED_AGENT_IDS = ALL_AGENT_IDS.filter((id) => !AGENTS[id].deprec
|
|
|
1117
1038
|
* `curl … | sh` / `brew install` script that carries NO version token — the
|
|
1118
1039
|
* installer can only ever fetch the *current* release, and the binary then keeps
|
|
1119
1040
|
* itself up to date in place (droid, grok, antigravity, cursor, hermes,
|
|
1120
|
-
*
|
|
1041
|
+
* goose). There is no semver to pin, so agents-cli must not model these as
|
|
1121
1042
|
* having multiple installable version-homes the way it does for npm-packaged
|
|
1122
1043
|
* agents (claude, codex, kimi, …).
|
|
1123
1044
|
*
|
|
@@ -3029,8 +2950,6 @@ export const AGENT_NAME_ALIASES = {
|
|
|
3029
2950
|
gh: 'copilot',
|
|
3030
2951
|
amp: 'amp',
|
|
3031
2952
|
sourcegraph: 'amp',
|
|
3032
|
-
kiro: 'kiro',
|
|
3033
|
-
'kiro-cli': 'kiro',
|
|
3034
2953
|
goose: 'goose',
|
|
3035
2954
|
'block-goose': 'goose',
|
|
3036
2955
|
antigravity: 'antigravity',
|
|
@@ -3,6 +3,16 @@ import { BrowserService } from './service.js';
|
|
|
3
3
|
import type { IPCRequest, IPCResponse } from './types.js';
|
|
4
4
|
export interface IPCRequestOptions {
|
|
5
5
|
autoStartDaemon?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Opt-in client-side deadline in ms — most IPC actions (browser automation,
|
|
8
|
+
* long-running recordings) have no fixed budget and must NOT get one by
|
|
9
|
+
* default. Set explicitly for a request that genuinely has one, e.g.
|
|
10
|
+
* `request-self-update`, whose handler answers immediately (it kicks the
|
|
11
|
+
* install off in the background — see `triggerSelfUpdateInBackground`), so its
|
|
12
|
+
* client deadline (`DAEMON_SELF_UPDATE_TRIGGER_TIMEOUT_MS`) is a short
|
|
13
|
+
* "did the daemon accept the trigger?" bound, not a wait on the install.
|
|
14
|
+
*/
|
|
15
|
+
timeoutMs?: number;
|
|
6
16
|
}
|
|
7
17
|
export declare class BrowserServiceNotRunningError extends Error {
|
|
8
18
|
constructor();
|
|
@@ -148,6 +158,23 @@ export declare class BrowserIPCServer {
|
|
|
148
158
|
* owns the shared process.
|
|
149
159
|
*/
|
|
150
160
|
export declare function shouldRecommendDaemonRefresh(daemonVersion: string | undefined, clientVersion: string): boolean;
|
|
161
|
+
/**
|
|
162
|
+
* Reconcile the running daemon's version with ours without evicting it. If the
|
|
163
|
+
* daemon is serving stale code, ask the daemon to run its OWN self-update path
|
|
164
|
+
* (`request-self-update`, same fail-closed check/install/verify/exit
|
|
165
|
+
* SelfUpdateService's periodic tick runs, self-update-service.ts) rather than
|
|
166
|
+
* telling a human to run `agents daemon restart` by hand. A browser client
|
|
167
|
+
* still owns neither the shared supervisor nor its sibling services, so
|
|
168
|
+
* version skew can never be permission for THIS client to stop or restart
|
|
169
|
+
* that process directly (PHNX-3605 holds) — it only asks the daemon to run a
|
|
170
|
+
* path the daemon already runs on its own schedule, which only exits once the
|
|
171
|
+
* daemon itself has verified the new version is good. If self-update declines
|
|
172
|
+
* (dev build, shadowed install, already current, or a real failure) this
|
|
173
|
+
* degrades to the same "nothing changed" outcome PHNX-3605 shipped.
|
|
174
|
+
*/
|
|
175
|
+
export declare function reconcileDaemonVersion(): Promise<void>;
|
|
176
|
+
/** Test-only: `reconcileDaemonVersion` runs at most once per real process — this clears that latch so a test file can drive more than one branch. */
|
|
177
|
+
export declare function resetVersionReconciliationForTest(): void;
|
|
151
178
|
export declare function sendIPCRequest(request: IPCRequest, opts?: IPCRequestOptions): Promise<IPCResponse>;
|
|
152
179
|
/**
|
|
153
180
|
* Fill actor / launchId / sessionId from the calling process when the request
|
|
@@ -160,3 +187,10 @@ export declare function stampCallerIdentity(request: IPCRequest, env?: NodeJS.Pr
|
|
|
160
187
|
* only the client process and IPC socket lifetime differ.
|
|
161
188
|
*/
|
|
162
189
|
export declare function connectBrowserIPC(opts?: IPCRequestOptions): Promise<BrowserIPCConnection>;
|
|
190
|
+
/**
|
|
191
|
+
* Exported (rather than kept module-private) so `ipc.test.ts` can drive the
|
|
192
|
+
* `opts.timeoutMs` client-side deadline directly against a raw `net.Server`
|
|
193
|
+
* test double that never responds — the hermetic way to prove the timeout
|
|
194
|
+
* actually fires without needing a slow real IPC action to provoke it.
|
|
195
|
+
*/
|
|
196
|
+
export declare function sendRawIPCRequest(request: IPCRequest, opts?: IPCRequestOptions): Promise<IPCResponse>;
|
package/dist/lib/browser/ipc.js
CHANGED
|
@@ -7,6 +7,8 @@ import { resolveBrowserTaskIdleMs } from '../device-config.js';
|
|
|
7
7
|
import { getDaemonLogPath, isDaemonRunning, signalDaemonReload, startDaemon, } from '../daemon/daemon.js';
|
|
8
8
|
import { getCliVersion } from '../version.js';
|
|
9
9
|
import { compareVersions } from '../agent-spec/primitives.js';
|
|
10
|
+
import { scheduleSelfUpdateExit, selfUpdateSyncDeclineReason, triggerSelfUpdateInBackground } from '../daemon/self-update-service.js';
|
|
11
|
+
import { log as daemonLog } from '../daemon/daemon.js';
|
|
10
12
|
import { isDaemonServiceEnabled, queueDaemonServiceRestart, setDaemonServiceEnabled, } from '../daemon-services.js';
|
|
11
13
|
import { isFleetRemoteInvocation } from './remote-control.js';
|
|
12
14
|
import { resolveCallerIdentity } from './caller-identity.js';
|
|
@@ -82,6 +84,17 @@ function isSameSocket(socketPath, expected) {
|
|
|
82
84
|
* coming near the grace window.
|
|
83
85
|
*/
|
|
84
86
|
const IPC_CLOSE_TIMEOUT_MS = 1_500;
|
|
87
|
+
/**
|
|
88
|
+
* Client-side ceiling on the on-demand `request-self-update` call inside
|
|
89
|
+
* {@link reconcileDaemonVersion}. The daemon handler is DECOUPLED from the
|
|
90
|
+
* install — it kicks the self-update off in the background and answers
|
|
91
|
+
* immediately — so this only needs to be long enough to confirm the daemon
|
|
92
|
+
* ACCEPTED the trigger, never long enough to wait out a check/install/verify.
|
|
93
|
+
* It must stay short because a version-skewed browser verb routes through this
|
|
94
|
+
* reconcile on its way to the real request; a wedged or gone daemon degrades to
|
|
95
|
+
* the advisory PHNX-3605 shipped rather than stalling the verb.
|
|
96
|
+
*/
|
|
97
|
+
const DAEMON_SELF_UPDATE_TRIGGER_TIMEOUT_MS = 5_000;
|
|
85
98
|
/**
|
|
86
99
|
* How long {@link waitForBrowserService} waits for the browser service to come up before
|
|
87
100
|
* failing loud (PHNX-3289).
|
|
@@ -655,6 +668,44 @@ export class BrowserIPCServer {
|
|
|
655
668
|
case 'version': {
|
|
656
669
|
return { ok: true, version: getCliVersion() };
|
|
657
670
|
}
|
|
671
|
+
// On-demand self-update, triggered by a client that noticed version
|
|
672
|
+
// skew (reconcileDaemonVersion below). DECOUPLED from the install
|
|
673
|
+
// (PHNX-3605): this handler MUST NOT await the check→download→install→
|
|
674
|
+
// verify, because it sits on the path of EVERY version-skewed
|
|
675
|
+
// `agents browser <verb>` (reconcileDaemonVersion → prepareIPC → here), so
|
|
676
|
+
// awaiting the whole thing would synchronously stall an ordinary browser
|
|
677
|
+
// verb for tens of seconds — worst case ~15 min — behind daemon
|
|
678
|
+
// housekeeping, exactly the client-stall PHNX-3605 exists to prevent.
|
|
679
|
+
// Instead: kick the self-update off in the BACKGROUND (bounded, and
|
|
680
|
+
// sharing the module-level in-flight guard so two triggers can't race a
|
|
681
|
+
// second install) and respond IMMEDIATELY that it was accepted. The daemon
|
|
682
|
+
// does install→verify→exit(0) on its own; the OS supervisor relaunches it
|
|
683
|
+
// and the browser reconnects. scheduleSelfUpdateExit (fired from the
|
|
684
|
+
// background continuation on a verified update) still delays the exit so
|
|
685
|
+
// this response has flushed to the socket first, and serializes against
|
|
686
|
+
// the periodic tick's own exit.
|
|
687
|
+
case 'request-self-update': {
|
|
688
|
+
// Instant, network-free decline gate FIRST (dev build / shadowed
|
|
689
|
+
// install), so a version-skewed browser client gets the PHNX-3605
|
|
690
|
+
// "nothing changed, not evicting" advisory immediately rather than a
|
|
691
|
+
// "triggered" it would never act on — reconcileDaemonVersion keys the
|
|
692
|
+
// advisory on !selfUpdateTriggered. Only a genuinely-eligible install is
|
|
693
|
+
// backgrounded below; the slow registry/download/install/verify never
|
|
694
|
+
// runs on this response path.
|
|
695
|
+
const declineReason = selfUpdateSyncDeclineReason();
|
|
696
|
+
if (declineReason) {
|
|
697
|
+
return { ok: true, selfUpdateTriggered: false, reason: declineReason };
|
|
698
|
+
}
|
|
699
|
+
void triggerSelfUpdateInBackground({ log: daemonLog })
|
|
700
|
+
.then((outcome) => {
|
|
701
|
+
if (outcome.updated)
|
|
702
|
+
scheduleSelfUpdateExit();
|
|
703
|
+
})
|
|
704
|
+
.catch((err) => {
|
|
705
|
+
daemonLog('ERROR', `self-update: background trigger failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
706
|
+
});
|
|
707
|
+
return { ok: true, selfUpdateTriggered: true };
|
|
708
|
+
}
|
|
658
709
|
case 'show': {
|
|
659
710
|
// Task-less by design — see BrowserService.showUrl. Absent from both
|
|
660
711
|
// PAGE_CREATE_VERBS and PAGE_RESOLVE_VERBS, so bindTask never ran above
|
|
@@ -707,7 +758,7 @@ export class BrowserIPCServer {
|
|
|
707
758
|
// this box's own configured default (or 30 minutes when unset).
|
|
708
759
|
const idleMs = request.idleMinutes !== undefined
|
|
709
760
|
? (request.idleMinutes === 0 ? null : request.idleMinutes * 60_000)
|
|
710
|
-
: resolveBrowserTaskIdleMs();
|
|
761
|
+
: await resolveBrowserTaskIdleMs();
|
|
711
762
|
const reaped = await this.service.reapAbandoned({ idleMs, dryRun: request.dryRun });
|
|
712
763
|
return { ok: true, reaped };
|
|
713
764
|
}
|
|
@@ -736,7 +787,15 @@ export class BrowserIPCServer {
|
|
|
736
787
|
};
|
|
737
788
|
}
|
|
738
789
|
if (request.profile) {
|
|
739
|
-
|
|
790
|
+
try {
|
|
791
|
+
await this.service.stopProfile(request.profile, {
|
|
792
|
+
fleetRemote: request.fleetRemote,
|
|
793
|
+
actor: request.actor,
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
catch (err) {
|
|
797
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
798
|
+
}
|
|
740
799
|
return { ok: true };
|
|
741
800
|
}
|
|
742
801
|
return {
|
|
@@ -1069,12 +1128,19 @@ export function shouldRecommendDaemonRefresh(daemonVersion, clientVersion) {
|
|
|
1069
1128
|
}
|
|
1070
1129
|
/**
|
|
1071
1130
|
* Reconcile the running daemon's version with ours without evicting it. If the
|
|
1072
|
-
* daemon is serving stale code,
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1131
|
+
* daemon is serving stale code, ask the daemon to run its OWN self-update path
|
|
1132
|
+
* (`request-self-update`, same fail-closed check/install/verify/exit
|
|
1133
|
+
* SelfUpdateService's periodic tick runs, self-update-service.ts) rather than
|
|
1134
|
+
* telling a human to run `agents daemon restart` by hand. A browser client
|
|
1135
|
+
* still owns neither the shared supervisor nor its sibling services, so
|
|
1136
|
+
* version skew can never be permission for THIS client to stop or restart
|
|
1137
|
+
* that process directly (PHNX-3605 holds) — it only asks the daemon to run a
|
|
1138
|
+
* path the daemon already runs on its own schedule, which only exits once the
|
|
1139
|
+
* daemon itself has verified the new version is good. If self-update declines
|
|
1140
|
+
* (dev build, shadowed install, already current, or a real failure) this
|
|
1141
|
+
* degrades to the same "nothing changed" outcome PHNX-3605 shipped.
|
|
1076
1142
|
*/
|
|
1077
|
-
async function reconcileDaemonVersion() {
|
|
1143
|
+
export async function reconcileDaemonVersion() {
|
|
1078
1144
|
if (versionReconciledThisProcess)
|
|
1079
1145
|
return;
|
|
1080
1146
|
versionReconciledThisProcess = true;
|
|
@@ -1091,8 +1157,41 @@ async function reconcileDaemonVersion() {
|
|
|
1091
1157
|
if (!shouldRecommendDaemonRefresh(daemon, client))
|
|
1092
1158
|
return;
|
|
1093
1159
|
process.stderr.write(`\nBrowser service is running on shared daemon ${daemon}, while this CLI is ${client}. `
|
|
1094
|
-
+ '
|
|
1095
|
-
|
|
1160
|
+
+ 'Asking the daemon to self-update in the background (verify-then-exit; the OS supervisor relaunches it onto the new code)...\n');
|
|
1161
|
+
try {
|
|
1162
|
+
// SHORT timeout — the handler is decoupled from the install now (it kicks
|
|
1163
|
+
// the self-update off in the background and answers at once), so this only
|
|
1164
|
+
// waits for the daemon to CONFIRM it accepted the trigger, not for the
|
|
1165
|
+
// install to finish. It must stay short so the real browser verb that
|
|
1166
|
+
// triggered this reconcile proceeds promptly; a wedged/gone daemon degrades
|
|
1167
|
+
// to the same advisory PHNX-3605 shipped.
|
|
1168
|
+
const resp = await sendRawIPCRequest({ action: 'request-self-update' }, { autoStartDaemon: false, timeoutMs: DAEMON_SELF_UPDATE_TRIGGER_TIMEOUT_MS });
|
|
1169
|
+
if (resp.selfUpdateTriggered) {
|
|
1170
|
+
process.stderr.write('Daemon self-update requested — it verifies a newer version in the background and restarts if one is available. '
|
|
1171
|
+
+ 'Continuing without waiting.\n\n');
|
|
1172
|
+
}
|
|
1173
|
+
else {
|
|
1174
|
+
// Two shapes reach here: a CURRENT daemon that ran the instant decline
|
|
1175
|
+
// gate (dev build / shadowed install) and answered {selfUpdateTriggered:
|
|
1176
|
+
// false, reason}, and an OLDER daemon that predates the field entirely and
|
|
1177
|
+
// answered without it (no reason). Both degrade to the same non-evicting
|
|
1178
|
+
// advisory PHNX-3605 shipped; only the lead sentence differs.
|
|
1179
|
+
const declineDetail = resp.reason
|
|
1180
|
+
? `Daemon self-update declined (${resp.reason}). `
|
|
1181
|
+
: 'Daemon did not accept the self-update request. ';
|
|
1182
|
+
process.stderr.write(declineDetail
|
|
1183
|
+
+ 'Continuing without evicting the daemon or its other services. '
|
|
1184
|
+
+ 'To load current daemon code when it is safe to interrupt every hosted service: agents daemon restart\n\n');
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
catch (err) {
|
|
1188
|
+
process.stderr.write(`Could not reach the daemon to request a self-update (${err instanceof Error ? err.message : String(err)}). `
|
|
1189
|
+
+ 'To load current daemon code when it is safe to interrupt every hosted service: agents daemon restart\n\n');
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
/** Test-only: `reconcileDaemonVersion` runs at most once per real process — this clears that latch so a test file can drive more than one branch. */
|
|
1193
|
+
export function resetVersionReconciliationForTest() {
|
|
1194
|
+
versionReconciledThisProcess = false;
|
|
1096
1195
|
}
|
|
1097
1196
|
export async function sendIPCRequest(request, opts = {}) {
|
|
1098
1197
|
// Stamp caller identity ONCE here so the 28+ call sites don't each have to.
|
|
@@ -1147,13 +1246,37 @@ export async function connectBrowserIPC(opts = {}) {
|
|
|
1147
1246
|
});
|
|
1148
1247
|
});
|
|
1149
1248
|
}
|
|
1150
|
-
|
|
1249
|
+
/**
|
|
1250
|
+
* Exported (rather than kept module-private) so `ipc.test.ts` can drive the
|
|
1251
|
+
* `opts.timeoutMs` client-side deadline directly against a raw `net.Server`
|
|
1252
|
+
* test double that never responds — the hermetic way to prove the timeout
|
|
1253
|
+
* actually fires without needing a slow real IPC action to provoke it.
|
|
1254
|
+
*/
|
|
1255
|
+
export async function sendRawIPCRequest(request, opts = {}) {
|
|
1151
1256
|
const endpoint = getIpcEndpoint();
|
|
1152
1257
|
const autoStartDaemon = opts.autoStartDaemon ?? true;
|
|
1153
1258
|
await prepareIPC(request.action, opts);
|
|
1154
1259
|
return new Promise((resolve, reject) => {
|
|
1155
1260
|
const socket = net.createConnection(endpoint);
|
|
1156
1261
|
let buffer = '';
|
|
1262
|
+
let settled = false;
|
|
1263
|
+
let deadline;
|
|
1264
|
+
const settle = (fn) => {
|
|
1265
|
+
if (settled)
|
|
1266
|
+
return;
|
|
1267
|
+
settled = true;
|
|
1268
|
+
if (deadline)
|
|
1269
|
+
clearTimeout(deadline);
|
|
1270
|
+
fn();
|
|
1271
|
+
};
|
|
1272
|
+
if (opts.timeoutMs) {
|
|
1273
|
+
deadline = setTimeout(() => {
|
|
1274
|
+
settle(() => {
|
|
1275
|
+
socket.destroy();
|
|
1276
|
+
reject(new Error(`IPC request '${request.action}' timed out after ${opts.timeoutMs}ms`));
|
|
1277
|
+
});
|
|
1278
|
+
}, opts.timeoutMs);
|
|
1279
|
+
}
|
|
1157
1280
|
socket.on('connect', () => {
|
|
1158
1281
|
socket.write(JSON.stringify(request) + '\n');
|
|
1159
1282
|
});
|
|
@@ -1162,21 +1285,27 @@ async function sendRawIPCRequest(request, opts = {}) {
|
|
|
1162
1285
|
const idx = buffer.indexOf('\n');
|
|
1163
1286
|
if (idx !== -1) {
|
|
1164
1287
|
const response = JSON.parse(buffer.slice(0, idx));
|
|
1165
|
-
|
|
1166
|
-
|
|
1288
|
+
settle(() => {
|
|
1289
|
+
socket.end();
|
|
1290
|
+
resolve(response);
|
|
1291
|
+
});
|
|
1167
1292
|
}
|
|
1168
1293
|
});
|
|
1169
1294
|
socket.on('error', (err) => {
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1295
|
+
settle(() => {
|
|
1296
|
+
if (!autoStartDaemon && (err.code === 'ENOENT' || err.code === 'ECONNREFUSED')) {
|
|
1297
|
+
reject(new BrowserServiceNotRunningError());
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
reject(new Error(`IPC error: ${err.message}`));
|
|
1301
|
+
});
|
|
1175
1302
|
});
|
|
1176
1303
|
socket.on('close', () => {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1304
|
+
settle(() => {
|
|
1305
|
+
if (!buffer.includes('\n')) {
|
|
1306
|
+
reject(new Error('Connection closed before response'));
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1180
1309
|
});
|
|
1181
1310
|
});
|
|
1182
1311
|
}
|
|
@@ -16,12 +16,15 @@
|
|
|
16
16
|
* The authoritative gate is {@link assertRemoteControlAllowedForRequest}, called
|
|
17
17
|
* inside the browser daemon at the top of `resolveOrCreateTask` — the one
|
|
18
18
|
* chokepoint every task-scoped verb resolves through — plus `BrowserService.start`
|
|
19
|
-
* for the task-less `browser start` command
|
|
19
|
+
* for the task-less `browser start` command and `BrowserService.stopProfile` for
|
|
20
|
+
* the task-less `browser stop --profile` command. It has to live there because ~18
|
|
20
21
|
* page verbs (`navigate`, `click`, `screenshot`, `tab-add`, …) launch OR attach
|
|
21
22
|
* to a browser implicitly, and gating only the `browser start` command left every
|
|
22
23
|
* one of them ungated; gating only the create branch of `resolveOrCreateTask` left
|
|
23
|
-
* the attach paths ungated (RUSH-3064)
|
|
24
|
-
*
|
|
24
|
+
* the attach paths ungated (RUSH-3064); `stop --profile` is handled by `bindTask`'s
|
|
25
|
+
* early return before `resolveOrCreateTask` ever runs, so it needed its own gate
|
|
26
|
+
* call too (PHNX-3317). {@link assertRemoteControlAllowed} remains as a fast-fail
|
|
27
|
+
* CLI-side check so a refused `start` never auto-creates a profile.
|
|
25
28
|
*/
|
|
26
29
|
/** Env marker set on every remote `agents` invocation by `buildRemoteAgentsInvocation` and `markFleetRemote`. */
|
|
27
30
|
export declare const FLEET_REMOTE_ENV = "AGENTS_FLEET_REMOTE";
|
|
@@ -16,12 +16,15 @@
|
|
|
16
16
|
* The authoritative gate is {@link assertRemoteControlAllowedForRequest}, called
|
|
17
17
|
* inside the browser daemon at the top of `resolveOrCreateTask` — the one
|
|
18
18
|
* chokepoint every task-scoped verb resolves through — plus `BrowserService.start`
|
|
19
|
-
* for the task-less `browser start` command
|
|
19
|
+
* for the task-less `browser start` command and `BrowserService.stopProfile` for
|
|
20
|
+
* the task-less `browser stop --profile` command. It has to live there because ~18
|
|
20
21
|
* page verbs (`navigate`, `click`, `screenshot`, `tab-add`, …) launch OR attach
|
|
21
22
|
* to a browser implicitly, and gating only the `browser start` command left every
|
|
22
23
|
* one of them ungated; gating only the create branch of `resolveOrCreateTask` left
|
|
23
|
-
* the attach paths ungated (RUSH-3064)
|
|
24
|
-
*
|
|
24
|
+
* the attach paths ungated (RUSH-3064); `stop --profile` is handled by `bindTask`'s
|
|
25
|
+
* early return before `resolveOrCreateTask` ever runs, so it needed its own gate
|
|
26
|
+
* call too (PHNX-3317). {@link assertRemoteControlAllowed} remains as a fast-fail
|
|
27
|
+
* CLI-side check so a refused `start` never auto-creates a profile.
|
|
25
28
|
*/
|
|
26
29
|
import { getConfigValue } from '../device-config.js';
|
|
27
30
|
/** Env marker set on every remote `agents` invocation by `buildRemoteAgentsInvocation` and `markFleetRemote`. */
|
|
@@ -263,7 +263,10 @@ export declare class BrowserService {
|
|
|
263
263
|
ok: boolean;
|
|
264
264
|
profile?: string;
|
|
265
265
|
}>;
|
|
266
|
-
stopProfile(profileRef: ProfileName | ConnectionKey
|
|
266
|
+
stopProfile(profileRef: ProfileName | ConnectionKey, opts?: {
|
|
267
|
+
fleetRemote?: boolean;
|
|
268
|
+
actor?: string;
|
|
269
|
+
}): Promise<void>;
|
|
267
270
|
navigate(taskId: string, url: string, profileRef?: ProfileName | ConnectionKey): Promise<{
|
|
268
271
|
tabId: string;
|
|
269
272
|
url: string;
|
|
@@ -773,7 +773,13 @@ export class BrowserService {
|
|
|
773
773
|
async done(taskName) {
|
|
774
774
|
return this.stop(taskName);
|
|
775
775
|
}
|
|
776
|
-
async stopProfile(profileRef) {
|
|
776
|
+
async stopProfile(profileRef, opts = {}) {
|
|
777
|
+
// Consent gate — this is a fleet-remote destructive path (kills the
|
|
778
|
+
// profile's browser process and clears its runtime dir) that reached the
|
|
779
|
+
// daemon without ever hitting resolveOrCreateTask's gate, since it is a
|
|
780
|
+
// task-less stop. Same per-request marker rule as every other gated verb —
|
|
781
|
+
// see remote-control.ts.
|
|
782
|
+
assertRemoteControlAllowedForRequest(opts.fleetRemote, { actor: opts.actor });
|
|
777
783
|
// Connections are keyed by the runtime key `<profile>@<device>` (see
|
|
778
784
|
// start()) while callers pass the bare profile name (or, occasionally, an
|
|
779
785
|
// exact key). A plain `connections.get(profileRef)` therefore missed every
|
|
@@ -287,7 +287,7 @@ export interface ReapResult {
|
|
|
287
287
|
/** Tasks left alone this pass: still live, still inside the idle window, or recording. */
|
|
288
288
|
skipped: number;
|
|
289
289
|
}
|
|
290
|
-
export type IPCAction = 'start' | 'show' | 'gc' | 'record-start' | 'record-stop' | 'done' | 'stop' | 'status' | 'history' | 'navigate' | 'tab-add' | 'tab-focus' | 'tab-close' | 'tab-list' | 'evaluate' | 'screenshot' | 'pdf' | 'refs' | 'click' | 'type' | 'press' | 'hover' | 'scroll' | 'set-viewport' | 'set-device' | 'console' | 'errors' | 'requests' | 'response-body' | 'wait' | 'set-download-path' | 'wait-download' | 'upload' | 'getAppLogs' | 'version';
|
|
290
|
+
export type IPCAction = 'start' | 'show' | 'gc' | 'record-start' | 'record-stop' | 'done' | 'stop' | 'status' | 'history' | 'navigate' | 'tab-add' | 'tab-focus' | 'tab-close' | 'tab-list' | 'evaluate' | 'screenshot' | 'pdf' | 'refs' | 'click' | 'type' | 'press' | 'hover' | 'scroll' | 'set-viewport' | 'set-device' | 'console' | 'errors' | 'requests' | 'response-body' | 'wait' | 'set-download-path' | 'wait-download' | 'upload' | 'getAppLogs' | 'version' | 'request-self-update';
|
|
291
291
|
export interface IPCRequest {
|
|
292
292
|
action: IPCAction;
|
|
293
293
|
task?: string;
|
|
@@ -425,6 +425,8 @@ export interface IPCResponse {
|
|
|
425
425
|
uploadMode?: 'input' | 'drop' | 'chooser';
|
|
426
426
|
appLogs?: any[];
|
|
427
427
|
version?: string;
|
|
428
|
+
selfUpdateTriggered?: boolean;
|
|
429
|
+
reason?: string;
|
|
428
430
|
skill?: {
|
|
429
431
|
name: string;
|
|
430
432
|
path: string;
|
|
@@ -33,6 +33,12 @@ export interface SendResult {
|
|
|
33
33
|
attachments?: string[];
|
|
34
34
|
/** Mailbox provider returns the enqueued message id. */
|
|
35
35
|
msgId?: string;
|
|
36
|
+
/**
|
|
37
|
+
* The exact body handed to the provider for THIS destination. Set by the owner
|
|
38
|
+
* fan-out (`sendToOwner`) so a per-destination compose is observable — Slack
|
|
39
|
+
* carries the `mrkdwn` labeled-link variant, iMessage the plain one (PHNX-3698).
|
|
40
|
+
*/
|
|
41
|
+
body?: string;
|
|
36
42
|
/** Per-destination results when the owner policy selects multiple channels. */
|
|
37
43
|
deliveries?: SendResult[];
|
|
38
44
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { Meta } from '../types.js';
|
|
12
12
|
import type { SendResult } from './registry.js';
|
|
13
|
+
import type { SinkMessageFormat } from '../sink-format.js';
|
|
13
14
|
/** Normalized delivery request after CLI/config resolution. */
|
|
14
15
|
export interface SendEnvelope {
|
|
15
16
|
text: string;
|
|
@@ -78,8 +79,16 @@ export declare function resolveSendEnvelope(input: ResolveSendInput, meta: Meta)
|
|
|
78
79
|
* internal caller that already has a resolved envelope.
|
|
79
80
|
*/
|
|
80
81
|
export declare function deliverEnvelope(envelope: SendEnvelope, meta: Meta): Promise<SendResult>;
|
|
81
|
-
/**
|
|
82
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Resolve + deliver in one step (CLI happy path).
|
|
84
|
+
*
|
|
85
|
+
* `ownerCompose`, when given, shapes the body PER owner destination on the
|
|
86
|
+
* policy fan-out — Slack gets `mrkdwn` labeled links, iMessage stays plain
|
|
87
|
+
* (PHNX-3698). Only the owner-policy path uses it; a direct `--channel`/`--to`
|
|
88
|
+
* send is delivered verbatim. The envelope's own `text` remains the plain
|
|
89
|
+
* default (validation, `--json`, dry-run display).
|
|
90
|
+
*/
|
|
91
|
+
export declare function sendMessage(input: ResolveSendInput, meta: Meta, ownerCompose?: (format: SinkMessageFormat) => string): Promise<{
|
|
83
92
|
result: SendResult;
|
|
84
93
|
envelope: SendEnvelope;
|
|
85
94
|
} | {
|
|
@@ -108,8 +108,16 @@ export async function deliverEnvelope(envelope, meta) {
|
|
|
108
108
|
dryRun: envelope.dryRun,
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
-
/**
|
|
112
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Resolve + deliver in one step (CLI happy path).
|
|
113
|
+
*
|
|
114
|
+
* `ownerCompose`, when given, shapes the body PER owner destination on the
|
|
115
|
+
* policy fan-out — Slack gets `mrkdwn` labeled links, iMessage stays plain
|
|
116
|
+
* (PHNX-3698). Only the owner-policy path uses it; a direct `--channel`/`--to`
|
|
117
|
+
* send is delivered verbatim. The envelope's own `text` remains the plain
|
|
118
|
+
* default (validation, `--json`, dry-run display).
|
|
119
|
+
*/
|
|
120
|
+
export async function sendMessage(input, meta, ownerCompose) {
|
|
113
121
|
const resolved = resolveSendEnvelope(input, meta);
|
|
114
122
|
if (!resolved.ok)
|
|
115
123
|
return { error: resolved.error };
|
|
@@ -123,6 +131,7 @@ export async function sendMessage(input, meta) {
|
|
|
123
131
|
thread: resolved.envelope.thread,
|
|
124
132
|
attachments: resolved.envelope.attachments,
|
|
125
133
|
from: resolved.envelope.from,
|
|
134
|
+
...(ownerCompose ? { composeForFormat: ownerCompose } : {}),
|
|
126
135
|
})
|
|
127
136
|
: await deliverEnvelope(resolved.envelope, meta);
|
|
128
137
|
return { result, envelope: resolved.envelope };
|
package/dist/lib/cloud/rush.d.ts
CHANGED
|
@@ -8,10 +8,18 @@ import type { CloudProvider, CloudTask, CloudTaskStatus, CloudEvent, DispatchOpt
|
|
|
8
8
|
/**
|
|
9
9
|
* Returns true when ~/.rush/user.yaml exists, carries an access_token, and
|
|
10
10
|
* the token has not passed its expires_at timestamp (Unix seconds). A missing
|
|
11
|
-
* expires_at
|
|
12
|
-
*
|
|
11
|
+
* expires_at, or `expires_at: 0` (a non-expiring Phoenix `pid_` bearer), is
|
|
12
|
+
* treated as non-expired (see isRushSessionExpired, PHNX-3645). Pass yamlPath
|
|
13
|
+
* to override the default path in tests.
|
|
13
14
|
*/
|
|
14
15
|
export declare function isRushSessionValid(yamlPath?: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Read the Rush session access token from ~/.rush/user.yaml. Exported (with an
|
|
18
|
+
* overridable yamlPath, like isRushSessionValid) so the freshness behavior —
|
|
19
|
+
* including the `expires_at: 0` non-expiring case (PHNX-3645) — is directly
|
|
20
|
+
* testable; the class methods call it with the default path.
|
|
21
|
+
*/
|
|
22
|
+
export declare function readToken(yamlPath?: string): string;
|
|
15
23
|
/** One version's entry in the account manifest sent on every dispatch. */
|
|
16
24
|
export interface AccountManifestEntry {
|
|
17
25
|
version: string;
|