@phnx-labs/agents-cli 1.22.46 → 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 +663 -0
- package/LICENSE +102 -182
- package/README.md +96 -49
- package/dist/bootstrap.js +4 -2
- package/dist/cli/command-registry.d.ts +2 -1
- package/dist/cli/command-registry.js +6 -2
- package/dist/commands/accounts.d.ts +58 -0
- package/dist/commands/accounts.js +243 -25
- package/dist/commands/artifacts.js +7 -6
- package/dist/commands/attach.js +1 -1
- package/dist/commands/auth.js +7 -5
- package/dist/commands/browser-sessions-picker.js +9 -7
- package/dist/commands/browser.js +711 -62
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/config.js +26 -24
- package/dist/commands/cost.d.ts +1 -1
- package/dist/commands/daemon.d.ts +21 -0
- package/dist/commands/daemon.js +147 -12
- package/dist/commands/detach.d.ts +13 -0
- package/dist/commands/detach.js +11 -8
- package/dist/commands/exec.js +107 -53
- package/dist/commands/feed-watch.d.ts +2 -0
- package/dist/commands/feed-watch.js +28 -0
- package/dist/commands/feed.js +27 -0
- package/dist/commands/feedback.js +3 -19
- 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/insights.d.ts +1 -1
- package/dist/commands/insights.js +2 -2
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/lease.d.ts +1 -1
- package/dist/commands/lease.js +7 -6
- 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 +4 -3
- package/dist/commands/run-broadcast.d.ts +1 -1
- package/dist/commands/run-broadcast.js +1 -1
- package/dist/commands/sessions-picker-factory.d.ts +2 -1
- package/dist/commands/sessions-picker-factory.js +1 -1
- package/dist/commands/sessions-picker.d.ts +22 -0
- package/dist/commands/sessions-picker.js +48 -9
- package/dist/commands/sessions-stop.d.ts +24 -0
- package/dist/commands/sessions-stop.js +66 -0
- package/dist/commands/sessions-trace.d.ts +82 -0
- package/dist/commands/sessions-trace.js +387 -0
- package/dist/commands/sessions.js +5 -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.d.ts +15 -0
- package/dist/commands/ssh.js +243 -18
- package/dist/commands/teams.js +34 -2
- package/dist/commands/traces.d.ts +2 -0
- package/dist/commands/traces.js +184 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +32 -62
- package/dist/commands/watchdog.js +9 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/lib/account-capabilities.d.ts +4 -0
- package/dist/lib/account-capabilities.js +13 -9
- package/dist/lib/account-registry.d.ts +2 -0
- package/dist/lib/account-registry.js +30 -2
- package/dist/lib/accounting/rotate.d.ts +9 -0
- package/dist/lib/accounting/rotate.js +12 -2
- package/dist/lib/accounting/usage.d.ts +84 -6
- package/dist/lib/accounting/usage.js +293 -75
- package/dist/lib/agent-modes.js +29 -1
- package/dist/lib/agent-spec/agents.d.ts +4 -3
- package/dist/lib/agent-spec/agents.js +25 -15
- package/dist/lib/analytics/mix-commands.d.ts +1 -1
- package/dist/lib/analytics/mix-commands.js +2 -2
- package/dist/lib/auth-health.d.ts +13 -3
- package/dist/lib/auth-health.js +50 -8
- package/dist/lib/browser/devices.d.ts +19 -0
- package/dist/lib/browser/devices.js +23 -0
- package/dist/lib/browser/drivers/ssh.d.ts +16 -2
- package/dist/lib/browser/drivers/ssh.js +36 -9
- package/dist/lib/browser/ipc.d.ts +1 -1
- package/dist/lib/browser/ipc.js +40 -9
- package/dist/lib/browser/profiles.d.ts +104 -65
- package/dist/lib/browser/profiles.js +277 -165
- 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 +28 -2
- package/dist/lib/browser/remote-control.js +35 -3
- 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 +44 -11
- package/dist/lib/browser/runtime-state.js +98 -15
- package/dist/lib/browser/service.d.ts +65 -9
- package/dist/lib/browser/service.js +196 -61
- package/dist/lib/browser/sessions-list.d.ts +1 -3
- package/dist/lib/browser/sessions-list.js +3 -16
- package/dist/lib/browser/task-index.d.ts +70 -0
- package/dist/lib/browser/task-index.js +180 -0
- package/dist/lib/browser/types.d.ts +12 -1
- 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/codex-policy.d.ts +11 -1
- package/dist/lib/codex-policy.js +20 -5
- 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 +6 -11
- package/dist/lib/config-keys.js +18 -19
- 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/runner.js +15 -2
- 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 +22 -20
- package/dist/lib/device-config.d.ts +25 -10
- package/dist/lib/device-config.js +90 -39
- package/dist/lib/devices/config-migration.d.ts +7 -0
- package/dist/lib/devices/config-migration.js +67 -3
- package/dist/lib/devices/health.d.ts +27 -2
- package/dist/lib/devices/health.js +39 -9
- package/dist/lib/devices/interactive-host.d.ts +15 -0
- package/dist/lib/devices/interactive-host.js +53 -0
- package/dist/lib/devices/registry.d.ts +61 -4
- package/dist/lib/devices/registry.js +94 -38
- package/dist/lib/devices/stats-cache.d.ts +26 -0
- package/dist/lib/devices/stats-cache.js +59 -7
- 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 +70 -14
- package/dist/lib/exec.js +110 -38
- package/dist/lib/feed/answer.d.ts +29 -0
- package/dist/lib/feed/answer.js +145 -0
- package/dist/lib/feed/attention.d.ts +83 -0
- package/dist/lib/feed/attention.js +0 -0
- package/dist/lib/feed/feed.d.ts +109 -0
- package/dist/lib/feed/feed.js +154 -3
- package/dist/lib/feed/pr-status.d.ts +14 -0
- package/dist/lib/feed/pr-status.js +47 -0
- package/dist/lib/feed/watch.d.ts +59 -0
- package/dist/lib/feed/watch.js +180 -0
- package/dist/lib/fleet/capture.js +6 -0
- package/dist/lib/fleet/remote-login.js +8 -3
- package/dist/lib/fleet/types.d.ts +17 -0
- package/dist/lib/harness/adapter.d.ts +11 -0
- package/dist/lib/harness/adapters/claude.js +38 -24
- package/dist/lib/harness/adapters/codex.js +2 -4
- 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 +19 -0
- package/dist/lib/hooks/install.js +2 -2
- package/dist/lib/hooks/match.d.ts +3 -0
- package/dist/lib/hooks/match.js +25 -0
- 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 +35 -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/hosts/registry.js +7 -0
- 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 +44 -7
- package/dist/lib/menubar/install-menubar.js +105 -23
- 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/open-url.d.ts +60 -2
- package/dist/lib/open-url.js +167 -12
- 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/refresh-coordinator.d.ts +1 -1
- package/dist/lib/refresh-coordinator.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.d.ts +67 -0
- package/dist/lib/session/active.js +86 -2
- package/dist/lib/session/db.d.ts +11 -1
- package/dist/lib/session/db.js +142 -8
- package/dist/lib/session/discover.d.ts +23 -1
- package/dist/lib/session/discover.js +44 -10
- 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/prompt.d.ts +35 -0
- package/dist/lib/session/prompt.js +56 -0
- 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.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +17 -3
- package/dist/lib/session/render.js +7 -3
- package/dist/lib/session/session-cache.d.ts +53 -0
- package/dist/lib/session/session-cache.js +93 -1
- 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-compare.d.ts +47 -0
- package/dist/lib/session/trajectory-compare.js +116 -0
- package/dist/lib/session/trajectory-html.d.ts +19 -0
- package/dist/lib/session/trajectory-html.js +770 -0
- package/dist/lib/session/trajectory-lineage.d.ts +92 -0
- package/dist/lib/session/trajectory-lineage.js +195 -0
- package/dist/lib/session/trajectory-text.d.ts +37 -0
- package/dist/lib/session/trajectory-text.js +284 -0
- package/dist/lib/session/trajectory.d.ts +102 -0
- package/dist/lib/session/trajectory.js +384 -0
- 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.d.ts +3 -1
- package/dist/lib/startup/command-registry.js +6 -3
- package/dist/lib/startup/dev-build.js +3 -3
- package/dist/lib/state.d.ts +2 -2
- package/dist/lib/state.js +11 -5
- package/dist/lib/subagents-registry.js +1 -1
- package/dist/lib/teams/agents.js +9 -0
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/inject.d.ts +19 -0
- package/dist/lib/terminal/inject.js +19 -9
- 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.d.ts +46 -13
- package/dist/lib/tmux/session.js +204 -27
- 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 +34 -19
- package/dist/lib/types.js +14 -0
- package/dist/lib/usage-backoff.d.ts +2 -1
- package/dist/lib/usage-backoff.js +60 -19
- package/dist/lib/usage-refresh.d.ts +18 -4
- package/dist/lib/usage-refresh.js +68 -19
- package/dist/lib/watchdog/log.d.ts +1 -1
- package/dist/lib/watchdog/log.js +1 -1
- package/dist/lib/watchdog/runner.d.ts +36 -45
- package/dist/lib/watchdog/runner.js +150 -119
- package/dist/lib/watchdog/service.d.ts +0 -1
- package/dist/lib/watchdog/service.js +0 -1
- package/dist/lib/watchdog/watchdog-agent.d.ts +38 -0
- package/dist/lib/watchdog/watchdog-agent.js +59 -0
- package/dist/lib/watchdog/watchdog.d.ts +11 -2
- package/dist/lib/watchdog/watchdog.js +37 -104
- package/dist/lib/workflows.d.ts +1 -1
- package/package.json +5 -7
- package/dist/commands/usage.d.ts +0 -27
- package/dist/commands/usage.js +0 -96
- 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 -24
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -123
- 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,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
|
+
}
|
|
@@ -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' | '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';
|
|
291
291
|
export interface IPCRequest {
|
|
292
292
|
action: IPCAction;
|
|
293
293
|
task?: string;
|
|
@@ -366,6 +366,17 @@ export interface IPCRequest {
|
|
|
366
366
|
* for `computer.action` events, so both tool surfaces key on one signal.
|
|
367
367
|
*/
|
|
368
368
|
sessionId?: string;
|
|
369
|
+
/**
|
|
370
|
+
* True when the CLI process that issued this request was itself dispatched to
|
|
371
|
+
* this machine by a fleet `--device` hop.
|
|
372
|
+
*
|
|
373
|
+
* Stamped client-side for the same reason actor/launchId are — and
|
|
374
|
+
* additionally because the daemon may have been auto-started BY a fleet-remote
|
|
375
|
+
* CLI and inherited AGENTS_FLEET_REMOTE for its whole life (startDetached
|
|
376
|
+
* passes `env: opts.env ?? process.env`). The daemon's own env is therefore
|
|
377
|
+
* not a truthful signal about the CURRENT caller; only the request is.
|
|
378
|
+
*/
|
|
379
|
+
fleetRemote?: boolean;
|
|
369
380
|
}
|
|
370
381
|
/** Subset of IPCResponse describing a recording start result. */
|
|
371
382
|
export interface RecordStartFields {
|
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
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export type CodexPolicyMode = 'plan' | 'edit' | 'skip';
|
|
1
|
+
export type CodexPolicyMode = 'plan' | 'edit' | 'auto' | 'skip';
|
|
2
2
|
export declare const CODEX_PLAN_PROFILE = "agents-plan";
|
|
3
3
|
export declare const CODEX_EDIT_PROFILE = "agents-edit";
|
|
4
|
+
export declare const CODEX_AUTO_PROFILE = "agents-auto";
|
|
4
5
|
/**
|
|
5
6
|
* Writable roots for Codex's `edit` profile: the managed user `.agents` dir, the
|
|
6
7
|
* baseline toolchain caches, and — when `cwd` is inside a repo — that repo's
|
|
@@ -17,6 +18,15 @@ export declare function codexPermissionProfileConfig(mode: Exclude<CodexPolicyMo
|
|
|
17
18
|
* Config overrides are deliberately used instead of the legacy `--sandbox`
|
|
18
19
|
* flags: named permission profiles are the only Codex surface that can keep a
|
|
19
20
|
* plan run filesystem-read-only while independently enabling network access.
|
|
21
|
+
*
|
|
22
|
+
* `auto` and `edit` share one sandbox (`:workspace` plus the writable roots) and
|
|
23
|
+
* differ only in `approval_policy`. `edit` is `on-request`: a command the sandbox
|
|
24
|
+
* denies comes back as an approval prompt, which is right when someone is sitting
|
|
25
|
+
* at the terminal. `auto` is `never`: nothing prompts, and a denied command
|
|
26
|
+
* surfaces to the model as a plain command failure — the only behavior that works
|
|
27
|
+
* for an unattended run, where a prompt nobody answers is an agent that has
|
|
28
|
+
* stopped. Autonomy is the approval axis only; neither widens the sandbox, and
|
|
29
|
+
* `skip` remains the sole mode that removes it.
|
|
20
30
|
*/
|
|
21
31
|
export declare function codexPolicyArgs(mode: CodexPolicyMode, writableRoots?: string[]): string[];
|
|
22
32
|
/** Preserve whether --mode was omitted when a run is re-dispatched remotely. */
|
package/dist/lib/codex-policy.js
CHANGED
|
@@ -4,6 +4,7 @@ import { codexDefaultWritableRoots } from './permissions.js';
|
|
|
4
4
|
import { repoAgentsDirForCwd } from './project-key.js';
|
|
5
5
|
export const CODEX_PLAN_PROFILE = 'agents-plan';
|
|
6
6
|
export const CODEX_EDIT_PROFILE = 'agents-edit';
|
|
7
|
+
export const CODEX_AUTO_PROFILE = 'agents-auto';
|
|
7
8
|
function unique(values) {
|
|
8
9
|
return [...new Set(values)];
|
|
9
10
|
}
|
|
@@ -28,25 +29,39 @@ function inlineWorkspaceRoots(roots) {
|
|
|
28
29
|
}
|
|
29
30
|
export function codexPermissionProfileConfig(mode, writableRoots = codexEditWritableRoots()) {
|
|
30
31
|
const parent = mode === 'plan' ? ':read-only' : ':workspace';
|
|
31
|
-
const roots = mode === '
|
|
32
|
-
?
|
|
33
|
-
:
|
|
32
|
+
const roots = mode === 'plan'
|
|
33
|
+
? ''
|
|
34
|
+
: `, workspace_roots = { ${inlineWorkspaceRoots(unique(writableRoots))} }`;
|
|
34
35
|
return `{ extends = ${JSON.stringify(parent)}${roots}, network = { enabled = true, allow_local_binding = true } }`;
|
|
35
36
|
}
|
|
37
|
+
const CODEX_PROFILES = {
|
|
38
|
+
plan: CODEX_PLAN_PROFILE,
|
|
39
|
+
edit: CODEX_EDIT_PROFILE,
|
|
40
|
+
auto: CODEX_AUTO_PROFILE,
|
|
41
|
+
};
|
|
36
42
|
/**
|
|
37
43
|
* Canonical Codex safety policy used by every native launch path.
|
|
38
44
|
*
|
|
39
45
|
* Config overrides are deliberately used instead of the legacy `--sandbox`
|
|
40
46
|
* flags: named permission profiles are the only Codex surface that can keep a
|
|
41
47
|
* plan run filesystem-read-only while independently enabling network access.
|
|
48
|
+
*
|
|
49
|
+
* `auto` and `edit` share one sandbox (`:workspace` plus the writable roots) and
|
|
50
|
+
* differ only in `approval_policy`. `edit` is `on-request`: a command the sandbox
|
|
51
|
+
* denies comes back as an approval prompt, which is right when someone is sitting
|
|
52
|
+
* at the terminal. `auto` is `never`: nothing prompts, and a denied command
|
|
53
|
+
* surfaces to the model as a plain command failure — the only behavior that works
|
|
54
|
+
* for an unattended run, where a prompt nobody answers is an agent that has
|
|
55
|
+
* stopped. Autonomy is the approval axis only; neither widens the sandbox, and
|
|
56
|
+
* `skip` remains the sole mode that removes it.
|
|
42
57
|
*/
|
|
43
58
|
export function codexPolicyArgs(mode, writableRoots = codexEditWritableRoots()) {
|
|
44
59
|
if (mode === 'skip')
|
|
45
60
|
return ['--dangerously-bypass-approvals-and-sandbox'];
|
|
46
|
-
const profile = mode
|
|
61
|
+
const profile = CODEX_PROFILES[mode];
|
|
47
62
|
return [
|
|
48
63
|
'-c',
|
|
49
|
-
'
|
|
64
|
+
`approval_policy=${mode === 'auto' ? '"never"' : '"on-request"'}`,
|
|
50
65
|
'-c',
|
|
51
66
|
`default_permissions=${JSON.stringify(profile)}`,
|
|
52
67
|
'-c',
|
|
@@ -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>;
|