@phnx-labs/agents-cli 1.22.41 → 1.22.43

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.
Files changed (253) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +10 -4
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +25 -7
  5. package/dist/cli/command-registry.d.ts +2 -3
  6. package/dist/cli/command-registry.js +4 -8
  7. package/dist/commands/accounts.d.ts +29 -0
  8. package/dist/commands/accounts.js +220 -29
  9. package/dist/commands/alias.d.ts +7 -4
  10. package/dist/commands/alias.js +34 -9
  11. package/dist/commands/auth.d.ts +9 -0
  12. package/dist/commands/auth.js +108 -0
  13. package/dist/commands/browser-sessions-picker.js +42 -62
  14. package/dist/commands/browser.js +30 -38
  15. package/dist/commands/computer-actions.d.ts +1 -1
  16. package/dist/commands/computer-actions.js +1 -1
  17. package/dist/commands/computer-sessions-picker.js +32 -55
  18. package/dist/commands/computer.d.ts +1 -1
  19. package/dist/commands/computer.js +22 -8
  20. package/dist/commands/daemon.js +1 -1
  21. package/dist/commands/doctor.d.ts +5 -3
  22. package/dist/commands/doctor.js +23 -8
  23. package/dist/commands/exec.js +3 -2
  24. package/dist/commands/feed.js +2 -31
  25. package/dist/commands/harness.js +3 -3
  26. package/dist/commands/hooks.js +1 -1
  27. package/dist/commands/import.js +1 -1
  28. package/dist/commands/insights.js +163 -85
  29. package/dist/commands/inspect.js +2 -2
  30. package/dist/commands/mine.js +1 -1
  31. package/dist/commands/monitors.js +1 -1
  32. package/dist/commands/org.d.ts +6 -0
  33. package/dist/commands/org.js +175 -0
  34. package/dist/commands/packages.js +1 -1
  35. package/dist/commands/profiles.js +2 -2
  36. package/dist/commands/prune.js +1 -1
  37. package/dist/commands/routines.js +44 -26
  38. package/dist/commands/run-account-picker.d.ts +19 -0
  39. package/dist/commands/run-account-picker.js +81 -0
  40. package/dist/commands/sessions-picker-factory.d.ts +40 -0
  41. package/dist/commands/sessions-picker-factory.js +67 -0
  42. package/dist/commands/sessions-picker.js +124 -122
  43. package/dist/commands/setup-browser.js +4 -4
  44. package/dist/commands/setup-mine.js +1 -1
  45. package/dist/commands/setup-preferences.js +2 -2
  46. package/dist/commands/setup.js +10 -4
  47. package/dist/commands/snapshot.d.ts +1 -1
  48. package/dist/commands/snapshot.js +4 -4
  49. package/dist/commands/status.d.ts +4 -2
  50. package/dist/commands/status.js +10 -7
  51. package/dist/commands/sync.d.ts +1 -0
  52. package/dist/commands/sync.js +30 -3
  53. package/dist/commands/versions.js +1 -1
  54. package/dist/commands/view.js +7 -1
  55. package/dist/commands/webhook.js +1 -1
  56. package/dist/lib/account-capabilities.d.ts +10 -0
  57. package/dist/lib/account-capabilities.js +34 -0
  58. package/dist/lib/accounting/rotate.d.ts +5 -3
  59. package/dist/lib/accounting/rotate.js +36 -7
  60. package/dist/lib/accounting/usage.d.ts +18 -4
  61. package/dist/lib/accounting/usage.js +138 -27
  62. package/dist/lib/agent-spec/agents.d.ts +584 -0
  63. package/dist/lib/agent-spec/agents.js +3127 -0
  64. package/dist/lib/agent-spec/provider.js +1 -1
  65. package/dist/lib/agents.d.ts +2 -584
  66. package/dist/lib/agents.js +2 -3127
  67. package/dist/lib/browser/drivers/local.d.ts +8 -2
  68. package/dist/lib/browser/drivers/local.js +8 -2
  69. package/dist/lib/browser/drivers/ssh.d.ts +8 -2
  70. package/dist/lib/browser/drivers/ssh.js +10 -4
  71. package/dist/lib/browser/hygiene.d.ts +1 -1
  72. package/dist/lib/browser/hygiene.js +5 -1
  73. package/dist/lib/browser/profiles.d.ts +74 -1
  74. package/dist/lib/browser/profiles.js +114 -5
  75. package/dist/lib/browser/runtime-state.d.ts +11 -5
  76. package/dist/lib/browser/runtime-state.js +13 -9
  77. package/dist/lib/browser/service.d.ts +68 -30
  78. package/dist/lib/browser/service.js +305 -194
  79. package/dist/lib/browser/types.d.ts +95 -3
  80. package/dist/lib/browser/types.js +51 -0
  81. package/dist/lib/catchup.js +1 -1
  82. package/dist/lib/cloud/rush.d.ts +0 -15
  83. package/dist/lib/cloud/rush.js +0 -35
  84. package/dist/lib/computer/actions.d.ts +1 -1
  85. package/dist/lib/computer/actions.js +1 -1
  86. package/dist/lib/{computer-rpc.js → computer/computer-rpc.js} +5 -5
  87. package/dist/lib/computer/dispatch.d.ts +1 -1
  88. package/dist/lib/computer/download.d.ts +1 -1
  89. package/dist/lib/computer/download.js +3 -3
  90. package/dist/lib/{ssh-tunnel.d.ts → computer/ssh-tunnel.d.ts} +1 -1
  91. package/dist/lib/{ssh-tunnel.js → computer/ssh-tunnel.js} +12 -12
  92. package/dist/lib/crabbox/runtimes.d.ts +6 -2
  93. package/dist/lib/crabbox/runtimes.js +21 -5
  94. package/dist/lib/daemon/daemon.js +87 -59
  95. package/dist/lib/{runner.d.ts → daemon/runner.d.ts} +28 -11
  96. package/dist/lib/{runner.js → daemon/runner.js} +143 -123
  97. package/dist/lib/devices/doctor-findings.d.ts +1 -1
  98. package/dist/lib/devices/doctor-findings.js +4 -4
  99. package/dist/lib/devices/fleet-inventory.js +1 -1
  100. package/dist/lib/devices/harness-inventory.js +1 -1
  101. package/dist/lib/devices/health-report.js +1 -1
  102. package/dist/lib/devices/registry.js +4 -22
  103. package/dist/lib/doctor-diff.d.ts +1 -1
  104. package/dist/lib/doctor-diff.js +1 -1
  105. package/dist/lib/drift-sync.d.ts +1 -1
  106. package/dist/lib/drift-sync.js +2 -2
  107. package/dist/lib/drift.d.ts +1 -1
  108. package/dist/lib/drift.js +1 -1
  109. package/dist/lib/entitlement.d.ts +31 -0
  110. package/dist/lib/entitlement.js +137 -0
  111. package/dist/lib/exec.js +52 -205
  112. package/dist/lib/feed/feed.js +2 -8
  113. package/dist/lib/format.d.ts +9 -0
  114. package/dist/lib/format.js +16 -0
  115. package/dist/lib/fs-atomic.d.ts +7 -0
  116. package/dist/lib/fs-atomic.js +9 -0
  117. package/dist/lib/github/pr-mergeable.d.ts +49 -0
  118. package/dist/lib/github/pr-mergeable.js +157 -0
  119. package/dist/lib/github/pr-verdict.d.ts +67 -0
  120. package/dist/lib/github/pr-verdict.js +72 -0
  121. package/dist/lib/harness/adapter.d.ts +161 -0
  122. package/dist/lib/harness/adapter.js +47 -0
  123. package/dist/lib/harness/adapters/claude.d.ts +2 -0
  124. package/dist/lib/harness/adapters/claude.js +110 -0
  125. package/dist/lib/harness/adapters/codex.d.ts +2 -0
  126. package/dist/lib/harness/adapters/codex.js +83 -0
  127. package/dist/lib/harness/adapters/copilot.d.ts +2 -0
  128. package/dist/lib/harness/adapters/copilot.js +23 -0
  129. package/dist/lib/harness/adapters/cursor.d.ts +2 -0
  130. package/dist/lib/harness/adapters/cursor.js +38 -0
  131. package/dist/lib/harness/adapters/droid.d.ts +2 -0
  132. package/dist/lib/harness/adapters/droid.js +17 -0
  133. package/dist/lib/harness/adapters/grok.d.ts +2 -0
  134. package/dist/lib/harness/adapters/grok.js +14 -0
  135. package/dist/lib/harness/adapters/kimi.d.ts +2 -0
  136. package/dist/lib/harness/adapters/kimi.js +49 -0
  137. package/dist/lib/harness/adapters/muse.d.ts +2 -0
  138. package/dist/lib/harness/adapters/muse.js +44 -0
  139. package/dist/lib/harness/adapters/opencode.d.ts +2 -0
  140. package/dist/lib/harness/adapters/opencode.js +14 -0
  141. package/dist/lib/harness/exec-config-version.d.ts +8 -0
  142. package/dist/lib/harness/exec-config-version.js +22 -0
  143. package/dist/lib/harness/index.d.ts +1 -0
  144. package/dist/lib/harness/index.js +34 -0
  145. package/dist/lib/{hooks.d.ts → hooks/install.d.ts} +1 -1
  146. package/dist/lib/{hooks.js → hooks/install.js} +11 -11
  147. package/dist/lib/hosts/passthrough.d.ts +10 -0
  148. package/dist/lib/hosts/passthrough.js +28 -6
  149. package/dist/lib/hosts/session-index.js +1 -1
  150. package/dist/lib/import.js +1 -1
  151. package/dist/lib/installations/index.d.ts +4 -4
  152. package/dist/lib/installations/index.js +3 -4
  153. package/dist/lib/installations/migrate.js +6 -6
  154. package/dist/lib/{shims.d.ts → installations/shims.d.ts} +2 -2
  155. package/dist/lib/{shims.js → installations/shims.js} +13 -115
  156. package/dist/lib/installations/store.d.ts +141 -3
  157. package/dist/lib/installations/store.js +475 -5
  158. package/dist/lib/installations/versions.d.ts +40 -125
  159. package/dist/lib/installations/versions.js +57 -462
  160. package/dist/lib/isolation-boundary-report.d.ts +1 -1
  161. package/dist/lib/isolation-boundary-report.js +1 -1
  162. package/dist/lib/mailbox-gc.js +2 -3
  163. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  164. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  165. package/dist/lib/menubar/install-menubar.js +19 -8
  166. package/dist/lib/monitors/config.d.ts +2 -2
  167. package/dist/lib/monitors/config.js +3 -2
  168. package/dist/lib/monitors/dispatch.d.ts +1 -1
  169. package/dist/lib/monitors/dispatch.js +2 -2
  170. package/dist/lib/monitors/state.d.ts +1 -1
  171. package/dist/lib/origin-machine.d.ts +18 -0
  172. package/dist/lib/origin-machine.js +34 -0
  173. package/dist/lib/perf/db.js +1 -1
  174. package/dist/lib/perf/spool.js +1 -1
  175. package/dist/lib/plugins/plugins.js +1 -1
  176. package/dist/lib/plugins/skills.js +1 -1
  177. package/dist/lib/prix-account.d.ts +158 -0
  178. package/dist/lib/prix-account.js +214 -0
  179. package/dist/lib/profiles.d.ts +7 -0
  180. package/dist/lib/profiles.js +26 -1
  181. package/dist/lib/project-key.js +2 -2
  182. package/dist/lib/project-launch.js +1 -1
  183. package/dist/lib/refresh.js +2 -2
  184. package/dist/lib/resource-inventory.d.ts +1 -1
  185. package/dist/lib/resource-inventory.js +1 -1
  186. package/dist/lib/rules/rules.js +1 -1
  187. package/dist/lib/sandbox.d.ts +24 -0
  188. package/dist/lib/sandbox.js +101 -0
  189. package/dist/lib/scheduling/routines.d.ts +18 -9
  190. package/dist/lib/scheduling/routines.js +17 -9
  191. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  192. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  193. package/dist/lib/secrets/agent.js +9 -1
  194. package/dist/lib/secrets/index.js +1 -1
  195. package/dist/lib/secrets/list-filter.js +1 -1
  196. package/dist/lib/self-heal/checks/hook-runtime.js +1 -1
  197. package/dist/lib/self-heal/checks/path.js +1 -1
  198. package/dist/lib/self-heal/checks/shadowing.js +1 -1
  199. package/dist/lib/self-heal/checks/shims.js +1 -1
  200. package/dist/lib/service-manifest.d.ts +20 -0
  201. package/dist/lib/service-manifest.js +34 -0
  202. package/dist/lib/session/cloud.js +1 -1
  203. package/dist/lib/session/db.js +1 -1
  204. package/dist/lib/session/discover.d.ts +39 -0
  205. package/dist/lib/session/discover.js +3 -3
  206. package/dist/lib/session/fork.js +1 -1
  207. package/dist/lib/session/live-metadata.js +1 -1
  208. package/dist/lib/session/origin-machine.d.ts +1 -18
  209. package/dist/lib/session/origin-machine.js +4 -34
  210. package/dist/lib/session/parse.js +24 -42
  211. package/dist/lib/session/recovery.js +1 -1
  212. package/dist/lib/session/relative-time.d.ts +1 -44
  213. package/dist/lib/session/relative-time.js +4 -120
  214. package/dist/lib/session/remote-list.js +1 -1
  215. package/dist/lib/session/remote.js +2 -2
  216. package/dist/lib/session/short-id.d.ts +1 -17
  217. package/dist/lib/session/short-id.js +4 -20
  218. package/dist/lib/session/width.d.ts +1 -29
  219. package/dist/lib/session/width.js +4 -101
  220. package/dist/lib/snapshot.d.ts +2 -2
  221. package/dist/lib/snapshot.js +1 -1
  222. package/dist/lib/staleness/prune.d.ts +1 -1
  223. package/dist/lib/staleness/prune.js +1 -1
  224. package/dist/lib/staleness/writers/hooks.js +1 -1
  225. package/dist/lib/startup/command-registry.d.ts +4 -1
  226. package/dist/lib/startup/command-registry.js +12 -5
  227. package/dist/lib/teams/agents.js +2 -19
  228. package/dist/lib/teams/parsers.js +20 -26
  229. package/dist/lib/teams/placement-probe.js +1 -1
  230. package/dist/lib/teams/registry.js +2 -21
  231. package/dist/lib/teams/scheduler.js +1 -1
  232. package/dist/lib/text/relative-time.d.ts +44 -0
  233. package/dist/lib/text/relative-time.js +120 -0
  234. package/dist/lib/text/short-id.d.ts +17 -0
  235. package/dist/lib/text/short-id.js +20 -0
  236. package/dist/lib/text/width.d.ts +29 -0
  237. package/dist/lib/text/width.js +101 -0
  238. package/dist/lib/triggers/handlers.d.ts +7 -0
  239. package/dist/lib/triggers/handlers.js +9 -1
  240. package/dist/lib/triggers/webhook.d.ts +1 -0
  241. package/dist/lib/triggers/webhook.js +3 -2
  242. package/dist/lib/uninstall.js +1 -1
  243. package/dist/lib/version.d.ts +9 -2
  244. package/dist/lib/version.js +11 -9
  245. package/dist/lib/view-types.d.ts +4 -0
  246. package/package.json +1 -1
  247. package/dist/commands/tickets.d.ts +0 -2
  248. package/dist/commands/tickets.js +0 -43
  249. package/dist/lib/observe-aliases.d.ts +0 -30
  250. package/dist/lib/observe-aliases.js +0 -38
  251. package/dist/lib/tickets/list.d.ts +0 -53
  252. package/dist/lib/tickets/list.js +0 -153
  253. /package/dist/lib/{computer-rpc.d.ts → computer/computer-rpc.d.ts} +0 -0
@@ -1,4 +1,66 @@
1
1
  export type BrowserType = 'chrome' | 'comet' | 'chromium' | 'brave' | 'edge' | 'arc' | 'custom';
2
+ /**
3
+ * The user-facing name of a profile — what `agents browser profiles list`
4
+ * prints and what `--profile <name>` takes. ALWAYS bare: it never carries an
5
+ * `@<endpoint>` suffix or a `.<fork>` suffix. `BrowserProfile.name` is one.
6
+ */
7
+ export type ProfileName = string;
8
+ /**
9
+ * The key of an endpoint preset within a profile (`endpoint-0`, `local`, …) —
10
+ * see {@link EndpointPreset} and `getEndpointPresets`.
11
+ */
12
+ export type EndpointName = string;
13
+ /**
14
+ * The RUNTIME key a live browser is registered under: `<profile>@<endpoint>`,
15
+ * plus an optional `.<n>` fork suffix for Electron forks. It keys
16
+ * `BrowserService.connections`, the profile's runtime dir, its `tasks.json`,
17
+ * and its capture dirs — so one YAML profile can run at several endpoints
18
+ * concurrently without colliding on disk.
19
+ *
20
+ * It is BRANDED on purpose. Before RUSH-2709 both this and a {@link ProfileName}
21
+ * were bare `string`s and `start()` overwrote `BrowserProfile.name` with the
22
+ * composite, so a connection key leaked into every user-facing listing and six
23
+ * consumers each invented their own rule for turning one back into the other.
24
+ * The brand makes `connections.get(someProfileName)` — the exact miss behind
25
+ * `status --profile comet-local` returning EMPTY for a live
26
+ * `comet-local@endpoint-0` — a compile error. Build one ONLY with
27
+ * {@link connectionKey} (deriving) or {@link asConnectionKey} (adopting a name
28
+ * that is already a runtime key, e.g. a directory read off disk).
29
+ */
30
+ export type ConnectionKey = string & {
31
+ readonly __connectionKey: unique symbol;
32
+ };
33
+ /** The single site that derives a {@link ConnectionKey} from its two parts. */
34
+ export declare function connectionKey(profile: ProfileName, endpoint: EndpointName): ConnectionKey;
35
+ /**
36
+ * Adopt a string that IS already a runtime key — a runtime directory name, a
37
+ * `tasks.json` `profile` field, a fork key. Never use it to launder a bare
38
+ * profile name into a key; use {@link connectionKey} for that.
39
+ */
40
+ export declare function asConnectionKey(raw: string): ConnectionKey;
41
+ /**
42
+ * Split a {@link ConnectionKey} back into its parts. The inverse of
43
+ * {@link connectionKey}, and the ONE rule every consumer uses to decide whether
44
+ * a runtime key belongs to a profile.
45
+ *
46
+ * Tolerates the legacy shape still on disk: a pre-composite key that is just
47
+ * the bare profile name (`comet-local`).
48
+ *
49
+ * A key with no `@` is read as a WHOLE profile name, never as `<name>.<fork>`,
50
+ * because nothing constrains a profile's name (`createProfile` checks only for
51
+ * a duplicate name and a port collision). Reading the trailing digits as a fork
52
+ * would let `stop --profile chrome` claim the runtime dirs of a separate
53
+ * profile named `chrome.2` and kill its browser. A fork is only ever created
54
+ * from a composite base (`forkElectronProfile` appends to a `<p>@<e>` key), so
55
+ * the `@`-bearing branch below is the one that must understand forks.
56
+ */
57
+ export declare function parseConnectionKey(key: string): {
58
+ profile: ProfileName;
59
+ endpoint?: EndpointName;
60
+ fork?: number;
61
+ };
62
+ /** True when `key` is a runtime key of `profile`. The only reconciliation rule. */
63
+ export declare function keyBelongsToProfile(key: string, profile: ProfileName): boolean;
2
64
  /**
3
65
  * A single named endpoint preset within a profile. Lets one profile cover
4
66
  * the local + remote variants of the same app (e.g. an Electron app on this
@@ -24,7 +86,12 @@ export interface EndpointPreset {
24
86
  targetFilter?: string;
25
87
  }
26
88
  export interface BrowserProfile {
27
- name: string;
89
+ /**
90
+ * The user-facing profile name, ALWAYS bare — never `name@endpoint`. The
91
+ * runtime key that carries the endpoint is a separate value
92
+ * ({@link ConnectionKey}); nothing may write one here.
93
+ */
94
+ name: ProfileName;
28
95
  description?: string;
29
96
  browser: BrowserType;
30
97
  binary?: string;
@@ -84,8 +151,24 @@ export interface Task {
84
151
  * short machine id can stay addressable while status stays readable.
85
152
  */
86
153
  label?: string;
87
- profile: string;
154
+ /**
155
+ * The RUNTIME key of the browser this task lives on (`<profile>@<endpoint>`),
156
+ * not the bare profile name — it addresses the runtime dir this task's
157
+ * `tasks.json` and captures are written under. Use
158
+ * {@link parseConnectionKey} to get the user-facing name out of it.
159
+ */
160
+ profile: ConnectionKey;
88
161
  tabs: Record<string, string>;
162
+ /**
163
+ * Tabs this task DRIVES but did not create, by shortId — a tab that already
164
+ * existed in the browser and was reused because the browser cannot open new
165
+ * ones (Arc: `Target.createTarget` crashes it, #2778/#2786). Every close path
166
+ * skips these: the task never opened the tab, so closing it on `done` would
167
+ * take away something that was there first. Same rule `adoptTabShowing`
168
+ * states for unowned pages, kept when reuse is unavoidable rather than
169
+ * optional.
170
+ */
171
+ borrowedTabs?: string[];
89
172
  currentTabId?: string;
90
173
  createdAt: number;
91
174
  /**
@@ -142,7 +225,16 @@ export interface TabInfo {
142
225
  task: string;
143
226
  }
144
227
  export interface ProfileStatus {
145
- name: string;
228
+ /**
229
+ * The BARE profile name — what the user passed to `--profile` and what
230
+ * `status` prints. The endpoint is reported separately in {@link endpoint};
231
+ * the raw runtime key (if a caller genuinely needs it) is {@link key}.
232
+ */
233
+ name: ProfileName;
234
+ /** Endpoint preset this browser is running at, when the key carries one. */
235
+ endpoint?: EndpointName;
236
+ /** The runtime key backing this status: `<profile>@<endpoint>[.<fork>]`. */
237
+ key?: ConnectionKey;
146
238
  running: boolean;
147
239
  port?: number;
148
240
  pid?: number;
@@ -1,3 +1,54 @@
1
+ /** The single site that derives a {@link ConnectionKey} from its two parts. */
2
+ export function connectionKey(profile, endpoint) {
3
+ return `${profile}@${endpoint}`;
4
+ }
5
+ /**
6
+ * Adopt a string that IS already a runtime key — a runtime directory name, a
7
+ * `tasks.json` `profile` field, a fork key. Never use it to launder a bare
8
+ * profile name into a key; use {@link connectionKey} for that.
9
+ */
10
+ export function asConnectionKey(raw) {
11
+ return raw;
12
+ }
13
+ /**
14
+ * Split a {@link ConnectionKey} back into its parts. The inverse of
15
+ * {@link connectionKey}, and the ONE rule every consumer uses to decide whether
16
+ * a runtime key belongs to a profile.
17
+ *
18
+ * Tolerates the legacy shape still on disk: a pre-composite key that is just
19
+ * the bare profile name (`comet-local`).
20
+ *
21
+ * A key with no `@` is read as a WHOLE profile name, never as `<name>.<fork>`,
22
+ * because nothing constrains a profile's name (`createProfile` checks only for
23
+ * a duplicate name and a port collision). Reading the trailing digits as a fork
24
+ * would let `stop --profile chrome` claim the runtime dirs of a separate
25
+ * profile named `chrome.2` and kill its browser. A fork is only ever created
26
+ * from a composite base (`forkElectronProfile` appends to a `<p>@<e>` key), so
27
+ * the `@`-bearing branch below is the one that must understand forks.
28
+ */
29
+ export function parseConnectionKey(key) {
30
+ // LAST `@`, not the first: a profile name may itself contain one (`me@work`),
31
+ // and splitting on the first would report its profile as `me` — so
32
+ // `status --profile me@work` would find nothing for a running browser.
33
+ const at = key.lastIndexOf('@');
34
+ if (at === -1)
35
+ return { profile: key };
36
+ const profile = key.slice(0, at);
37
+ const rest = key.slice(at + 1);
38
+ const dot = rest.indexOf('.');
39
+ if (dot === -1)
40
+ return { profile, endpoint: rest };
41
+ const fork = Number(rest.slice(dot + 1));
42
+ return {
43
+ profile,
44
+ endpoint: rest.slice(0, dot),
45
+ fork: Number.isFinite(fork) ? fork : undefined,
46
+ };
47
+ }
48
+ /** True when `key` is a runtime key of `profile`. The only reconciliation rule. */
49
+ export function keyBelongsToProfile(key, profile) {
50
+ return parseConnectionKey(key).profile === profile;
51
+ }
1
52
  export const TASK_ID_REGEX = /^[a-z0-9][a-z0-9-]*$/;
2
53
  export function isValidTaskId(id) {
3
54
  return TASK_ID_REGEX.test(id) && id.length <= 64;
@@ -38,7 +38,7 @@ import * as fs from 'fs';
38
38
  import * as path from 'path';
39
39
  import { readJob, writeRunMeta, getRunDir, } from './scheduling/routines.js';
40
40
  import { detectOverdueJobs } from './overdue.js';
41
- import { executeJobDetached } from './runner.js';
41
+ import { executeJobDetached } from './daemon/runner.js';
42
42
  /**
43
43
  * Is this routine allowed to run late? Default true — a routine you scheduled
44
44
  * is one you expect to have run, so losing a fire silently is never the helpful
@@ -25,21 +25,6 @@ export interface AccountManifest {
25
25
  fp: string;
26
26
  versions: AccountManifestEntry[];
27
27
  }
28
- /**
29
- * Read the raw OAuth credentials for one Claude version. On Mac, prefer the
30
- * Keychain blob (canonical). On Linux/CI, fall back to `.claude/.credentials.json`
31
- * inside the version home (where the Linux Claude CLI stores its OAuth).
32
- *
33
- * Returns null when no credentials are findable — caller treats as "version
34
- * is installed but not signed in" and skips it from the manifest.
35
- *
36
- * NOTE: the darwin branch returns the UNWRAPPED oauth sub-object (what the Rush
37
- * server re-wraps). It is NOT the shape Claude Code reads back from
38
- * `.credentials.json`. The lease exporter (crabbox/runtimes.ts) therefore only
39
- * reuses the Linux `.credentials.json` branch here and reads the wrapped raw
40
- * Keychain payload itself on darwin.
41
- */
42
- export declare function readClaudeCredentialsBlob(home: string): Promise<string | null>;
43
28
  /**
44
29
  * Build a manifest of the user's local Claude installations to send on every
45
30
  * cloud dispatch. The manifest is the contract the server uses to detect when
@@ -13,7 +13,6 @@ import { resolveDispatchRepos, normalizeProviderStatus, MAX_IMAGES_PER_DISPATCH
13
13
  import { parseSSE } from './stream.js';
14
14
  import { listInstalledVersions, getVersionHomePath } from '../installations/versions.js';
15
15
  import { getAccountInfo } from '../agents.js';
16
- import { loadClaudeOauth } from '../accounting/usage.js';
17
16
  import { selectBalancedVersion } from '../accounting/rotate.js';
18
17
  const PROXY_BASE = process.env.RUSH_PROXY_BASE ?? 'https://api.prix.dev';
19
18
  const USER_YAML = path.join(os.homedir(), '.rush', 'user.yaml');
@@ -112,40 +111,6 @@ function parsePromptCode(body) {
112
111
  return null;
113
112
  }
114
113
  }
115
- /**
116
- * Read the raw OAuth credentials for one Claude version. On Mac, prefer the
117
- * Keychain blob (canonical). On Linux/CI, fall back to `.claude/.credentials.json`
118
- * inside the version home (where the Linux Claude CLI stores its OAuth).
119
- *
120
- * Returns null when no credentials are findable — caller treats as "version
121
- * is installed but not signed in" and skips it from the manifest.
122
- *
123
- * NOTE: the darwin branch returns the UNWRAPPED oauth sub-object (what the Rush
124
- * server re-wraps). It is NOT the shape Claude Code reads back from
125
- * `.credentials.json`. The lease exporter (crabbox/runtimes.ts) therefore only
126
- * reuses the Linux `.credentials.json` branch here and reads the wrapped raw
127
- * Keychain payload itself on darwin.
128
- */
129
- export async function readClaudeCredentialsBlob(home) {
130
- if (process.platform === 'darwin') {
131
- const oauth = await loadClaudeOauth(home);
132
- if (oauth && oauth.accessToken) {
133
- return JSON.stringify(oauth);
134
- }
135
- }
136
- const credsPath = path.join(home, '.claude', '.credentials.json');
137
- try {
138
- if (fs.existsSync(credsPath)) {
139
- const raw = fs.readFileSync(credsPath, 'utf-8').trim();
140
- if (raw)
141
- return raw;
142
- }
143
- }
144
- catch {
145
- // fall through to null
146
- }
147
- return null;
148
- }
149
114
  /**
150
115
  * Build a manifest of the user's local Claude installations to send on every
151
116
  * cloud dispatch. The manifest is the contract the server uses to detect when
@@ -1,4 +1,4 @@
1
- import type { ComputerClient } from '../computer-rpc.js';
1
+ import type { ComputerClient } from './computer-rpc.js';
2
2
  export interface AppInfo {
3
3
  pid: number;
4
4
  name: string;
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { resolvePolicyPath } from '../computer-rpc.js';
4
+ import { resolvePolicyPath } from './computer-rpc.js';
5
5
  import { COMPUTER_INPUT_GATED_VERBS, formatComputerPermissionGrantHint } from '../permissions.js';
6
6
  import { emit as emitEvent } from '../feed/events.js';
7
7
  import { recordComputerSession } from '../session/db.js';
@@ -16,7 +16,7 @@ import { createConnection } from 'net';
16
16
  import * as fs from 'fs';
17
17
  import * as path from 'path';
18
18
  import { fileURLToPath } from 'url';
19
- import { getHelpersDir, getLogsDir, getUserPermissionsDir, getPermissionsDir } from './state.js';
19
+ import { getHelpersDir, getLogsDir, getUserPermissionsDir, getPermissionsDir } from '../state.js';
20
20
  // Resolve the socket path used by the launchd-managed daemon. Internal
21
21
  // scratch — lives under getHelpersDir() (~/.agents/.cache/helpers/),
22
22
  // matching browser.sock.
@@ -174,10 +174,10 @@ 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). apps/cli/dist/lib -> repo root (4 up) -> native/computer-mac.
178
- path.resolve(here, '..', '..', '..', '..', 'native', 'computer-mac', 'dist', 'ComputerHelper.app', 'Contents', 'MacOS', 'ComputerHelper'),
179
- // Bundled with the npm package.
180
- path.resolve(here, '..', 'computer-helper', 'ComputerHelper.app', 'Contents', 'MacOS', 'ComputerHelper'),
177
+ // Local build (running from the agents-cli checkout). apps/cli/dist/lib/computer -> repo root (5 up) -> native/computer-mac.
178
+ path.resolve(here, '..', '..', '..', '..', '..', 'native', 'computer-mac', 'dist', 'ComputerHelper.app', 'Contents', 'MacOS', 'ComputerHelper'),
179
+ // Bundled with the npm package (dist/lib/computer -> dist/).
180
+ path.resolve(here, '..', '..', 'computer-helper', 'ComputerHelper.app', 'Contents', 'MacOS', 'ComputerHelper'),
181
181
  ];
182
182
  for (const c of candidates) {
183
183
  if (fs.existsSync(c))
@@ -1,4 +1,4 @@
1
- import type { ComputerClient } from '../computer-rpc.js';
1
+ import type { ComputerClient } from './computer-rpc.js';
2
2
  import { type AppInfo } from './actions.js';
3
3
  import type { VerbDispatcher } from './loop.js';
4
4
  declare function rpcMethodFor(verb: string): string;
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * The helper is a signed + notarized universal `.app` bundle published as a
6
6
  * GitHub release asset per tagged CLI version — the same distribution model as
7
- * the Windows helper (see `lib/ssh-tunnel.ts`). A fresh `npm i -g` machine has
7
+ * the Windows helper (see `lib/computer/ssh-tunnel.ts`). A fresh `npm i -g` machine has
8
8
  * no local build, so `agents computer setup` / `agents setup computer` fetch the
9
9
  * asset for the running CLI version, verify its sha256 against the published
10
10
  * `.sha256`, then verify the code signature (Developer ID Team + notarization)
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * The helper is a signed + notarized universal `.app` bundle published as a
6
6
  * GitHub release asset per tagged CLI version — the same distribution model as
7
- * the Windows helper (see `lib/ssh-tunnel.ts`). A fresh `npm i -g` machine has
7
+ * the Windows helper (see `lib/computer/ssh-tunnel.ts`). A fresh `npm i -g` machine has
8
8
  * no local build, so `agents computer setup` / `agents setup computer` fetch the
9
9
  * asset for the running CLI version, verify its sha256 against the published
10
10
  * `.sha256`, then verify the code signature (Developer ID Team + notarization)
@@ -21,8 +21,8 @@ import { Readable } from 'node:stream';
21
21
  import { pipeline } from 'node:stream/promises';
22
22
  import { getCacheDir } from '../state.js';
23
23
  import { getCliVersion } from '../version.js';
24
- import { parseSha256Asset, sha256File } from '../ssh-tunnel.js';
25
- import { resolveHelperApp } from '../computer-rpc.js';
24
+ import { parseSha256Asset, sha256File } from './ssh-tunnel.js';
25
+ import { resolveHelperApp } from './computer-rpc.js';
26
26
  /** GitHub repo whose `v<version>` releases carry the helper asset. */
27
27
  export const HELPER_RELEASE_REPO = 'phnx-labs/agents-cli';
28
28
  /** The zipped `.app` release asset name. */
@@ -17,7 +17,7 @@
17
17
  * `devices/connect` primitives — no parallel SSH implementation.
18
18
  */
19
19
  import { type ChildProcess } from 'child_process';
20
- import { type DeviceProfile } from './devices/registry.js';
20
+ import { type DeviceProfile } from '../devices/registry.js';
21
21
  export interface StartTunnelOptions {
22
22
  /**
23
23
  * Detach the tunnel so it OUTLIVES this CLI process. Used by
@@ -24,14 +24,14 @@ import { fileURLToPath } from 'url';
24
24
  import { randomBytes, createHash } from 'crypto';
25
25
  import { Readable } from 'stream';
26
26
  import { pipeline } from 'stream/promises';
27
- import { sshExec, SSH_OPTS, assertValidSshTarget } from './ssh-exec.js';
28
- import { backgroundSpawnOptions } from './platform/process.js';
29
- import { encodePowerShell } from './browser/drivers/ssh.js';
30
- import { getDevice } from './devices/registry.js';
31
- import { deviceIdentityArgs, sshTargetFor } from './devices/connect.js';
32
- import { hostNameFor } from './devices/ssh-config.js';
33
- import { getCacheDir } from './state.js';
34
- import { getCliVersion } from './version.js';
27
+ import { sshExec, SSH_OPTS, assertValidSshTarget } from '../ssh-exec.js';
28
+ import { backgroundSpawnOptions } from '../platform/process.js';
29
+ import { encodePowerShell } from '../browser/drivers/ssh.js';
30
+ import { getDevice } from '../devices/registry.js';
31
+ import { deviceIdentityArgs, sshTargetFor } from '../devices/connect.js';
32
+ import { hostNameFor } from '../devices/ssh-config.js';
33
+ import { getCacheDir } from '../state.js';
34
+ import { getCliVersion } from '../version.js';
35
35
  import { openComputerClient, resolveTcpEndpoint } from './computer-rpc.js';
36
36
  /** Build the ssh argv (after the `ssh` program name) for an `-L` tunnel. Pure.
37
37
  *
@@ -115,10 +115,10 @@ export const WIN_HELPER_TOKEN_FILE = 'helper-token';
115
115
  export function resolveWinHelperExe() {
116
116
  const here = path.dirname(fileURLToPath(import.meta.url));
117
117
  const candidates = [
118
- // Running from the agents-cli checkout. apps/cli/dist/lib -> repo root (4 up) -> native/computer-win.
119
- path.resolve(here, '..', '..', '..', '..', 'native', 'computer-win', 'dist', WIN_HELPER_EXE),
120
- // Bundled with the npm package (dist/lib -> package root).
121
- path.resolve(here, '..', 'computer-helper-win', WIN_HELPER_EXE),
118
+ // Running from the agents-cli checkout. apps/cli/dist/lib/computer -> repo root (5 up) -> native/computer-win.
119
+ path.resolve(here, '..', '..', '..', '..', '..', 'native', 'computer-win', 'dist', WIN_HELPER_EXE),
120
+ // Bundled with the npm package (dist/lib/computer -> dist/).
121
+ path.resolve(here, '..', '..', 'computer-helper-win', WIN_HELPER_EXE),
122
122
  ];
123
123
  for (const c of candidates) {
124
124
  if (fs.existsSync(c))
@@ -85,8 +85,12 @@ export declare const CLAUDE_TOKEN_REMOTE = ".claude/.credentials.json";
85
85
  * versioned home) uses a hash-suffixed service, so we try the bare service first,
86
86
  * then enumerate installed version homes (preferring the account whose email
87
87
  * matches `preferEmail`, so the token matches the `.claude.json` config we copy).
88
- * Off macOS the local Claude CLI stores the token in `.credentials.json` already —
89
- * reuse the rush.ts Linux branch verbatim.
88
+ * Off macOS the local Claude CLI stores the wrapped rotating blob in
89
+ * `.credentials.json` already. Read that file here with a shape check
90
+ * (`claudeAiOauth.accessToken`). Do not reintroduce a shared helper that
91
+ * also serves Rush Cloud dispatch — dispatch is email-only (SING-1b) and
92
+ * the old `readClaudeCredentialsBlob` path was the #1767 TTY-banner leak
93
+ * (RUSH-2359).
90
94
  *
91
95
  * The reader/service/version helpers are injected so unit tests never touch the
92
96
  * real Keychain.
@@ -19,7 +19,6 @@ import { getAccountInfo } from '../agents.js';
19
19
  import { getKeychainToken } from '../secrets/index.js';
20
20
  import { getClaudeKeychainService } from '../accounting/usage.js';
21
21
  import { listInstalledVersions, getVersionHomePath } from '../installations/versions.js';
22
- import { readClaudeCredentialsBlob } from '../cloud/rush.js';
23
22
  export const LEASE_RUNTIMES = [
24
23
  { id: 'claude', label: 'Claude Code', localCandidates: ['.claude/.claude.json', '.claude.json'], remote: '.claude.json' },
25
24
  { id: 'codex', label: 'Codex CLI', localCandidates: ['.codex/auth.json'], remote: '.codex/auth.json' },
@@ -193,8 +192,12 @@ function isClaudeCredentialsBlob(s) {
193
192
  * versioned home) uses a hash-suffixed service, so we try the bare service first,
194
193
  * then enumerate installed version homes (preferring the account whose email
195
194
  * matches `preferEmail`, so the token matches the `.claude.json` config we copy).
196
- * Off macOS the local Claude CLI stores the token in `.credentials.json` already —
197
- * reuse the rush.ts Linux branch verbatim.
195
+ * Off macOS the local Claude CLI stores the wrapped rotating blob in
196
+ * `.credentials.json` already. Read that file here with a shape check
197
+ * (`claudeAiOauth.accessToken`). Do not reintroduce a shared helper that
198
+ * also serves Rush Cloud dispatch — dispatch is email-only (SING-1b) and
199
+ * the old `readClaudeCredentialsBlob` path was the #1767 TTY-banner leak
200
+ * (RUSH-2359).
198
201
  *
199
202
  * The reader/service/version helpers are injected so unit tests never touch the
200
203
  * real Keychain.
@@ -248,9 +251,22 @@ export async function resolveClaudeCredentialsBlob(opts) {
248
251
  }
249
252
  return null;
250
253
  }
251
- // Off darwin: the local Claude CLI already stores the wrapped blob on disk.
254
+ // Off darwin: the local Claude CLI stores the wrapped rotating blob on disk.
255
+ // Read it directly so a file-based setup-token (Rush Cloud dispatch) is not
256
+ // mistaken for a native OAuth login that SING-1b must refuse to copy.
252
257
  const home = process.env.AGENTS_REAL_HOME || os.homedir();
253
- return readClaudeCredentialsBlob(home);
258
+ const credsPath = path.join(home, '.claude', '.credentials.json');
259
+ try {
260
+ if (fs.existsSync(credsPath)) {
261
+ const raw = fs.readFileSync(credsPath, 'utf-8').trim();
262
+ if (raw && isClaudeCredentialsBlob(raw))
263
+ return raw;
264
+ }
265
+ }
266
+ catch {
267
+ /* no readable credentials file */
268
+ }
269
+ return null;
254
270
  }
255
271
  /**
256
272
  * The credential-provisioning snippet for a `--lease` box.