@phnx-labs/agents-cli 1.22.116 → 1.22.118
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +247 -0
- package/dist/bootstrap.js +4 -11
- package/dist/commands/browser.d.ts +52 -70
- package/dist/commands/browser.js +192 -3562
- package/dist/commands/daemon.d.ts +39 -2
- package/dist/commands/daemon.js +72 -73
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.d.ts +18 -0
- package/dist/commands/exec.js +104 -12
- package/dist/commands/repo.js +13 -49
- package/dist/commands/route.js +5 -5
- package/dist/commands/routines.js +39 -12
- package/dist/commands/sessions-render.d.ts +1 -1
- package/dist/commands/sessions-render.js +0 -1
- package/dist/commands/sessions.test-fixture.d.ts +0 -1
- package/dist/commands/sessions.test-fixture.js +0 -37
- package/dist/commands/setup-browser.d.ts +17 -12
- package/dist/commands/setup-browser.js +52 -87
- package/dist/commands/setup-preferences.d.ts +0 -17
- package/dist/commands/setup-preferences.js +18 -49
- package/dist/commands/setup-term.d.ts +4 -3
- package/dist/commands/setup-term.js +5 -4
- package/dist/commands/setup.js +11 -11
- package/dist/commands/ssh.js +10 -1
- package/dist/commands/sync.js +6 -44
- package/dist/commands/teams.js +0 -1
- package/dist/commands/update.d.ts +19 -0
- package/dist/commands/update.js +34 -6
- package/dist/commands/view.d.ts +12 -1
- package/dist/commands/view.js +47 -2
- package/dist/index.js +107 -26
- package/dist/lib/account-capabilities.js +0 -2
- package/dist/lib/account-catalog.d.ts +38 -0
- package/dist/lib/account-catalog.js +110 -26
- package/dist/lib/account-provider-registry.js +1 -1
- package/dist/lib/account-registry.d.ts +1 -1
- package/dist/lib/account-registry.js +36 -2
- package/dist/lib/accounting/rotate.js +22 -8
- package/dist/lib/accounting/usage-ingest.d.ts +6 -1
- package/dist/lib/accounting/usage-ingest.js +115 -45
- package/dist/lib/accounting/usage-sync.d.ts +134 -31
- package/dist/lib/accounting/usage-sync.js +227 -66
- package/dist/lib/accounting/usage.d.ts +16 -3
- package/dist/lib/accounting/usage.js +26 -8
- package/dist/lib/accounts/slots.d.ts +8 -0
- package/dist/lib/accounts/slots.js +17 -0
- package/dist/lib/add-dir.js +0 -1
- package/dist/lib/agent-cli-commands.js +0 -1
- package/dist/lib/agent-spec/agents.d.ts +6 -7
- package/dist/lib/agent-spec/agents.js +6 -60
- package/dist/lib/auth-health.d.ts +69 -1
- package/dist/lib/auth-health.js +133 -4
- package/dist/lib/browser/context.d.ts +82 -0
- package/dist/lib/browser/context.js +88 -0
- package/dist/lib/browser/paths.d.ts +25 -0
- package/dist/lib/browser/paths.js +51 -0
- package/dist/lib/browser/record.d.ts +38 -0
- package/dist/lib/browser/record.js +57 -0
- package/dist/lib/browser/sessions-list.js +1 -2
- package/dist/lib/browser-client.d.ts +158 -0
- package/dist/lib/browser-client.js +234 -0
- package/dist/lib/channels/owner-sink.d.ts +1 -18
- package/dist/lib/channels/owner-sink.js +29 -82
- package/dist/lib/channels/providers/rush.d.ts +7 -7
- package/dist/lib/channels/providers/rush.js +127 -57
- package/dist/lib/claude-statusline.d.ts +1 -1
- package/dist/lib/claude-statusline.js +15 -3
- package/dist/lib/commands.js +1 -1
- package/dist/lib/crabbox/lease.js +1 -1
- package/dist/lib/crabbox/runtimes.js +1 -3
- package/dist/lib/daemon/account-state-daemon-service.js +1 -0
- package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
- package/dist/lib/daemon/auth-sync-service.js +37 -43
- package/dist/lib/daemon/daemon.d.ts +25 -14
- package/dist/lib/daemon/daemon.js +92 -154
- package/dist/lib/daemon/runner.d.ts +2 -2
- package/dist/lib/daemon/runner.js +32 -9
- package/dist/lib/daemon/self-update-service.d.ts +35 -34
- package/dist/lib/daemon/self-update-service.js +40 -36
- package/dist/lib/daemon/service.d.ts +12 -8
- package/dist/lib/daemon/supervisor.d.ts +65 -54
- package/dist/lib/daemon/supervisor.js +136 -171
- package/dist/lib/daemon/testdata/process-view-start.js +5 -2
- package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
- package/dist/lib/daemon/usage-sync-service.js +46 -78
- package/dist/lib/daemon-health.d.ts +22 -5
- package/dist/lib/daemon-health.js +47 -3
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +0 -10
- package/dist/lib/daemon-ticks.js +4 -2
- package/dist/lib/devices/doctor-findings.js +8 -11
- package/dist/lib/devices/harness-inventory.js +14 -5
- package/dist/lib/exec-account-home.js +5 -0
- package/dist/lib/exec.js +0 -11
- package/dist/lib/feed/events.js +0 -1
- package/dist/lib/feed/tool-activity.js +5 -2
- package/dist/lib/fleet-shared-state.d.ts +45 -0
- package/dist/lib/fleet-shared-state.js +78 -9
- package/dist/lib/fs-atomic.js +25 -0
- package/dist/lib/harness-auth-capabilities.js +0 -1
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/hooks/cache.d.ts +7 -0
- package/dist/lib/hooks/cache.js +27 -2
- package/dist/lib/hooks/install.js +7 -1
- package/dist/lib/hosts/ready.d.ts +38 -8
- package/dist/lib/hosts/ready.js +62 -14
- package/dist/lib/hosts/remote-cmd.js +1 -0
- package/dist/lib/installations/active-check.d.ts +27 -0
- package/dist/lib/installations/active-check.js +61 -0
- package/dist/lib/installations/migrate.js +6 -3
- package/dist/lib/installations/shims.js +9 -7
- package/dist/lib/installations/update.js +21 -15
- package/dist/lib/installations/versions.js +12 -1
- package/dist/lib/manifest.js +1 -1
- package/dist/lib/models.js +0 -2
- package/dist/lib/open-url.js +28 -54
- package/dist/lib/placement.d.ts +2 -0
- package/dist/lib/placement.js +5 -0
- package/dist/lib/profiles.js +0 -2
- package/dist/lib/refresh.js +1 -1
- package/dist/lib/routine-readiness.d.ts +35 -0
- package/dist/lib/routine-readiness.js +74 -16
- package/dist/lib/rules/compile.js +6 -0
- package/dist/lib/sandbox.js +0 -2
- package/dist/lib/secrets-policy.d.ts +89 -11
- package/dist/lib/secrets-policy.js +175 -28
- package/dist/lib/session/db.d.ts +1 -1
- package/dist/lib/session/db.js +65 -18
- package/dist/lib/session/discover.js +1 -283
- package/dist/lib/session/mirror.d.ts +4 -3
- package/dist/lib/session/mirror.js +4 -3
- package/dist/lib/session/parse.d.ts +0 -2
- package/dist/lib/session/parse.js +1 -138
- package/dist/lib/session/throughput.d.ts +1 -3
- package/dist/lib/session/throughput.js +0 -20
- package/dist/lib/session/types.d.ts +2 -2
- package/dist/lib/session/types.js +2 -2
- package/dist/lib/sessions-client.d.ts +60 -1
- package/dist/lib/sessions-client.js +227 -4
- package/dist/lib/setup-tool-status.js +8 -12
- package/dist/lib/signin-badge.d.ts +3 -2
- package/dist/lib/signin-badge.js +6 -3
- package/dist/lib/smart-launch.js +4 -1
- package/dist/lib/staleness/detectors/commands.d.ts +1 -1
- package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
- package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
- package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
- package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
- package/dist/lib/staleness/detectors/rules.d.ts +1 -1
- package/dist/lib/staleness/detectors/skills.d.ts +1 -1
- package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
- package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
- package/dist/lib/staleness/writers/commands.d.ts +1 -1
- package/dist/lib/staleness/writers/hooks.d.ts +1 -1
- package/dist/lib/staleness/writers/mcp.d.ts +1 -1
- package/dist/lib/staleness/writers/permissions.d.ts +1 -1
- package/dist/lib/staleness/writers/plugins.d.ts +1 -1
- package/dist/lib/staleness/writers/rules.d.ts +1 -1
- package/dist/lib/staleness/writers/skills.d.ts +1 -1
- package/dist/lib/staleness/writers/sources.js +5 -0
- package/dist/lib/staleness/writers/subagents.d.ts +1 -1
- package/dist/lib/staleness/writers/workflows.d.ts +1 -1
- package/dist/lib/state.d.ts +6 -4
- package/dist/lib/state.js +7 -17
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/parsers.js +1 -136
- package/dist/lib/teams/placement-probe.js +50 -15
- package/dist/lib/teams/scheduler.d.ts +7 -0
- package/dist/lib/types.d.ts +10 -1
- package/dist/lib/types.js +1 -1
- package/dist/lib/view-types.d.ts +32 -0
- package/dist/lib/watchdog/read.js +0 -1
- package/dist/lib/watchdog/rotate.js +1 -2
- package/dist/lib/watchdog/runner.js +2 -2
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdogTail.js +0 -31
- package/package.json +1 -1
- package/scripts/postinstall.js +8 -8
- package/dist/browser.d.ts +0 -2
- package/dist/browser.js +0 -17
- package/dist/commands/browser-picker.d.ts +0 -18
- package/dist/commands/browser-picker.js +0 -97
- package/dist/lib/browser/arc-discovery.d.ts +0 -52
- package/dist/lib/browser/arc-discovery.js +0 -197
- package/dist/lib/browser/arc-dom.d.ts +0 -14
- package/dist/lib/browser/arc-dom.js +0 -121
- package/dist/lib/browser/caller-identity.d.ts +0 -40
- package/dist/lib/browser/caller-identity.js +0 -175
- package/dist/lib/browser/cdp.d.ts +0 -51
- package/dist/lib/browser/cdp.js +0 -254
- package/dist/lib/browser/chrome.d.ts +0 -141
- package/dist/lib/browser/chrome.js +0 -704
- package/dist/lib/browser/chromium-discovery.d.ts +0 -28
- package/dist/lib/browser/chromium-discovery.js +0 -96
- package/dist/lib/browser/devices.d.ts +0 -34
- package/dist/lib/browser/devices.js +0 -61
- package/dist/lib/browser/domain-skills.d.ts +0 -71
- package/dist/lib/browser/domain-skills.js +0 -195
- package/dist/lib/browser/drivers/arc.d.ts +0 -40
- package/dist/lib/browser/drivers/arc.js +0 -276
- package/dist/lib/browser/drivers/firefox.d.ts +0 -99
- package/dist/lib/browser/drivers/firefox.js +0 -377
- package/dist/lib/browser/drivers/local.d.ts +0 -55
- package/dist/lib/browser/drivers/local.js +0 -266
- package/dist/lib/browser/drivers/ssh.d.ts +0 -120
- package/dist/lib/browser/drivers/ssh.js +0 -467
- package/dist/lib/browser/editor.d.ts +0 -3
- package/dist/lib/browser/editor.js +0 -50
- package/dist/lib/browser/ffmpeg.d.ts +0 -12
- package/dist/lib/browser/ffmpeg.js +0 -184
- package/dist/lib/browser/firefox-discovery.d.ts +0 -69
- package/dist/lib/browser/firefox-discovery.js +0 -162
- package/dist/lib/browser/har.d.ts +0 -85
- package/dist/lib/browser/har.js +0 -77
- package/dist/lib/browser/hygiene.d.ts +0 -97
- package/dist/lib/browser/hygiene.js +0 -153
- package/dist/lib/browser/index.d.ts +0 -5
- package/dist/lib/browser/index.js +0 -5
- package/dist/lib/browser/input.d.ts +0 -7
- package/dist/lib/browser/input.js +0 -92
- package/dist/lib/browser/ipc.d.ts +0 -204
- package/dist/lib/browser/ipc.js +0 -1453
- package/dist/lib/browser/login-detection.d.ts +0 -87
- package/dist/lib/browser/login-detection.js +0 -274
- package/dist/lib/browser/profiles.d.ts +0 -422
- package/dist/lib/browser/profiles.js +0 -1157
- package/dist/lib/browser/refs.d.ts +0 -89
- package/dist/lib/browser/refs.js +0 -191
- package/dist/lib/browser/registry.d.ts +0 -70
- package/dist/lib/browser/registry.js +0 -252
- package/dist/lib/browser/remote-control.d.ts +0 -59
- package/dist/lib/browser/remote-control.js +0 -85
- package/dist/lib/browser/resolve-target.d.ts +0 -66
- package/dist/lib/browser/resolve-target.js +0 -264
- package/dist/lib/browser/runtime-state.d.ts +0 -291
- package/dist/lib/browser/runtime-state.js +0 -584
- package/dist/lib/browser/secret-ref.d.ts +0 -10
- package/dist/lib/browser/secret-ref.js +0 -14
- package/dist/lib/browser/service.d.ts +0 -812
- package/dist/lib/browser/service.js +0 -4693
- package/dist/lib/browser/stream.d.ts +0 -17
- package/dist/lib/browser/stream.js +0 -72
- package/dist/lib/browser/task-index.d.ts +0 -78
- package/dist/lib/browser/task-index.js +0 -199
- package/dist/lib/browser/types.d.ts +0 -647
- package/dist/lib/browser/types.js +0 -61
- package/dist/lib/browser/upload.d.ts +0 -29
- package/dist/lib/browser/upload.js +0 -298
- package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
- package/dist/lib/daemon/browser-ipc-service.js +0 -46
- package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
- package/dist/lib/daemon/browser-task-reap-service.js +0 -26
- package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
- package/dist/lib/fleet-shared-repo-sync.js +0 -490
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { Readable, Writable } from 'stream';
|
|
2
|
-
interface BrowserIPCStreamOptions {
|
|
3
|
-
input: Readable;
|
|
4
|
-
output: Writable;
|
|
5
|
-
task?: string;
|
|
6
|
-
actor: string;
|
|
7
|
-
launchId?: string;
|
|
8
|
-
/** Calling agent session, forwarded onto `start` requests (RUSH-2549). */
|
|
9
|
-
sessionId?: string;
|
|
10
|
-
autoStartDaemon?: boolean;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Read browser IPC requests as NDJSON and write one NDJSON response per line.
|
|
14
|
-
* The Node process and daemon connection stay alive until input closes.
|
|
15
|
-
*/
|
|
16
|
-
export declare function runBrowserIPCStream(options: BrowserIPCStreamOptions): Promise<void>;
|
|
17
|
-
export {};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { createInterface } from 'readline';
|
|
2
|
-
import { connectBrowserIPC } from './ipc.js';
|
|
3
|
-
import { assertRemoteControlAllowed } from './remote-control.js';
|
|
4
|
-
function parseRequest(line) {
|
|
5
|
-
const parsed = JSON.parse(line);
|
|
6
|
-
if (!parsed ||
|
|
7
|
-
typeof parsed !== 'object' ||
|
|
8
|
-
Array.isArray(parsed) ||
|
|
9
|
-
typeof parsed.action !== 'string') {
|
|
10
|
-
throw new Error('Each input line must be a JSON object with an action');
|
|
11
|
-
}
|
|
12
|
-
return parsed;
|
|
13
|
-
}
|
|
14
|
-
function writeResponse(output, response) {
|
|
15
|
-
output.write(`${JSON.stringify(response)}\n`);
|
|
16
|
-
}
|
|
17
|
-
function writeErrorResponse(output, error) {
|
|
18
|
-
writeResponse(output, {
|
|
19
|
-
ok: false,
|
|
20
|
-
error: error instanceof Error ? error.message : String(error),
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Read browser IPC requests as NDJSON and write one NDJSON response per line.
|
|
25
|
-
* The Node process and daemon connection stay alive until input closes.
|
|
26
|
-
*/
|
|
27
|
-
export async function runBrowserIPCStream(options) {
|
|
28
|
-
const client = await connectBrowserIPC({ autoStartDaemon: options.autoStartDaemon });
|
|
29
|
-
const lines = createInterface({ input: options.input, crlfDelay: Infinity });
|
|
30
|
-
let defaultTask = options.task;
|
|
31
|
-
try {
|
|
32
|
-
for await (const line of lines) {
|
|
33
|
-
if (!line.trim())
|
|
34
|
-
continue;
|
|
35
|
-
let request;
|
|
36
|
-
try {
|
|
37
|
-
request = parseRequest(line);
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
writeErrorResponse(options.output, error);
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (request.action === 'start') {
|
|
44
|
-
try {
|
|
45
|
-
assertRemoteControlAllowed();
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
writeErrorResponse(options.output, error);
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
request = {
|
|
52
|
-
...request,
|
|
53
|
-
taskName: request.taskName ?? defaultTask,
|
|
54
|
-
actor: request.actor ?? options.actor,
|
|
55
|
-
launchId: request.launchId ?? options.launchId,
|
|
56
|
-
sessionId: request.sessionId ?? options.sessionId,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
else if (!request.task && defaultTask) {
|
|
60
|
-
request = { ...request, task: defaultTask };
|
|
61
|
-
}
|
|
62
|
-
const response = await client.request(request);
|
|
63
|
-
if (response.task)
|
|
64
|
-
defaultTask = response.task;
|
|
65
|
-
writeResponse(options.output, response);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
finally {
|
|
69
|
-
lines.close();
|
|
70
|
-
await client.close();
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
interface TaskBinding {
|
|
2
|
-
device: string;
|
|
3
|
-
profile?: string;
|
|
4
|
-
url?: string;
|
|
5
|
-
sessionId?: string;
|
|
6
|
-
launchId?: string;
|
|
7
|
-
createdAt: number;
|
|
8
|
-
}
|
|
9
|
-
type TaskIndex = Record<string, TaskBinding>;
|
|
10
|
-
export declare const REJECT_DEVICE_MESSAGE: string;
|
|
11
|
-
type TaskRoute = {
|
|
12
|
-
kind: 'proceed';
|
|
13
|
-
task?: string;
|
|
14
|
-
device: string;
|
|
15
|
-
} | {
|
|
16
|
-
kind: 'unknown';
|
|
17
|
-
task: string;
|
|
18
|
-
message: string;
|
|
19
|
-
} | {
|
|
20
|
-
kind: 'ambiguous';
|
|
21
|
-
message: string;
|
|
22
|
-
} | {
|
|
23
|
-
kind: 'reject-device';
|
|
24
|
-
message: string;
|
|
25
|
-
};
|
|
26
|
-
export declare function isTerminalBrowserVerb(verb: string): boolean;
|
|
27
|
-
export declare function readTaskIndex(): TaskIndex;
|
|
28
|
-
export declare function getTaskBinding(name: string): TaskBinding | undefined;
|
|
29
|
-
export declare function listTaskBindings(): Array<{
|
|
30
|
-
name: string;
|
|
31
|
-
} & TaskBinding>;
|
|
32
|
-
export declare function bindTask(name: string, binding: TaskBinding): void;
|
|
33
|
-
export declare function updateTaskBinding(name: string, patch: Partial<TaskBinding>): void;
|
|
34
|
-
export declare function unbindTask(name: string): void;
|
|
35
|
-
/** Drop every binding recorded under this profile (after `stop --profile` / gc). */
|
|
36
|
-
export declare function unbindTasksForProfile(profile: string): void;
|
|
37
|
-
export declare function tasksForCaller(sessionId?: string, launchId?: string): Array<{
|
|
38
|
-
name: string;
|
|
39
|
-
} & TaskBinding>;
|
|
40
|
-
export declare function formatOpenTaskList(entries: Array<{
|
|
41
|
-
name: string;
|
|
42
|
-
} & TaskBinding>, now?: number): string;
|
|
43
|
-
export declare function unknownTaskMessage(name: string, entries?: ({
|
|
44
|
-
name: string;
|
|
45
|
-
} & TaskBinding)[]): string;
|
|
46
|
-
export declare function ambiguousTasksMessage(entries: Array<{
|
|
47
|
-
name: string;
|
|
48
|
-
} & TaskBinding>): string;
|
|
49
|
-
/**
|
|
50
|
-
* Decide where a page verb should run.
|
|
51
|
-
*
|
|
52
|
-
* `--device` on a later verb is always rejected (bind it at start).
|
|
53
|
-
* A named task that is not in the local index fails loud with the open-task
|
|
54
|
-
* list. Two or more tasks for this caller, with no `--task`, fail the same way.
|
|
55
|
-
* Zero matches with no `--task` follows the fleet browser hub when the caller
|
|
56
|
-
* passed one (`opts.hub` — the box drives `browser.device` by default), so a
|
|
57
|
-
* cold page verb reaches the logged-in hub browser exactly as a bare
|
|
58
|
-
* `agents browser start` already does; with no hub it proceeds locally and the
|
|
59
|
-
* daemon resolves from caller identity for the first implicit create. The caller
|
|
60
|
-
* (the CLI's task-routing hook) supplies `opts.hub` from `defaultBrowserHub()`,
|
|
61
|
-
* which is already undefined on the hub itself and on a fleet-remote re-exec, so
|
|
62
|
-
* this can never forward to self or loop.
|
|
63
|
-
*/
|
|
64
|
-
export declare function resolveTaskRoute(opts: {
|
|
65
|
-
task?: string;
|
|
66
|
-
device?: string;
|
|
67
|
-
sessionId?: string;
|
|
68
|
-
launchId?: string;
|
|
69
|
-
self?: string;
|
|
70
|
-
hub?: string;
|
|
71
|
-
}): TaskRoute;
|
|
72
|
-
/**
|
|
73
|
-
* The daemon sandboxes screenshot writes to the browser runtime dir, so `-o`
|
|
74
|
-
* outside that dir is ignored there. The CLI process honors `-o` by copying
|
|
75
|
-
* the captured file to the requested path. Same path → no copy.
|
|
76
|
-
*/
|
|
77
|
-
export declare function honorScreenshotOutput(requested: string | undefined, daemonPath: string): string;
|
|
78
|
-
export {};
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local task → device index.
|
|
3
|
-
*
|
|
4
|
-
* `tasks.json` is per-profile and lives in that profile's cache dir on the
|
|
5
|
-
* machine running the browser daemon, so a later verb issued from a different
|
|
6
|
-
* box cannot see where task `post` lives. The machine that STARTS a task
|
|
7
|
-
* records the binding here — a small JSON file in THIS box's browser cache.
|
|
8
|
-
* No fleet state, no sync: the agent that opens a task is the agent that
|
|
9
|
-
* drives it.
|
|
10
|
-
*
|
|
11
|
-
* Kind is not stored. A missing name fails loud and lists the open tasks;
|
|
12
|
-
* it never guesses, never opens a new browser, never falls back to a default
|
|
13
|
-
* profile.
|
|
14
|
-
*/
|
|
15
|
-
import * as fs from 'node:fs';
|
|
16
|
-
import * as path from 'node:path';
|
|
17
|
-
import { getBrowserRuntimeDir } from '../state.js';
|
|
18
|
-
import { machineId } from '../machine-id.js';
|
|
19
|
-
export const REJECT_DEVICE_MESSAGE = '--device is only valid on `agents browser start`.\n' +
|
|
20
|
-
'The task is bound to a device at start; later verbs resolve it from --task.\n' +
|
|
21
|
-
'Next: agents browser start --task <name> --device <device>';
|
|
22
|
-
/**
|
|
23
|
-
* Verbs that CLOSE a browsing context. A cold (no bound task) close verb must
|
|
24
|
-
* never follow the fleet hub: with no task it would target a browsing context
|
|
25
|
-
* this session never opened, and on the hub could stop an unrelated task. They
|
|
26
|
-
* stay local unless an explicit `--task` or a local binding routes them. The
|
|
27
|
-
* driving/read verbs (navigate, screenshot, click, …) are safe to forward cold
|
|
28
|
-
* because the hub creates the caller's OWN task, keyed to the identity the
|
|
29
|
-
* dispatch now forwards.
|
|
30
|
-
*/
|
|
31
|
-
const TERMINAL_BROWSER_VERBS = new Set(['done', 'stop']);
|
|
32
|
-
export function isTerminalBrowserVerb(verb) {
|
|
33
|
-
return TERMINAL_BROWSER_VERBS.has(verb);
|
|
34
|
-
}
|
|
35
|
-
function taskIndexPath() {
|
|
36
|
-
return path.join(getBrowserRuntimeDir(), 'task-index.json');
|
|
37
|
-
}
|
|
38
|
-
export function readTaskIndex() {
|
|
39
|
-
const file = taskIndexPath();
|
|
40
|
-
if (!fs.existsSync(file))
|
|
41
|
-
return {};
|
|
42
|
-
let parsed;
|
|
43
|
-
try {
|
|
44
|
-
parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
throw new Error(`Cannot read browser task index at ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
-
}
|
|
49
|
-
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
50
|
-
throw new Error(`Cannot read browser task index at ${file}: document root must be a map.`);
|
|
51
|
-
}
|
|
52
|
-
return parsed;
|
|
53
|
-
}
|
|
54
|
-
function writeTaskIndex(index) {
|
|
55
|
-
const file = taskIndexPath();
|
|
56
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
57
|
-
const tmp = `${file}.${process.pid}.tmp`;
|
|
58
|
-
fs.writeFileSync(tmp, `${JSON.stringify(index, null, 2)}\n`);
|
|
59
|
-
fs.renameSync(tmp, file);
|
|
60
|
-
}
|
|
61
|
-
export function getTaskBinding(name) {
|
|
62
|
-
return readTaskIndex()[name];
|
|
63
|
-
}
|
|
64
|
-
export function listTaskBindings() {
|
|
65
|
-
return Object.entries(readTaskIndex())
|
|
66
|
-
.map(([name, binding]) => ({ name, ...binding }))
|
|
67
|
-
.sort((a, b) => a.createdAt - b.createdAt);
|
|
68
|
-
}
|
|
69
|
-
export function bindTask(name, binding) {
|
|
70
|
-
if (!name) {
|
|
71
|
-
throw new Error('Cannot bind a browser task with an empty name.');
|
|
72
|
-
}
|
|
73
|
-
if (!binding.device) {
|
|
74
|
-
throw new Error(`Cannot bind browser task "${name}" without a device.`);
|
|
75
|
-
}
|
|
76
|
-
const index = readTaskIndex();
|
|
77
|
-
index[name] = { ...binding };
|
|
78
|
-
writeTaskIndex(index);
|
|
79
|
-
}
|
|
80
|
-
export function updateTaskBinding(name, patch) {
|
|
81
|
-
const index = readTaskIndex();
|
|
82
|
-
const existing = index[name];
|
|
83
|
-
if (!existing)
|
|
84
|
-
return;
|
|
85
|
-
index[name] = { ...existing, ...patch };
|
|
86
|
-
writeTaskIndex(index);
|
|
87
|
-
}
|
|
88
|
-
export function unbindTask(name) {
|
|
89
|
-
const index = readTaskIndex();
|
|
90
|
-
if (!(name in index))
|
|
91
|
-
return;
|
|
92
|
-
delete index[name];
|
|
93
|
-
writeTaskIndex(index);
|
|
94
|
-
}
|
|
95
|
-
/** Drop every binding recorded under this profile (after `stop --profile` / gc). */
|
|
96
|
-
export function unbindTasksForProfile(profile) {
|
|
97
|
-
const index = readTaskIndex();
|
|
98
|
-
let changed = false;
|
|
99
|
-
for (const [name, binding] of Object.entries(index)) {
|
|
100
|
-
if (binding.profile === profile) {
|
|
101
|
-
delete index[name];
|
|
102
|
-
changed = true;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (changed)
|
|
106
|
-
writeTaskIndex(index);
|
|
107
|
-
}
|
|
108
|
-
export function tasksForCaller(sessionId, launchId) {
|
|
109
|
-
return listTaskBindings().filter((entry) => {
|
|
110
|
-
if (sessionId && entry.sessionId === sessionId)
|
|
111
|
-
return true;
|
|
112
|
-
if (launchId && entry.launchId === launchId)
|
|
113
|
-
return true;
|
|
114
|
-
return false;
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
function formatAge(createdAt, now = Date.now()) {
|
|
118
|
-
const ageMs = Math.max(0, now - createdAt);
|
|
119
|
-
if (ageMs < 60_000)
|
|
120
|
-
return `${Math.round(ageMs / 1000)}s`;
|
|
121
|
-
return `${Math.round(ageMs / 60_000)}m`;
|
|
122
|
-
}
|
|
123
|
-
export function formatOpenTaskList(entries, now = Date.now()) {
|
|
124
|
-
if (entries.length === 0)
|
|
125
|
-
return ' (no open tasks)';
|
|
126
|
-
return entries
|
|
127
|
-
.map((entry) => {
|
|
128
|
-
const url = entry.url && entry.url.length > 0 ? entry.url : '-';
|
|
129
|
-
return ` ${entry.name} url=${url} device=${entry.device} age=${formatAge(entry.createdAt, now)}`;
|
|
130
|
-
})
|
|
131
|
-
.join('\n');
|
|
132
|
-
}
|
|
133
|
-
export function unknownTaskMessage(name, entries = listTaskBindings()) {
|
|
134
|
-
return [
|
|
135
|
-
`Unknown browser task "${name}".`,
|
|
136
|
-
'Open tasks:',
|
|
137
|
-
formatOpenTaskList(entries),
|
|
138
|
-
'Next: agents browser start --task <name> [--device <device>]',
|
|
139
|
-
].join('\n');
|
|
140
|
-
}
|
|
141
|
-
export function ambiguousTasksMessage(entries) {
|
|
142
|
-
return [
|
|
143
|
-
'Multiple browser tasks for this session — pass --task <name>:',
|
|
144
|
-
formatOpenTaskList(entries),
|
|
145
|
-
'Next: agents browser status',
|
|
146
|
-
].join('\n');
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Decide where a page verb should run.
|
|
150
|
-
*
|
|
151
|
-
* `--device` on a later verb is always rejected (bind it at start).
|
|
152
|
-
* A named task that is not in the local index fails loud with the open-task
|
|
153
|
-
* list. Two or more tasks for this caller, with no `--task`, fail the same way.
|
|
154
|
-
* Zero matches with no `--task` follows the fleet browser hub when the caller
|
|
155
|
-
* passed one (`opts.hub` — the box drives `browser.device` by default), so a
|
|
156
|
-
* cold page verb reaches the logged-in hub browser exactly as a bare
|
|
157
|
-
* `agents browser start` already does; with no hub it proceeds locally and the
|
|
158
|
-
* daemon resolves from caller identity for the first implicit create. The caller
|
|
159
|
-
* (the CLI's task-routing hook) supplies `opts.hub` from `defaultBrowserHub()`,
|
|
160
|
-
* which is already undefined on the hub itself and on a fleet-remote re-exec, so
|
|
161
|
-
* this can never forward to self or loop.
|
|
162
|
-
*/
|
|
163
|
-
export function resolveTaskRoute(opts) {
|
|
164
|
-
if (opts.device) {
|
|
165
|
-
return { kind: 'reject-device', message: REJECT_DEVICE_MESSAGE };
|
|
166
|
-
}
|
|
167
|
-
const self = opts.self ?? machineId();
|
|
168
|
-
if (opts.task) {
|
|
169
|
-
const binding = getTaskBinding(opts.task);
|
|
170
|
-
if (!binding) {
|
|
171
|
-
return { kind: 'unknown', task: opts.task, message: unknownTaskMessage(opts.task) };
|
|
172
|
-
}
|
|
173
|
-
return { kind: 'proceed', task: opts.task, device: binding.device };
|
|
174
|
-
}
|
|
175
|
-
const matches = tasksForCaller(opts.sessionId, opts.launchId);
|
|
176
|
-
if (matches.length > 1) {
|
|
177
|
-
return { kind: 'ambiguous', message: ambiguousTasksMessage(matches) };
|
|
178
|
-
}
|
|
179
|
-
if (matches.length === 1) {
|
|
180
|
-
const only = matches[0];
|
|
181
|
-
return { kind: 'proceed', task: only.name, device: only.device };
|
|
182
|
-
}
|
|
183
|
-
return { kind: 'proceed', device: opts.hub ?? self };
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* The daemon sandboxes screenshot writes to the browser runtime dir, so `-o`
|
|
187
|
-
* outside that dir is ignored there. The CLI process honors `-o` by copying
|
|
188
|
-
* the captured file to the requested path. Same path → no copy.
|
|
189
|
-
*/
|
|
190
|
-
export function honorScreenshotOutput(requested, daemonPath) {
|
|
191
|
-
if (!requested)
|
|
192
|
-
return daemonPath;
|
|
193
|
-
const dest = path.resolve(requested);
|
|
194
|
-
if (path.resolve(daemonPath) === dest)
|
|
195
|
-
return dest;
|
|
196
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
197
|
-
fs.copyFileSync(daemonPath, dest);
|
|
198
|
-
return dest;
|
|
199
|
-
}
|