@phnx-labs/agents-cli 1.22.47 → 1.22.49
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 +401 -17
- package/README.md +66 -32
- package/dist/bootstrap.js +4 -2
- package/dist/cli/command-registry.d.ts +1 -0
- package/dist/cli/command-registry.js +2 -0
- package/dist/commands/accounts.d.ts +58 -0
- package/dist/commands/accounts.js +206 -32
- package/dist/commands/artifacts.js +7 -6
- package/dist/commands/attach.js +1 -1
- package/dist/commands/browser.js +501 -103
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/config.js +0 -20
- package/dist/commands/daemon.d.ts +21 -0
- package/dist/commands/daemon.js +147 -12
- package/dist/commands/exec.js +52 -15
- package/dist/commands/focus.d.ts +14 -0
- package/dist/commands/focus.js +48 -5
- package/dist/commands/go.d.ts +3 -0
- package/dist/commands/go.js +16 -3
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/lease.d.ts +1 -1
- package/dist/commands/lease.js +4 -3
- package/dist/commands/mailboxes.js +4 -3
- package/dist/commands/menubar.js +11 -4
- package/dist/commands/projects.d.ts +16 -1
- package/dist/commands/projects.js +58 -64
- package/dist/commands/resume.js +1 -1
- package/dist/commands/route.js +4 -4
- package/dist/commands/routines.js +3 -2
- package/dist/commands/run-broadcast.d.ts +1 -1
- package/dist/commands/run-broadcast.js +1 -1
- package/dist/commands/sessions-picker.d.ts +22 -0
- package/dist/commands/sessions-picker.js +48 -9
- package/dist/commands/sessions-trace.js +1 -1
- package/dist/commands/sessions.js +1 -0
- package/dist/commands/setup-secrets.js +23 -0
- package/dist/commands/share.d.ts +20 -5
- package/dist/commands/share.js +152 -52
- package/dist/commands/ssh.js +54 -7
- package/dist/commands/traces.d.ts +2 -0
- package/dist/commands/traces.js +184 -0
- package/dist/commands/view.d.ts +1 -1
- package/dist/commands/view.js +17 -34
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/lib/accounting/usage.d.ts +14 -3
- package/dist/lib/accounting/usage.js +64 -18
- package/dist/lib/agent-spec/agents.js +11 -9
- package/dist/lib/browser/drivers/ssh.d.ts +16 -2
- package/dist/lib/browser/drivers/ssh.js +36 -9
- package/dist/lib/browser/ipc.js +8 -3
- package/dist/lib/browser/profiles.d.ts +35 -101
- package/dist/lib/browser/profiles.js +130 -301
- package/dist/lib/browser/registry.d.ts +39 -0
- package/dist/lib/browser/registry.js +149 -0
- package/dist/lib/browser/remote-control.d.ts +8 -6
- package/dist/lib/browser/remote-control.js +8 -6
- package/dist/lib/browser/resolve-target.d.ts +68 -0
- package/dist/lib/browser/resolve-target.js +250 -0
- package/dist/lib/browser/runtime-state.d.ts +40 -16
- package/dist/lib/browser/runtime-state.js +92 -25
- package/dist/lib/browser/service.d.ts +28 -9
- package/dist/lib/browser/service.js +139 -62
- package/dist/lib/browser/task-index.d.ts +70 -0
- package/dist/lib/browser/task-index.js +180 -0
- package/dist/lib/byok-usage.js +2 -2
- package/dist/lib/claude-statusline.d.ts +29 -0
- package/dist/lib/claude-statusline.js +139 -0
- package/dist/lib/cloud/host.d.ts +1 -1
- package/dist/lib/cloud/host.js +1 -1
- package/dist/lib/cloud/rush.js +1 -1
- package/dist/lib/computer/computer-rpc.js +2 -2
- package/dist/lib/computer/download.d.ts +15 -15
- package/dist/lib/computer/download.js +35 -98
- package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
- package/dist/lib/computer/ssh-tunnel.js +12 -22
- package/dist/lib/config-keys.d.ts +2 -8
- package/dist/lib/config-keys.js +2 -13
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +2 -2
- package/dist/lib/crabbox/lease.d.ts +9 -0
- package/dist/lib/crabbox/lease.js +70 -15
- package/dist/lib/crabbox/runtimes.js +1 -1
- package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
- package/dist/lib/daemon/account-state-daemon-service.js +26 -0
- package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
- package/dist/lib/daemon/browser-ipc-service.js +46 -0
- package/dist/lib/daemon/daemon.d.ts +3 -0
- package/dist/lib/daemon/daemon.js +212 -444
- package/dist/lib/daemon/device-probe-service.d.ts +26 -0
- package/dist/lib/daemon/device-probe-service.js +47 -0
- package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
- package/dist/lib/daemon/keychain-reap-service.js +32 -0
- package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
- package/dist/lib/daemon/monitor-engine-service.js +29 -0
- package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
- package/dist/lib/daemon/secrets-broker-service.js +66 -0
- package/dist/lib/daemon/self-heal-service.d.ts +27 -0
- package/dist/lib/daemon/self-heal-service.js +48 -0
- package/dist/lib/daemon/service.d.ts +102 -0
- package/dist/lib/daemon/service.js +98 -0
- package/dist/lib/daemon/session-index-service.d.ts +22 -0
- package/dist/lib/daemon/session-index-service.js +40 -0
- package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
- package/dist/lib/daemon/state-dir-check-service.js +63 -0
- package/dist/lib/daemon/supervisor.d.ts +87 -0
- package/dist/lib/daemon/supervisor.js +298 -0
- package/dist/lib/daemon/watchdog-service.d.ts +19 -0
- package/dist/lib/daemon/watchdog-service.js +40 -0
- package/dist/lib/daemon-health.d.ts +15 -0
- package/dist/lib/daemon-health.js +29 -5
- package/dist/lib/daemon-services.d.ts +8 -1
- package/dist/lib/daemon-services.js +55 -0
- package/dist/lib/daemon-ticks.d.ts +13 -2
- package/dist/lib/daemon-ticks.js +20 -18
- package/dist/lib/device-config.d.ts +0 -2
- package/dist/lib/device-config.js +1 -23
- package/dist/lib/devices/registry.d.ts +1 -1
- package/dist/lib/devices/registry.js +1 -1
- package/dist/lib/devices/stats-cache.d.ts +20 -0
- package/dist/lib/devices/stats-cache.js +49 -5
- package/dist/lib/devices/worker-pick.d.ts +48 -0
- package/dist/lib/devices/worker-pick.js +113 -0
- package/dist/lib/exec.d.ts +67 -13
- package/dist/lib/exec.js +85 -29
- package/dist/lib/harness/adapters/cursor.js +8 -11
- package/dist/lib/helper-download.d.ts +108 -0
- package/dist/lib/helper-download.js +194 -0
- package/dist/lib/helper-versions.d.ts +52 -0
- package/dist/lib/helper-versions.js +58 -0
- package/dist/lib/hooks/cache.js +11 -0
- package/dist/lib/hooks/install.js +2 -2
- package/dist/lib/hosts/credentials.d.ts +1 -1
- package/dist/lib/hosts/credentials.js +1 -1
- package/dist/lib/hosts/dispatch.d.ts +7 -1
- package/dist/lib/hosts/dispatch.js +24 -5
- package/dist/lib/hosts/passthrough.d.ts +8 -15
- package/dist/lib/hosts/passthrough.js +19 -16
- package/dist/lib/hosts/providers/devices.d.ts +1 -1
- package/dist/lib/hosts/providers/devices.js +1 -1
- package/dist/lib/hosts/reconnect.d.ts +179 -21
- package/dist/lib/hosts/reconnect.js +266 -45
- package/dist/lib/installations/migrate.d.ts +4 -5
- package/dist/lib/installations/migrate.js +6 -7
- package/dist/lib/installations/shims.d.ts +1 -1
- package/dist/lib/installations/shims.js +23 -13
- package/dist/lib/installations/versions.js +25 -2
- package/dist/lib/linear-autoclose.d.ts +1 -1
- package/dist/lib/linear-autoclose.js +1 -1
- package/dist/lib/loop.d.ts +2 -2
- package/dist/lib/loop.js +1 -1
- package/dist/lib/menubar/download-menubar.d.ts +47 -0
- package/dist/lib/menubar/download-menubar.js +60 -0
- package/dist/lib/menubar/install-menubar.d.ts +31 -7
- package/dist/lib/menubar/install-menubar.js +79 -21
- package/dist/lib/menubar/notify-desktop.d.ts +3 -3
- package/dist/lib/menubar/notify-desktop.js +6 -6
- package/dist/lib/model-tiers.d.ts +1 -1
- package/dist/lib/project-probe.d.ts +1 -1
- package/dist/lib/project-probe.js +1 -1
- package/dist/lib/project-pull.d.ts +1 -1
- package/dist/lib/project-pull.js +2 -2
- package/dist/lib/projects.d.ts +1 -1
- package/dist/lib/projects.js +1 -1
- package/dist/lib/sandbox.js +6 -7
- package/dist/lib/secrets/download-keychain.d.ts +47 -0
- package/dist/lib/secrets/download-keychain.js +70 -0
- package/dist/lib/secrets/install-helper.d.ts +20 -1
- package/dist/lib/secrets/install-helper.js +44 -3
- package/dist/lib/session/active.js +17 -1
- package/dist/lib/session/db.d.ts +11 -1
- package/dist/lib/session/db.js +138 -6
- package/dist/lib/session/discover.d.ts +21 -1
- package/dist/lib/session/discover.js +33 -5
- package/dist/lib/session/highlights.d.ts +49 -1
- package/dist/lib/session/highlights.js +95 -0
- package/dist/lib/session/host-link.d.ts +16 -2
- package/dist/lib/session/host-link.js +7 -1
- package/dist/lib/session/parse.js +33 -1
- package/dist/lib/session/remote/remote-list.d.ts +9 -0
- package/dist/lib/session/remote/remote-list.js +19 -2
- package/dist/lib/session/remote/watch.js +11 -2
- package/dist/lib/session/session-cache.d.ts +53 -0
- package/dist/lib/session/session-cache.js +88 -0
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/tool-calls.d.ts +2 -0
- package/dist/lib/session/tool-calls.js +1 -1
- package/dist/lib/session/trajectory-html.d.ts +5 -1
- package/dist/lib/session/trajectory-html.js +42 -22
- package/dist/lib/session/trajectory-text.js +9 -2
- package/dist/lib/session/trajectory.js +53 -4
- package/dist/lib/session/types.d.ts +15 -0
- package/dist/lib/sha256-asset.d.ts +26 -0
- package/dist/lib/sha256-asset.js +41 -0
- package/dist/lib/share/backend.d.ts +95 -0
- package/dist/lib/share/backend.js +139 -0
- package/dist/lib/share/delete.d.ts +6 -2
- package/dist/lib/share/delete.js +8 -9
- package/dist/lib/share/html.d.ts +22 -0
- package/dist/lib/share/html.js +88 -0
- package/dist/lib/share/provision.d.ts +18 -4
- package/dist/lib/share/provision.js +38 -4
- package/dist/lib/share/publish.d.ts +39 -15
- package/dist/lib/share/publish.js +55 -26
- package/dist/lib/share/worker-template.js +171 -11
- package/dist/lib/sqlite.js +16 -3
- package/dist/lib/ssh-exec.d.ts +65 -0
- package/dist/lib/ssh-exec.js +120 -4
- package/dist/lib/startup/command-registry.js +1 -1
- package/dist/lib/startup/dev-build.js +3 -3
- package/dist/lib/state.d.ts +1 -1
- package/dist/lib/state.js +7 -3
- package/dist/lib/subagents-registry.js +1 -1
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/types.d.ts +1 -1
- package/dist/lib/terminal/types.js +1 -1
- package/dist/lib/tmux/orphan-reap.js +1 -1
- package/dist/lib/tmux/session.js +31 -0
- package/dist/lib/traces/backend.d.ts +29 -0
- package/dist/lib/traces/backend.js +46 -0
- package/dist/lib/traces/classify.d.ts +52 -0
- package/dist/lib/traces/classify.js +72 -0
- package/dist/lib/traces/config.d.ts +10 -0
- package/dist/lib/traces/config.js +2 -0
- package/dist/lib/traces/provision.d.ts +19 -0
- package/dist/lib/traces/provision.js +30 -0
- package/dist/lib/traces/sync.d.ts +150 -0
- package/dist/lib/traces/sync.js +447 -0
- package/dist/lib/traces/worker-template.d.ts +4 -0
- package/dist/lib/traces/worker-template.js +159 -0
- package/dist/lib/types.d.ts +18 -19
- package/dist/lib/types.js +14 -0
- package/dist/lib/usage-refresh.d.ts +1 -1
- package/dist/lib/usage-refresh.js +11 -8
- package/dist/lib/workflows.d.ts +1 -1
- package/package.json +4 -6
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -26
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -135
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
- package/dist/lib/usage-fleet.d.ts +0 -32
- package/dist/lib/usage-fleet.js +0 -125
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export interface TaskBinding {
|
|
2
|
+
device: string;
|
|
3
|
+
profile?: string;
|
|
4
|
+
url?: string;
|
|
5
|
+
sessionId?: string;
|
|
6
|
+
launchId?: string;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
}
|
|
9
|
+
export type TaskIndex = Record<string, TaskBinding>;
|
|
10
|
+
export declare const REJECT_DEVICE_MESSAGE: string;
|
|
11
|
+
export 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 taskIndexPath(): string;
|
|
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` proceeds locally — the daemon still resolves
|
|
56
|
+
* from caller identity for the first implicit create.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveTaskRoute(opts: {
|
|
59
|
+
task?: string;
|
|
60
|
+
device?: string;
|
|
61
|
+
sessionId?: string;
|
|
62
|
+
launchId?: string;
|
|
63
|
+
self?: string;
|
|
64
|
+
}): TaskRoute;
|
|
65
|
+
/**
|
|
66
|
+
* The daemon sandboxes screenshot writes to the browser runtime dir, so `-o`
|
|
67
|
+
* outside that dir is ignored there. The CLI process honors `-o` by copying
|
|
68
|
+
* the captured file to the requested path. Same path → no copy.
|
|
69
|
+
*/
|
|
70
|
+
export declare function honorScreenshotOutput(requested: string | undefined, daemonPath: string): string;
|
|
@@ -0,0 +1,180 @@
|
|
|
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
|
+
export function taskIndexPath() {
|
|
23
|
+
return path.join(getBrowserRuntimeDir(), 'task-index.json');
|
|
24
|
+
}
|
|
25
|
+
export function readTaskIndex() {
|
|
26
|
+
const file = taskIndexPath();
|
|
27
|
+
if (!fs.existsSync(file))
|
|
28
|
+
return {};
|
|
29
|
+
let parsed;
|
|
30
|
+
try {
|
|
31
|
+
parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw new Error(`Cannot read browser task index at ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
35
|
+
}
|
|
36
|
+
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
37
|
+
throw new Error(`Cannot read browser task index at ${file}: document root must be a map.`);
|
|
38
|
+
}
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
function writeTaskIndex(index) {
|
|
42
|
+
const file = taskIndexPath();
|
|
43
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
44
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
45
|
+
fs.writeFileSync(tmp, `${JSON.stringify(index, null, 2)}\n`);
|
|
46
|
+
fs.renameSync(tmp, file);
|
|
47
|
+
}
|
|
48
|
+
export function getTaskBinding(name) {
|
|
49
|
+
return readTaskIndex()[name];
|
|
50
|
+
}
|
|
51
|
+
export function listTaskBindings() {
|
|
52
|
+
return Object.entries(readTaskIndex())
|
|
53
|
+
.map(([name, binding]) => ({ name, ...binding }))
|
|
54
|
+
.sort((a, b) => a.createdAt - b.createdAt);
|
|
55
|
+
}
|
|
56
|
+
export function bindTask(name, binding) {
|
|
57
|
+
if (!name) {
|
|
58
|
+
throw new Error('Cannot bind a browser task with an empty name.');
|
|
59
|
+
}
|
|
60
|
+
if (!binding.device) {
|
|
61
|
+
throw new Error(`Cannot bind browser task "${name}" without a device.`);
|
|
62
|
+
}
|
|
63
|
+
const index = readTaskIndex();
|
|
64
|
+
index[name] = { ...binding };
|
|
65
|
+
writeTaskIndex(index);
|
|
66
|
+
}
|
|
67
|
+
export function updateTaskBinding(name, patch) {
|
|
68
|
+
const index = readTaskIndex();
|
|
69
|
+
const existing = index[name];
|
|
70
|
+
if (!existing)
|
|
71
|
+
return;
|
|
72
|
+
index[name] = { ...existing, ...patch };
|
|
73
|
+
writeTaskIndex(index);
|
|
74
|
+
}
|
|
75
|
+
export function unbindTask(name) {
|
|
76
|
+
const index = readTaskIndex();
|
|
77
|
+
if (!(name in index))
|
|
78
|
+
return;
|
|
79
|
+
delete index[name];
|
|
80
|
+
writeTaskIndex(index);
|
|
81
|
+
}
|
|
82
|
+
/** Drop every binding recorded under this profile (after `stop --profile` / gc). */
|
|
83
|
+
export function unbindTasksForProfile(profile) {
|
|
84
|
+
const index = readTaskIndex();
|
|
85
|
+
let changed = false;
|
|
86
|
+
for (const [name, binding] of Object.entries(index)) {
|
|
87
|
+
if (binding.profile === profile) {
|
|
88
|
+
delete index[name];
|
|
89
|
+
changed = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (changed)
|
|
93
|
+
writeTaskIndex(index);
|
|
94
|
+
}
|
|
95
|
+
export function tasksForCaller(sessionId, launchId) {
|
|
96
|
+
return listTaskBindings().filter((entry) => {
|
|
97
|
+
if (sessionId && entry.sessionId === sessionId)
|
|
98
|
+
return true;
|
|
99
|
+
if (launchId && entry.launchId === launchId)
|
|
100
|
+
return true;
|
|
101
|
+
return false;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function formatAge(createdAt, now = Date.now()) {
|
|
105
|
+
const ageMs = Math.max(0, now - createdAt);
|
|
106
|
+
if (ageMs < 60_000)
|
|
107
|
+
return `${Math.round(ageMs / 1000)}s`;
|
|
108
|
+
return `${Math.round(ageMs / 60_000)}m`;
|
|
109
|
+
}
|
|
110
|
+
export function formatOpenTaskList(entries, now = Date.now()) {
|
|
111
|
+
if (entries.length === 0)
|
|
112
|
+
return ' (no open tasks)';
|
|
113
|
+
return entries
|
|
114
|
+
.map((entry) => {
|
|
115
|
+
const url = entry.url && entry.url.length > 0 ? entry.url : '-';
|
|
116
|
+
return ` ${entry.name} url=${url} device=${entry.device} age=${formatAge(entry.createdAt, now)}`;
|
|
117
|
+
})
|
|
118
|
+
.join('\n');
|
|
119
|
+
}
|
|
120
|
+
export function unknownTaskMessage(name, entries = listTaskBindings()) {
|
|
121
|
+
return [
|
|
122
|
+
`Unknown browser task "${name}".`,
|
|
123
|
+
'Open tasks:',
|
|
124
|
+
formatOpenTaskList(entries),
|
|
125
|
+
'Next: agents browser start --task <name> [--device <device>]',
|
|
126
|
+
].join('\n');
|
|
127
|
+
}
|
|
128
|
+
export function ambiguousTasksMessage(entries) {
|
|
129
|
+
return [
|
|
130
|
+
'Multiple browser tasks for this session — pass --task <name>:',
|
|
131
|
+
formatOpenTaskList(entries),
|
|
132
|
+
'Next: agents browser status',
|
|
133
|
+
].join('\n');
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Decide where a page verb should run.
|
|
137
|
+
*
|
|
138
|
+
* `--device` on a later verb is always rejected (bind it at start).
|
|
139
|
+
* A named task that is not in the local index fails loud with the open-task
|
|
140
|
+
* list. Two or more tasks for this caller, with no `--task`, fail the same way.
|
|
141
|
+
* Zero matches with no `--task` proceeds locally — the daemon still resolves
|
|
142
|
+
* from caller identity for the first implicit create.
|
|
143
|
+
*/
|
|
144
|
+
export function resolveTaskRoute(opts) {
|
|
145
|
+
if (opts.device) {
|
|
146
|
+
return { kind: 'reject-device', message: REJECT_DEVICE_MESSAGE };
|
|
147
|
+
}
|
|
148
|
+
const self = opts.self ?? machineId();
|
|
149
|
+
if (opts.task) {
|
|
150
|
+
const binding = getTaskBinding(opts.task);
|
|
151
|
+
if (!binding) {
|
|
152
|
+
return { kind: 'unknown', task: opts.task, message: unknownTaskMessage(opts.task) };
|
|
153
|
+
}
|
|
154
|
+
return { kind: 'proceed', task: opts.task, device: binding.device };
|
|
155
|
+
}
|
|
156
|
+
const matches = tasksForCaller(opts.sessionId, opts.launchId);
|
|
157
|
+
if (matches.length > 1) {
|
|
158
|
+
return { kind: 'ambiguous', message: ambiguousTasksMessage(matches) };
|
|
159
|
+
}
|
|
160
|
+
if (matches.length === 1) {
|
|
161
|
+
const only = matches[0];
|
|
162
|
+
return { kind: 'proceed', task: only.name, device: only.device };
|
|
163
|
+
}
|
|
164
|
+
return { kind: 'proceed', device: self };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The daemon sandboxes screenshot writes to the browser runtime dir, so `-o`
|
|
168
|
+
* outside that dir is ignored there. The CLI process honors `-o` by copying
|
|
169
|
+
* the captured file to the requested path. Same path → no copy.
|
|
170
|
+
*/
|
|
171
|
+
export function honorScreenshotOutput(requested, daemonPath) {
|
|
172
|
+
if (!requested)
|
|
173
|
+
return daemonPath;
|
|
174
|
+
const dest = path.resolve(requested);
|
|
175
|
+
if (path.resolve(daemonPath) === dest)
|
|
176
|
+
return dest;
|
|
177
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
178
|
+
fs.copyFileSync(daemonPath, dest);
|
|
179
|
+
return dest;
|
|
180
|
+
}
|
package/dist/lib/byok-usage.js
CHANGED
|
@@ -150,12 +150,12 @@ export function renderByokBar(result) {
|
|
|
150
150
|
return '';
|
|
151
151
|
const { budget } = result;
|
|
152
152
|
if (budget.limitUsd === null) {
|
|
153
|
-
const bar = renderBar(0, 10
|
|
153
|
+
const bar = renderBar(0, 10);
|
|
154
154
|
const credit = budget.remainingUsd !== null ? `$${budget.remainingUsd.toFixed(2)} credit, ` : '';
|
|
155
155
|
return `$: ${bar} ${credit}$${budget.usedUsd.toFixed(2)} used (no spending limit)`;
|
|
156
156
|
}
|
|
157
157
|
const pct = budget.usedPercent ?? 0;
|
|
158
|
-
const bar = renderBar(pct, 10
|
|
158
|
+
const bar = renderBar(pct, 10);
|
|
159
159
|
const color = getUsageColor(pct);
|
|
160
160
|
const remaining = budget.remainingUsd !== null ? `$${budget.remainingUsd.toFixed(2)}` : '?';
|
|
161
161
|
const limit = `$${budget.limitUsd.toFixed(2)}`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const CLAUDE_STATUSLINE_COMMAND = "agents __claude-statusline";
|
|
2
|
+
interface ClaudeStatusLinePayload {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
workspace?: {
|
|
5
|
+
current_dir?: string;
|
|
6
|
+
};
|
|
7
|
+
model?: {
|
|
8
|
+
display_name?: string;
|
|
9
|
+
id?: string;
|
|
10
|
+
};
|
|
11
|
+
rate_limits?: {
|
|
12
|
+
five_hour?: {
|
|
13
|
+
used_percentage?: number;
|
|
14
|
+
resets_at?: number;
|
|
15
|
+
};
|
|
16
|
+
seven_day?: {
|
|
17
|
+
used_percentage?: number;
|
|
18
|
+
resets_at?: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare function ingestClaudeStatusLineUsage(payload: ClaudeStatusLinePayload, versionHome: string): boolean;
|
|
23
|
+
export declare function renderClaudeStatusLine(payload: ClaudeStatusLinePayload, host?: string, delegated?: string): string;
|
|
24
|
+
export declare function runClaudeStatusLine(): Promise<number>;
|
|
25
|
+
export declare function installClaudeStatusLine(versionHome: string): {
|
|
26
|
+
changed: boolean;
|
|
27
|
+
error?: string;
|
|
28
|
+
};
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { spawnSync } from 'child_process';
|
|
5
|
+
import { readClaudeHomeConfig } from './agent-spec/agents.js';
|
|
6
|
+
import { atomicWriteFileSync } from './fs-atomic.js';
|
|
7
|
+
import { mergeClaudeUsageCacheWindows } from './accounting/usage.js';
|
|
8
|
+
export const CLAUDE_STATUSLINE_COMMAND = 'agents __claude-statusline';
|
|
9
|
+
const DELEGATE_FILE = path.join('.agents', 'claude-statusline-delegate');
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
function versionHomeFromEnv(env) {
|
|
14
|
+
const configDir = env.CLAUDE_CONFIG_DIR?.trim();
|
|
15
|
+
return configDir ? path.dirname(configDir) : null;
|
|
16
|
+
}
|
|
17
|
+
function windowFromNative(key, value) {
|
|
18
|
+
if (!value || !Number.isFinite(value.used_percentage))
|
|
19
|
+
return null;
|
|
20
|
+
const resetSeconds = value.resets_at;
|
|
21
|
+
return {
|
|
22
|
+
key,
|
|
23
|
+
label: key === 'session' ? 'Current session' : 'Current week',
|
|
24
|
+
shortLabel: key === 'session' ? 'S' : 'W',
|
|
25
|
+
usedPercent: Math.max(0, Math.min(100, value.used_percentage)),
|
|
26
|
+
resetsAt: Number.isFinite(resetSeconds) ? new Date(resetSeconds * 1000) : null,
|
|
27
|
+
windowMinutes: key === 'session' ? 300 : 10_080,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function ingestClaudeStatusLineUsage(payload, versionHome) {
|
|
31
|
+
const identity = readClaudeHomeConfig(versionHome)?.identity;
|
|
32
|
+
if (!identity?.usageKey || !payload.rate_limits)
|
|
33
|
+
return false;
|
|
34
|
+
const windows = [
|
|
35
|
+
windowFromNative('session', payload.rate_limits.five_hour),
|
|
36
|
+
windowFromNative('week', payload.rate_limits.seven_day),
|
|
37
|
+
].filter((value) => value !== null);
|
|
38
|
+
if (windows.length === 0)
|
|
39
|
+
return false;
|
|
40
|
+
mergeClaudeUsageCacheWindows(identity.usageKey, {
|
|
41
|
+
source: 'live',
|
|
42
|
+
sourceLabel: 'Claude response rate limits',
|
|
43
|
+
capturedAt: new Date(),
|
|
44
|
+
windows,
|
|
45
|
+
});
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
function delegatePath(versionHome) {
|
|
49
|
+
return path.join(versionHome, DELEGATE_FILE);
|
|
50
|
+
}
|
|
51
|
+
function renderDelegate(payload, versionHome) {
|
|
52
|
+
let command = '';
|
|
53
|
+
try {
|
|
54
|
+
command = fs.readFileSync(delegatePath(versionHome), 'utf8').trim();
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
if (!command || command === CLAUDE_STATUSLINE_COMMAND)
|
|
60
|
+
return '';
|
|
61
|
+
const result = spawnSync(command, {
|
|
62
|
+
shell: true,
|
|
63
|
+
input: payload,
|
|
64
|
+
encoding: 'utf8',
|
|
65
|
+
env: process.env,
|
|
66
|
+
});
|
|
67
|
+
return result.status === 0 ? result.stdout.trim() : '';
|
|
68
|
+
}
|
|
69
|
+
export function renderClaudeStatusLine(payload, host = os.hostname().split('.')[0] || os.hostname(), delegated = '') {
|
|
70
|
+
const model = payload.model?.display_name?.trim() || payload.model?.id?.trim() || 'model pending';
|
|
71
|
+
const parts = [host, model];
|
|
72
|
+
if (delegated)
|
|
73
|
+
parts.push(delegated);
|
|
74
|
+
const fiveHour = payload.rate_limits?.five_hour?.used_percentage;
|
|
75
|
+
const sevenDay = payload.rate_limits?.seven_day?.used_percentage;
|
|
76
|
+
if (Number.isFinite(fiveHour))
|
|
77
|
+
parts.push(`5h ${Math.round(fiveHour)}%`);
|
|
78
|
+
if (Number.isFinite(sevenDay))
|
|
79
|
+
parts.push(`7d ${Math.round(sevenDay)}%`);
|
|
80
|
+
return parts.join(' · ');
|
|
81
|
+
}
|
|
82
|
+
export async function runClaudeStatusLine() {
|
|
83
|
+
const raw = await new Promise((resolve, reject) => {
|
|
84
|
+
let input = '';
|
|
85
|
+
process.stdin.setEncoding('utf8');
|
|
86
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
87
|
+
process.stdin.on('end', () => resolve(input));
|
|
88
|
+
process.stdin.on('error', reject);
|
|
89
|
+
});
|
|
90
|
+
let payload;
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(raw);
|
|
93
|
+
payload = isRecord(parsed) ? parsed : {};
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
payload = {};
|
|
97
|
+
}
|
|
98
|
+
const versionHome = versionHomeFromEnv(process.env);
|
|
99
|
+
if (versionHome)
|
|
100
|
+
ingestClaudeStatusLineUsage(payload, versionHome);
|
|
101
|
+
process.stdout.write(renderClaudeStatusLine(payload, undefined, versionHome ? renderDelegate(raw, versionHome) : ''));
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
export function installClaudeStatusLine(versionHome) {
|
|
105
|
+
const settingsPath = path.join(versionHome, '.claude', 'settings.json');
|
|
106
|
+
try {
|
|
107
|
+
let settings = {};
|
|
108
|
+
if (fs.existsSync(settingsPath)) {
|
|
109
|
+
const parsed = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
110
|
+
if (!isRecord(parsed))
|
|
111
|
+
return { changed: false, error: 'settings.json is not an object' };
|
|
112
|
+
settings = parsed;
|
|
113
|
+
}
|
|
114
|
+
const priorStatusLine = isRecord(settings.statusLine) ? settings.statusLine : {};
|
|
115
|
+
const existing = typeof priorStatusLine.command === 'string'
|
|
116
|
+
? priorStatusLine.command.trim()
|
|
117
|
+
: '';
|
|
118
|
+
if (existing === CLAUDE_STATUSLINE_COMMAND)
|
|
119
|
+
return { changed: false };
|
|
120
|
+
if (existing) {
|
|
121
|
+
fs.mkdirSync(path.dirname(delegatePath(versionHome)), { recursive: true });
|
|
122
|
+
atomicWriteFileSync(delegatePath(versionHome), `${existing}\n`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
fs.rmSync(delegatePath(versionHome), { force: true });
|
|
126
|
+
}
|
|
127
|
+
settings.statusLine = {
|
|
128
|
+
...priorStatusLine,
|
|
129
|
+
type: 'command',
|
|
130
|
+
command: CLAUDE_STATUSLINE_COMMAND,
|
|
131
|
+
};
|
|
132
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
133
|
+
atomicWriteFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
|
|
134
|
+
return { changed: true };
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
return { changed: false, error: error instanceof Error ? error.message : String(error) };
|
|
138
|
+
}
|
|
139
|
+
}
|
package/dist/lib/cloud/host.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* launch as `agents run --device`, and the resulting task shows up in BOTH
|
|
7
7
|
* `agents cloud ps` and `agents devices ps` — one store (the host-task sidecars
|
|
8
8
|
* under ~/.agents/.cache/hosts/), two views. No new transport, no relay: SSH
|
|
9
|
-
* is the only wire, exactly like the rest of the
|
|
9
|
+
* is the only wire, exactly like the rest of the host design (docs/fleet.md).
|
|
10
10
|
*
|
|
11
11
|
* Status semantics follow reconcile.ts's prime rule: completion is only ever
|
|
12
12
|
* CONFIRMED from the remote `.exit` file — an unreachable host leaves a task
|
package/dist/lib/cloud/host.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* launch as `agents run --device`, and the resulting task shows up in BOTH
|
|
7
7
|
* `agents cloud ps` and `agents devices ps` — one store (the host-task sidecars
|
|
8
8
|
* under ~/.agents/.cache/hosts/), two views. No new transport, no relay: SSH
|
|
9
|
-
* is the only wire, exactly like the rest of the
|
|
9
|
+
* is the only wire, exactly like the rest of the host design (docs/fleet.md).
|
|
10
10
|
*
|
|
11
11
|
* Status semantics follow reconcile.ts's prime rule: completion is only ever
|
|
12
12
|
* CONFIRMED from the remote `.exit` file — an unreachable host leaves a task
|
package/dist/lib/cloud/rush.js
CHANGED
|
@@ -290,7 +290,7 @@ export class RushCloudProvider {
|
|
|
290
290
|
`placement does not securely inject them yet. Create and sync one with:`,
|
|
291
291
|
` agents accounts add <name> --provider anthropic --auth api-key # or: --auth setup-token`,
|
|
292
292
|
` agents accounts sync <name> <device>`,
|
|
293
|
-
`then run locally or on that device with --account <name>. See docs/
|
|
293
|
+
`then run locally or on that device with --account <name>. See docs/secrets.md (SING-1b).`,
|
|
294
294
|
].join('\n'));
|
|
295
295
|
}
|
|
296
296
|
}
|
|
@@ -174,8 +174,8 @@ export function writeComputerPeers(allowedExecPaths) {
|
|
|
174
174
|
export function resolveHelperExec() {
|
|
175
175
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
176
176
|
const candidates = [
|
|
177
|
-
// Local build (running from the agents-cli checkout).
|
|
178
|
-
path.resolve(here, '..', '..', '..', '..', '
|
|
177
|
+
// Local build (running from the agents-cli checkout). cli/dist/lib/computer -> repo root (4 up) -> native/computer-mac.
|
|
178
|
+
path.resolve(here, '..', '..', '..', '..', 'native', 'computer-mac', 'dist', 'ComputerHelper.app', 'Contents', 'MacOS', 'ComputerHelper'),
|
|
179
179
|
// Bundled with the npm package (dist/lib/computer -> dist/).
|
|
180
180
|
path.resolve(here, '..', '..', 'computer-helper', 'ComputerHelper.app', 'Contents', 'MacOS', 'ComputerHelper'),
|
|
181
181
|
];
|
|
@@ -3,25 +3,28 @@
|
|
|
3
3
|
* ("ComputerHelper.app").
|
|
4
4
|
*
|
|
5
5
|
* The helper is a signed + notarized universal `.app` bundle published as a
|
|
6
|
-
* GitHub release asset
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* GitHub release asset on its own `computer-mac/v<x.y.z>` tag (see
|
|
7
|
+
* `helper-versions.ts`). The Windows helper (`lib/computer/ssh-tunnel.ts`) does
|
|
8
|
+
* NOT yet follow this model — it still keys its exe to the CLI's own tag.
|
|
9
|
+
*
|
|
10
|
+
* A fresh `npm i -g` machine has no local build, so `agents computer setup` / `agents setup computer` fetch the
|
|
11
|
+
* asset for the pinned helper version, verify its sha256 against the published
|
|
10
12
|
* `.sha256`, then verify the code signature (Developer ID Team + notarization)
|
|
11
13
|
* before it is ever copied to /Applications.
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
+
* The download + verify machinery is shared with the menu-bar helper in
|
|
16
|
+
* `../helper-download.ts`; this file is the ComputerHelper spec + its wrappers,
|
|
17
|
+
* keeping the historical export surface (`verifyMacHelper`, `downloadMacHelperApp`,
|
|
18
|
+
* `ensureMacHelperApp`, …) stable for existing callers. ComputerHelper does NOT
|
|
19
|
+
* pin a designated requirement (no `expectedBundleId`), matching its verification
|
|
20
|
+
* strength before the shared primitive existed.
|
|
15
21
|
*/
|
|
16
|
-
|
|
17
|
-
export
|
|
22
|
+
import { EXPECTED_TEAM_ID, HELPER_RELEASE_REPO, parseTeamId } from '../helper-download.js';
|
|
23
|
+
export { EXPECTED_TEAM_ID, HELPER_RELEASE_REPO, parseTeamId };
|
|
18
24
|
/** The zipped `.app` release asset name. */
|
|
19
25
|
export declare const MAC_HELPER_ASSET = "ComputerHelper.app.zip";
|
|
20
26
|
/** The bundle directory name once extracted. */
|
|
21
27
|
export declare const MAC_HELPER_APP_NAME = "ComputerHelper.app";
|
|
22
|
-
/** Apple Developer ID Team the helper must be signed by (defense in depth on top
|
|
23
|
-
* of `spctl` notarization assessment). "Developer ID Application: Muqit Nawaz". */
|
|
24
|
-
export declare const EXPECTED_TEAM_ID = "2HTP252L87";
|
|
25
28
|
/** Cache dir for the downloaded helper, one subdir per release tag. */
|
|
26
29
|
export declare function macHelperCacheDir(version: string): string;
|
|
27
30
|
/** Release-asset URLs for the helper zip + its checksum at one `v<version>` tag. */
|
|
@@ -29,9 +32,6 @@ export declare function macHelperAssetUrls(version: string): {
|
|
|
29
32
|
zip: string;
|
|
30
33
|
sha256: string;
|
|
31
34
|
};
|
|
32
|
-
/** Extract `TeamIdentifier=XXXX` from `codesign -dv --verbose=4` output (which is
|
|
33
|
-
* emitted on stderr). Returns null when absent (ad-hoc / unsigned). */
|
|
34
|
-
export declare function parseTeamId(codesignInfo: string): string | null;
|
|
35
35
|
/**
|
|
36
36
|
* Verify a helper `.app` bundle is intact, signed by the expected Developer ID
|
|
37
37
|
* Team, and notarized (Gatekeeper-accepted). Throws with an actionable message
|
|
@@ -48,7 +48,7 @@ export declare function downloadMacHelperApp(version: string): Promise<string>;
|
|
|
48
48
|
/**
|
|
49
49
|
* Resolve the helper `.app` to install from: a local build / bundled copy first
|
|
50
50
|
* (repo checkout), else the checksum + signature-verified release-asset download
|
|
51
|
-
* for the
|
|
51
|
+
* for the pinned helper version. Throws with the tag it checked when neither
|
|
52
52
|
* exists. macOS only.
|
|
53
53
|
*/
|
|
54
54
|
export declare function ensureMacHelperApp(version?: string): Promise<string>;
|