@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.
Files changed (249) hide show
  1. package/CHANGELOG.md +401 -17
  2. package/README.md +66 -32
  3. package/dist/bootstrap.js +4 -2
  4. package/dist/cli/command-registry.d.ts +1 -0
  5. package/dist/cli/command-registry.js +2 -0
  6. package/dist/commands/accounts.d.ts +58 -0
  7. package/dist/commands/accounts.js +206 -32
  8. package/dist/commands/artifacts.js +7 -6
  9. package/dist/commands/attach.js +1 -1
  10. package/dist/commands/browser.js +501 -103
  11. package/dist/commands/config.d.ts +0 -1
  12. package/dist/commands/config.js +0 -20
  13. package/dist/commands/daemon.d.ts +21 -0
  14. package/dist/commands/daemon.js +147 -12
  15. package/dist/commands/exec.js +52 -15
  16. package/dist/commands/focus.d.ts +14 -0
  17. package/dist/commands/focus.js +48 -5
  18. package/dist/commands/go.d.ts +3 -0
  19. package/dist/commands/go.js +16 -3
  20. package/dist/commands/inspect.js +2 -2
  21. package/dist/commands/lease.d.ts +1 -1
  22. package/dist/commands/lease.js +4 -3
  23. package/dist/commands/mailboxes.js +4 -3
  24. package/dist/commands/menubar.js +11 -4
  25. package/dist/commands/projects.d.ts +16 -1
  26. package/dist/commands/projects.js +58 -64
  27. package/dist/commands/resume.js +1 -1
  28. package/dist/commands/route.js +4 -4
  29. package/dist/commands/routines.js +3 -2
  30. package/dist/commands/run-broadcast.d.ts +1 -1
  31. package/dist/commands/run-broadcast.js +1 -1
  32. package/dist/commands/sessions-picker.d.ts +22 -0
  33. package/dist/commands/sessions-picker.js +48 -9
  34. package/dist/commands/sessions-trace.js +1 -1
  35. package/dist/commands/sessions.js +1 -0
  36. package/dist/commands/setup-secrets.js +23 -0
  37. package/dist/commands/share.d.ts +20 -5
  38. package/dist/commands/share.js +152 -52
  39. package/dist/commands/ssh.js +54 -7
  40. package/dist/commands/traces.d.ts +2 -0
  41. package/dist/commands/traces.js +184 -0
  42. package/dist/commands/view.d.ts +1 -1
  43. package/dist/commands/view.js +17 -34
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.js +5 -0
  46. package/dist/lib/accounting/usage.d.ts +14 -3
  47. package/dist/lib/accounting/usage.js +64 -18
  48. package/dist/lib/agent-spec/agents.js +11 -9
  49. package/dist/lib/browser/drivers/ssh.d.ts +16 -2
  50. package/dist/lib/browser/drivers/ssh.js +36 -9
  51. package/dist/lib/browser/ipc.js +8 -3
  52. package/dist/lib/browser/profiles.d.ts +35 -101
  53. package/dist/lib/browser/profiles.js +130 -301
  54. package/dist/lib/browser/registry.d.ts +39 -0
  55. package/dist/lib/browser/registry.js +149 -0
  56. package/dist/lib/browser/remote-control.d.ts +8 -6
  57. package/dist/lib/browser/remote-control.js +8 -6
  58. package/dist/lib/browser/resolve-target.d.ts +68 -0
  59. package/dist/lib/browser/resolve-target.js +250 -0
  60. package/dist/lib/browser/runtime-state.d.ts +40 -16
  61. package/dist/lib/browser/runtime-state.js +92 -25
  62. package/dist/lib/browser/service.d.ts +28 -9
  63. package/dist/lib/browser/service.js +139 -62
  64. package/dist/lib/browser/task-index.d.ts +70 -0
  65. package/dist/lib/browser/task-index.js +180 -0
  66. package/dist/lib/byok-usage.js +2 -2
  67. package/dist/lib/claude-statusline.d.ts +29 -0
  68. package/dist/lib/claude-statusline.js +139 -0
  69. package/dist/lib/cloud/host.d.ts +1 -1
  70. package/dist/lib/cloud/host.js +1 -1
  71. package/dist/lib/cloud/rush.js +1 -1
  72. package/dist/lib/computer/computer-rpc.js +2 -2
  73. package/dist/lib/computer/download.d.ts +15 -15
  74. package/dist/lib/computer/download.js +35 -98
  75. package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
  76. package/dist/lib/computer/ssh-tunnel.js +12 -22
  77. package/dist/lib/config-keys.d.ts +2 -8
  78. package/dist/lib/config-keys.js +2 -13
  79. package/dist/lib/crabbox/cli.d.ts +1 -1
  80. package/dist/lib/crabbox/cli.js +2 -2
  81. package/dist/lib/crabbox/lease.d.ts +9 -0
  82. package/dist/lib/crabbox/lease.js +70 -15
  83. package/dist/lib/crabbox/runtimes.js +1 -1
  84. package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
  85. package/dist/lib/daemon/account-state-daemon-service.js +26 -0
  86. package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
  87. package/dist/lib/daemon/browser-ipc-service.js +46 -0
  88. package/dist/lib/daemon/daemon.d.ts +3 -0
  89. package/dist/lib/daemon/daemon.js +212 -444
  90. package/dist/lib/daemon/device-probe-service.d.ts +26 -0
  91. package/dist/lib/daemon/device-probe-service.js +47 -0
  92. package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
  93. package/dist/lib/daemon/keychain-reap-service.js +32 -0
  94. package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
  95. package/dist/lib/daemon/monitor-engine-service.js +29 -0
  96. package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
  97. package/dist/lib/daemon/secrets-broker-service.js +66 -0
  98. package/dist/lib/daemon/self-heal-service.d.ts +27 -0
  99. package/dist/lib/daemon/self-heal-service.js +48 -0
  100. package/dist/lib/daemon/service.d.ts +102 -0
  101. package/dist/lib/daemon/service.js +98 -0
  102. package/dist/lib/daemon/session-index-service.d.ts +22 -0
  103. package/dist/lib/daemon/session-index-service.js +40 -0
  104. package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
  105. package/dist/lib/daemon/state-dir-check-service.js +63 -0
  106. package/dist/lib/daemon/supervisor.d.ts +87 -0
  107. package/dist/lib/daemon/supervisor.js +298 -0
  108. package/dist/lib/daemon/watchdog-service.d.ts +19 -0
  109. package/dist/lib/daemon/watchdog-service.js +40 -0
  110. package/dist/lib/daemon-health.d.ts +15 -0
  111. package/dist/lib/daemon-health.js +29 -5
  112. package/dist/lib/daemon-services.d.ts +8 -1
  113. package/dist/lib/daemon-services.js +55 -0
  114. package/dist/lib/daemon-ticks.d.ts +13 -2
  115. package/dist/lib/daemon-ticks.js +20 -18
  116. package/dist/lib/device-config.d.ts +0 -2
  117. package/dist/lib/device-config.js +1 -23
  118. package/dist/lib/devices/registry.d.ts +1 -1
  119. package/dist/lib/devices/registry.js +1 -1
  120. package/dist/lib/devices/stats-cache.d.ts +20 -0
  121. package/dist/lib/devices/stats-cache.js +49 -5
  122. package/dist/lib/devices/worker-pick.d.ts +48 -0
  123. package/dist/lib/devices/worker-pick.js +113 -0
  124. package/dist/lib/exec.d.ts +67 -13
  125. package/dist/lib/exec.js +85 -29
  126. package/dist/lib/harness/adapters/cursor.js +8 -11
  127. package/dist/lib/helper-download.d.ts +108 -0
  128. package/dist/lib/helper-download.js +194 -0
  129. package/dist/lib/helper-versions.d.ts +52 -0
  130. package/dist/lib/helper-versions.js +58 -0
  131. package/dist/lib/hooks/cache.js +11 -0
  132. package/dist/lib/hooks/install.js +2 -2
  133. package/dist/lib/hosts/credentials.d.ts +1 -1
  134. package/dist/lib/hosts/credentials.js +1 -1
  135. package/dist/lib/hosts/dispatch.d.ts +7 -1
  136. package/dist/lib/hosts/dispatch.js +24 -5
  137. package/dist/lib/hosts/passthrough.d.ts +8 -15
  138. package/dist/lib/hosts/passthrough.js +19 -16
  139. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  140. package/dist/lib/hosts/providers/devices.js +1 -1
  141. package/dist/lib/hosts/reconnect.d.ts +179 -21
  142. package/dist/lib/hosts/reconnect.js +266 -45
  143. package/dist/lib/installations/migrate.d.ts +4 -5
  144. package/dist/lib/installations/migrate.js +6 -7
  145. package/dist/lib/installations/shims.d.ts +1 -1
  146. package/dist/lib/installations/shims.js +23 -13
  147. package/dist/lib/installations/versions.js +25 -2
  148. package/dist/lib/linear-autoclose.d.ts +1 -1
  149. package/dist/lib/linear-autoclose.js +1 -1
  150. package/dist/lib/loop.d.ts +2 -2
  151. package/dist/lib/loop.js +1 -1
  152. package/dist/lib/menubar/download-menubar.d.ts +47 -0
  153. package/dist/lib/menubar/download-menubar.js +60 -0
  154. package/dist/lib/menubar/install-menubar.d.ts +31 -7
  155. package/dist/lib/menubar/install-menubar.js +79 -21
  156. package/dist/lib/menubar/notify-desktop.d.ts +3 -3
  157. package/dist/lib/menubar/notify-desktop.js +6 -6
  158. package/dist/lib/model-tiers.d.ts +1 -1
  159. package/dist/lib/project-probe.d.ts +1 -1
  160. package/dist/lib/project-probe.js +1 -1
  161. package/dist/lib/project-pull.d.ts +1 -1
  162. package/dist/lib/project-pull.js +2 -2
  163. package/dist/lib/projects.d.ts +1 -1
  164. package/dist/lib/projects.js +1 -1
  165. package/dist/lib/sandbox.js +6 -7
  166. package/dist/lib/secrets/download-keychain.d.ts +47 -0
  167. package/dist/lib/secrets/download-keychain.js +70 -0
  168. package/dist/lib/secrets/install-helper.d.ts +20 -1
  169. package/dist/lib/secrets/install-helper.js +44 -3
  170. package/dist/lib/session/active.js +17 -1
  171. package/dist/lib/session/db.d.ts +11 -1
  172. package/dist/lib/session/db.js +138 -6
  173. package/dist/lib/session/discover.d.ts +21 -1
  174. package/dist/lib/session/discover.js +33 -5
  175. package/dist/lib/session/highlights.d.ts +49 -1
  176. package/dist/lib/session/highlights.js +95 -0
  177. package/dist/lib/session/host-link.d.ts +16 -2
  178. package/dist/lib/session/host-link.js +7 -1
  179. package/dist/lib/session/parse.js +33 -1
  180. package/dist/lib/session/remote/remote-list.d.ts +9 -0
  181. package/dist/lib/session/remote/remote-list.js +19 -2
  182. package/dist/lib/session/remote/watch.js +11 -2
  183. package/dist/lib/session/session-cache.d.ts +53 -0
  184. package/dist/lib/session/session-cache.js +88 -0
  185. package/dist/lib/session/state.js +1 -1
  186. package/dist/lib/session/tool-calls.d.ts +2 -0
  187. package/dist/lib/session/tool-calls.js +1 -1
  188. package/dist/lib/session/trajectory-html.d.ts +5 -1
  189. package/dist/lib/session/trajectory-html.js +42 -22
  190. package/dist/lib/session/trajectory-text.js +9 -2
  191. package/dist/lib/session/trajectory.js +53 -4
  192. package/dist/lib/session/types.d.ts +15 -0
  193. package/dist/lib/sha256-asset.d.ts +26 -0
  194. package/dist/lib/sha256-asset.js +41 -0
  195. package/dist/lib/share/backend.d.ts +95 -0
  196. package/dist/lib/share/backend.js +139 -0
  197. package/dist/lib/share/delete.d.ts +6 -2
  198. package/dist/lib/share/delete.js +8 -9
  199. package/dist/lib/share/html.d.ts +22 -0
  200. package/dist/lib/share/html.js +88 -0
  201. package/dist/lib/share/provision.d.ts +18 -4
  202. package/dist/lib/share/provision.js +38 -4
  203. package/dist/lib/share/publish.d.ts +39 -15
  204. package/dist/lib/share/publish.js +55 -26
  205. package/dist/lib/share/worker-template.js +171 -11
  206. package/dist/lib/sqlite.js +16 -3
  207. package/dist/lib/ssh-exec.d.ts +65 -0
  208. package/dist/lib/ssh-exec.js +120 -4
  209. package/dist/lib/startup/command-registry.js +1 -1
  210. package/dist/lib/startup/dev-build.js +3 -3
  211. package/dist/lib/state.d.ts +1 -1
  212. package/dist/lib/state.js +7 -3
  213. package/dist/lib/subagents-registry.js +1 -1
  214. package/dist/lib/terminal/index.d.ts +1 -1
  215. package/dist/lib/terminal/types.d.ts +1 -1
  216. package/dist/lib/terminal/types.js +1 -1
  217. package/dist/lib/tmux/orphan-reap.js +1 -1
  218. package/dist/lib/tmux/session.js +31 -0
  219. package/dist/lib/traces/backend.d.ts +29 -0
  220. package/dist/lib/traces/backend.js +46 -0
  221. package/dist/lib/traces/classify.d.ts +52 -0
  222. package/dist/lib/traces/classify.js +72 -0
  223. package/dist/lib/traces/config.d.ts +10 -0
  224. package/dist/lib/traces/config.js +2 -0
  225. package/dist/lib/traces/provision.d.ts +19 -0
  226. package/dist/lib/traces/provision.js +30 -0
  227. package/dist/lib/traces/sync.d.ts +150 -0
  228. package/dist/lib/traces/sync.js +447 -0
  229. package/dist/lib/traces/worker-template.d.ts +4 -0
  230. package/dist/lib/traces/worker-template.js +159 -0
  231. package/dist/lib/types.d.ts +18 -19
  232. package/dist/lib/types.js +14 -0
  233. package/dist/lib/usage-refresh.d.ts +1 -1
  234. package/dist/lib/usage-refresh.js +11 -8
  235. package/dist/lib/workflows.d.ts +1 -1
  236. package/package.json +4 -6
  237. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  238. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
  239. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  240. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  241. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
  242. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  243. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -26
  244. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  245. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
  246. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -135
  247. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  248. package/dist/lib/usage-fleet.d.ts +0 -32
  249. package/dist/lib/usage-fleet.js +0 -125
@@ -1,8 +1,9 @@
1
- import type { Command } from 'commander';
1
+ import { type Command } from 'commander';
2
2
  import { type ShareConfig } from '../lib/share/config.js';
3
3
  import { type CloudflareRequester } from '../lib/share/provision.js';
4
4
  import { type PublishResult } from '../lib/share/publish.js';
5
5
  import { deleteShare, type DeleteShareResult } from '../lib/share/delete.js';
6
+ import type { PhoenixSession } from '../lib/identity/client.js';
6
7
  export declare function formatSharePublishResult(result: PublishResult, json?: boolean): string;
7
8
  /** Compare the configured endpoint's last-deployed template hash against the
8
9
  * hash of the CURRENT `worker-template.ts` render. A config with no recorded
@@ -61,10 +62,16 @@ export declare function parseShareListing(user: string, body: string): ShareList
61
62
  * Worker's `?format=json` route. Fails loud when share was never configured, when
62
63
  * the deployed template is known-outdated (RUSH-2449 templateHash), or when the
63
64
  * live response proves the route is absent (404 or a non-JSON 200 = the old HTML
64
- * gallery) — never a silent empty/wrong result. */
65
+ * gallery) — never a silent empty/wrong result. Signed-in users hit the managed
66
+ * endpoint; BYO still uses `readShareConfig()`. */
65
67
  export declare function runShareList(opts?: {
66
68
  githubUser?: string;
67
69
  config?: ShareConfig;
70
+ writeToken?: string;
71
+ byo?: boolean;
72
+ /** DI seam — override `readSession()`. Named `phoenixSession` because
73
+ * `session` is already the listing filter (session id). */
74
+ phoenixSession?: PhoenixSession | null;
68
75
  fetchListing?: ListingFetchFn;
69
76
  /** Only shares published by this agent/harness (case-insensitive, exact). */
70
77
  agent?: string;
@@ -111,6 +118,9 @@ export declare function parseShareRevisions(key: string, body: string): ShareRev
111
118
  export declare function runShareRevisions(target: string, opts?: {
112
119
  githubUser?: string;
113
120
  config?: ShareConfig;
121
+ writeToken?: string;
122
+ byo?: boolean;
123
+ session?: PhoenixSession | null;
114
124
  fetchListing?: ListingFetchFn;
115
125
  }): Promise<ShareRevisionsResult>;
116
126
  export declare function formatShareRevisions(result: ShareRevisionsResult, json?: boolean): string;
@@ -156,6 +166,8 @@ export declare function runShareProvision(opts: {
156
166
  domain?: string;
157
167
  analyticsToken?: string;
158
168
  request?: CloudflareRequester;
169
+ /** Bind PHOENIX_ID_BASE even when the mapped hostname is not the managed domain. */
170
+ managed?: boolean;
159
171
  }): Promise<void>;
160
172
  export interface ShareUpdateResult {
161
173
  updated: boolean;
@@ -167,15 +179,18 @@ export interface ShareUpdateResult {
167
179
  * current `worker-template.ts`. Reuses the existing account/worker/bucket and
168
180
  * write token from `readShareConfig()`/the `share` bundle — never creates a
169
181
  * bucket, touches routes/domains, or regenerates the token (see
170
- * `updateWorker` in `lib/share/provision.ts` for how the token survives the
171
- * re-upload). Idempotent: no-ops when the deployed hash already matches
172
- * unless `force`. */
182
+ * `updateWorker` in `lib/share/provision.ts` for how the token and, on a
183
+ * managed endpoint, `PHOENIX_ID_BASE` — survives the re-upload). Idempotent:
184
+ * no-ops the script upload when the deployed hash already matches unless
185
+ * `force`; a managed endpoint still re-applies `PHOENIX_ID_BASE`. */
173
186
  export declare function runShareUpdate(opts?: {
174
187
  bundle?: string;
175
188
  account?: string;
176
189
  token?: string;
177
190
  force?: boolean;
178
191
  request?: CloudflareRequester;
192
+ /** Bind PHOENIX_ID_BASE even when the configured hostname is not the managed domain. */
193
+ managed?: boolean;
179
194
  }): Promise<ShareUpdateResult>;
180
195
  /** Join an existing share endpoint (no provisioning): prompt for the endpoint
181
196
  * details + write token and persist them. Shared by `agents artifacts share join`
@@ -1,18 +1,21 @@
1
- // `agents artifacts share` — publish an HTML file to your own Cloudflare R2
2
- // behind a tiny Worker, and get a shareable link (~$0). See apps/cli/docs/share.md.
1
+ // `agents artifacts share` — publish an HTML file to a shareable link. Signed-in
2
+ // users hit the managed endpoint (share.agents-cli.sh) with the Phoenix bearer;
3
+ // otherwise the existing BYO Cloudflare R2 + Worker path. See cli/docs/observability.md.
3
4
  //
4
5
  // Registered under the `artifacts` group by commands/artifacts.ts; the
5
6
  // provisioning door lives beside it at `agents artifacts setup`
6
7
  // (commands/artifacts-setup.ts), which calls `runShareProvision` below.
7
8
  import { existsSync } from 'node:fs';
8
9
  import { formatBytes } from '../lib/format.js';
10
+ import { Option } from 'commander';
9
11
  import chalk from 'chalk';
10
12
  import { DEFAULT_BUCKET_NAME, DEFAULT_CF_BUNDLE, DEFAULT_SHARE_DOMAIN, DEFAULT_WORKER_NAME, generateWriteToken, readCloudflareCreds, readShareConfig, readWriteToken, readWriteTokenEnv, readWriteTokenFromBundle, storeWriteToken, writeShareConfig, } from '../lib/share/config.js';
11
- import { addCustomDomain, configureBucketLifecycle, createBucket, deployWorker, enableWorkersDev, findZoneId, hashWorkerScript, updateWorker, setWorkerSecret, } from '../lib/share/provision.js';
12
- import { publishFile, resolveShareUsername, parseMetaEntries } from '../lib/share/publish.js';
13
+ import { addCustomDomain, configureBucketLifecycle, createBucket, deployWorker, enableWorkersDev, findZoneId, hashWorkerScript, putWorkerSecret, updateWorker, WORKER_PHOENIX_ID_BASE_SECRET, setWorkerSecret, } from '../lib/share/provision.js';
14
+ import { publishFile, resolveShareUsername, parseMetaEntries, resolveShareVisibility, } from '../lib/share/publish.js';
13
15
  import { deleteShare, resolveDeleteTarget } from '../lib/share/delete.js';
14
16
  import { renderWorkerScript } from '../lib/share/worker-template.js';
15
17
  import { analyticsEnabled } from '../lib/share/analytics.js';
18
+ import { phoenixIdBaseForDeploy, resolveShareBackend, } from '../lib/share/backend.js';
16
19
  import { resolveGitHubUsername } from '../lib/git.js';
17
20
  import { setHelpSections } from '../lib/help.js';
18
21
  export function formatSharePublishResult(result, json = false) {
@@ -29,8 +32,13 @@ export function formatSharePublishResult(result, json = false) {
29
32
  lines.push(chalk.dim(` expires ${new Date(result.expiresAt).toLocaleString()}`));
30
33
  else
31
34
  lines.push(chalk.dim(' expires never'));
32
- if (result.unlisted)
33
- lines.push(chalk.dim(' unlisted (hidden from gallery / share list)'));
35
+ const visibility = result.visibility ?? (result.unlisted ? 'unlisted' : 'public');
36
+ if (visibility === 'unlisted') {
37
+ lines.push(chalk.dim(' visibility: unlisted (noindex, hidden from gallery)'));
38
+ }
39
+ else {
40
+ lines.push(chalk.dim(` visibility: ${visibility}`));
41
+ }
34
42
  return lines.join('\n');
35
43
  }
36
44
  /** Compare the configured endpoint's last-deployed template hash against the
@@ -100,25 +108,42 @@ export function parseShareListing(user, body) {
100
108
  });
101
109
  return { user, count: objects.length, objects };
102
110
  }
111
+ /** Namespace for list/revisions: managed uses the Phoenix handle (email
112
+ * local-part); BYO still resolves GitHub username (gh / git config / --for-user). */
113
+ async function namespaceForBackend(backend, githubUser) {
114
+ if (backend.kind === 'managed')
115
+ return backend.namespace;
116
+ return resolveShareUsername({ githubUser: githubUser || backend.namespace || undefined });
117
+ }
103
118
  /** Fetch and parse the machine-readable listing of the caller's namespace from the
104
119
  * Worker's `?format=json` route. Fails loud when share was never configured, when
105
120
  * the deployed template is known-outdated (RUSH-2449 templateHash), or when the
106
121
  * live response proves the route is absent (404 or a non-JSON 200 = the old HTML
107
- * gallery) — never a silent empty/wrong result. */
122
+ * gallery) — never a silent empty/wrong result. Signed-in users hit the managed
123
+ * endpoint; BYO still uses `readShareConfig()`. */
108
124
  export async function runShareList(opts = {}) {
109
- const cfg = opts.config ?? readShareConfig();
110
- if (!cfg) {
111
- throw new Error("Not set up yet. Run 'agents artifacts setup' (provision your own endpoint) or 'agents artifacts share join' (use an existing one).");
112
- }
113
- // A known-stale template can't have the listing route — say so before any network
114
- // call. 'unknown' (provisioned before templateHash tracking) is attempted, then
115
- // caught by the response checks below if the route turns out to be absent.
116
- const templateStatus = shareTemplateStatus(cfg);
117
- if (templateStatus === 'outdated') {
118
- throw new Error(OUTDATED_TEMPLATE_HINT);
125
+ const backend = resolveShareBackend({
126
+ githubUser: opts.githubUser,
127
+ config: opts.config,
128
+ writeToken: opts.writeToken,
129
+ byo: opts.byo,
130
+ session: opts.phoenixSession,
131
+ requireToken: false,
132
+ });
133
+ // Managed: the platform Worker is current; skip the BYO template-hash gate.
134
+ // A known-stale BYO template can't have the listing route — say so before any
135
+ // network call. 'unknown' (provisioned before templateHash tracking) is
136
+ // attempted, then caught by the response checks below if the route is absent.
137
+ let templateStatus = 'current';
138
+ if (backend.kind === 'byo') {
139
+ const cfg = opts.config ?? readShareConfig();
140
+ templateStatus = cfg ? shareTemplateStatus(cfg) : 'unknown';
141
+ if (templateStatus === 'outdated') {
142
+ throw new Error(OUTDATED_TEMPLATE_HINT);
143
+ }
119
144
  }
120
- const user = await resolveShareUsername({ githubUser: opts.githubUser });
121
- const listUrl = `${cfg.baseUrl.replace(/\/+$/, '')}/${encodeURIComponent(user)}?format=json`;
145
+ const user = await namespaceForBackend(backend, opts.githubUser);
146
+ const listUrl = `${backend.baseUrl.replace(/\/+$/, '')}/${encodeURIComponent(user)}?format=json`;
122
147
  const fetchListing = opts.fetchListing ?? defaultListingFetch;
123
148
  const res = await fetchListing(listUrl);
124
149
  if (res.status === 404) {
@@ -128,18 +153,22 @@ export async function runShareList(opts = {}) {
128
153
  // endpoint predates the listing route entirely. The recorded templateHash
129
154
  // disambiguates: a 'current' template HAS the route, so its 404 means an empty
130
155
  // namespace ("nothing published"); otherwise the route may be absent, so point
131
- // at `agents artifacts share update`.
156
+ // at `agents artifacts share update`. Managed is always 'current'.
132
157
  if (templateStatus === 'current') {
133
158
  return { user, count: 0, objects: [] };
134
159
  }
135
160
  throw new Error(OUTDATED_TEMPLATE_HINT);
136
161
  }
137
162
  if (res.status !== 200) {
138
- throw new Error(`Listing failed (${res.status}) for ${listUrl}. Check the endpoint is reachable, or that 'agents artifacts setup' completed.`);
163
+ throw new Error(`Listing failed (${res.status}) for ${listUrl}. Check the endpoint is reachable, or that 'agents artifacts setup' / 'agents auth login' completed.`);
139
164
  }
140
165
  if (!/application\/json/i.test(res.contentType)) {
141
166
  // A 200 that isn't JSON means the old Worker ignored ?format=json and served
142
- // the HTML gallery — the deployed template is outdated.
167
+ // the HTML gallery — the deployed template is outdated. Managed never hits
168
+ // this (platform Worker is current); fail loud if it somehow does.
169
+ if (backend.kind === 'managed') {
170
+ throw new Error(`Managed listing at ${listUrl} did not return JSON (${res.contentType || 'no content-type'}).`);
171
+ }
143
172
  throw new Error(OUTDATED_TEMPLATE_HINT);
144
173
  }
145
174
  const parsed = parseShareListing(user, res.body);
@@ -239,22 +268,34 @@ export function parseShareRevisions(key, body) {
239
268
  * or a bare slug resolved against the caller's own namespace) — reuses
240
269
  * {@link resolveDeleteTarget} rather than re-deriving the key. */
241
270
  export async function runShareRevisions(target, opts = {}) {
242
- const cfg = opts.config ?? readShareConfig();
243
- if (!cfg) {
244
- throw new Error("Not set up yet. Run 'agents artifacts setup' (provision your own endpoint) or 'agents artifacts share join' (use an existing one).");
245
- }
246
- const templateStatus = shareTemplateStatus(cfg);
247
- if (templateStatus === 'outdated') {
248
- throw new Error(OUTDATED_TEMPLATE_HINT);
271
+ const backend = resolveShareBackend({
272
+ githubUser: opts.githubUser,
273
+ config: opts.config,
274
+ writeToken: opts.writeToken,
275
+ byo: opts.byo,
276
+ session: opts.session,
277
+ requireToken: false,
278
+ });
279
+ if (backend.kind === 'byo') {
280
+ const cfg = opts.config ?? readShareConfig();
281
+ if (cfg && shareTemplateStatus(cfg) === 'outdated') {
282
+ throw new Error(OUTDATED_TEMPLATE_HINT);
283
+ }
249
284
  }
250
- const { key } = await resolveDeleteTarget(target, { githubUser: opts.githubUser });
251
- const revUrl = `${cfg.baseUrl.replace(/\/+$/, '')}/${key}?revisions=json`;
285
+ const githubUser = backend.kind === 'managed'
286
+ ? backend.namespace
287
+ : opts.githubUser || backend.namespace || undefined;
288
+ const { key } = await resolveDeleteTarget(target, { githubUser });
289
+ const revUrl = `${backend.baseUrl.replace(/\/+$/, '')}/${key}?revisions=json`;
252
290
  const fetchListing = opts.fetchListing ?? defaultListingFetch;
253
291
  const res = await fetchListing(revUrl);
254
292
  if (res.status !== 200) {
255
- throw new Error(`Revisions lookup failed (${res.status}) for ${revUrl}. Check the endpoint is reachable, or that 'agents artifacts setup' completed.`);
293
+ throw new Error(`Revisions lookup failed (${res.status}) for ${revUrl}. Check the endpoint is reachable, or that 'agents artifacts setup' / 'agents auth login' completed.`);
256
294
  }
257
295
  if (!/application\/json/i.test(res.contentType)) {
296
+ if (backend.kind === 'managed') {
297
+ throw new Error(`Managed revisions at ${revUrl} did not return JSON (${res.contentType || 'no content-type'}).`);
298
+ }
258
299
  throw new Error(OUTDATED_TEMPLATE_HINT);
259
300
  }
260
301
  return parseShareRevisions(key, res.body);
@@ -390,13 +431,16 @@ const SHARE_DELETE_NOTES = `
390
431
  export function registerShareCommands(artifactsCmd) {
391
432
  const shareCmd = artifactsCmd
392
433
  .command('share')
393
- .description('Publish an HTML file to your own Cloudflare R2 and get a shareable link (~$0).')
434
+ .description('Publish an HTML file to a shareable link managed if signed in, otherwise your Cloudflare R2.')
394
435
  .argument('[file]', 'file to publish (HTML or any static asset)')
395
- .option('--slug <slug>', 'custom URL slug under your namespace (default: <project>-<feature>-<hash>)')
396
- .option('--github-user <user>', 'GitHub username for the share namespace (default: resolved from gh/git config)')
436
+ .option('--slug <slug>', 'custom URL slug under your namespace (default: <feature>-<16hex>)')
437
+ .option('--github-user <user>', 'GitHub username for the share namespace (default: resolved from gh/git config; ignored on the managed endpoint)')
397
438
  .option('--expire <spec>', "auto-expire (default 30d). e.g. 12h, 30d, 2026-08-01, or 'never'")
398
- .option('--unlisted', 'hide from the public gallery and `agents artifacts share list` (direct URL still works)')
399
- .option('--private', 'alias of --unlisted')
439
+ .addOption(new Option('--visibility <level>', 'public | unlisted (default public). unlisted is a capability URL: GET still works, hidden from the gallery, X-Robots-Tag: noindex')
440
+ .choices(['public', 'unlisted'])
441
+ .default('public'))
442
+ .addOption(new Option('--unlisted', 'hidden alias of --visibility unlisted').hideHelp())
443
+ .addOption(new Option('--private', 'hidden alias of --visibility unlisted').hideHelp())
400
444
  .option('--force', 'publish even when the file contains emails or credential-shaped strings')
401
445
  .option('--no-cover', 'skip the OG preview image (HTML pages get one by default)')
402
446
  .option('--no-analytics', 'skip injecting the Cloudflare Web Analytics beacon')
@@ -417,11 +461,16 @@ export function registerShareCommands(artifactsCmd) {
417
461
  }
418
462
  try {
419
463
  const meta = parseMetaEntries(opts.meta);
464
+ const visibility = resolveShareVisibility({
465
+ visibility: opts.visibility,
466
+ unlisted: opts.unlisted === true || opts.private === true,
467
+ });
420
468
  const result = await publishFile(file, {
421
469
  slug: opts.slug,
422
470
  githubUser: opts.githubUser,
423
471
  expire: opts.expire,
424
- unlisted: opts.unlisted === true || opts.private === true,
472
+ visibility,
473
+ unlisted: visibility === 'unlisted',
425
474
  force: opts.force,
426
475
  cover: opts.cover,
427
476
  analytics: opts.analytics,
@@ -438,11 +487,12 @@ export function registerShareCommands(artifactsCmd) {
438
487
  });
439
488
  setHelpSections(shareCmd, {
440
489
  examples: `
441
- # Publish an HTML fileauto OG cover, default 30d expiry, shareable link
490
+ # Signed in? Just publishno Cloudflare setup (managed endpoint)
491
+ agents auth login
442
492
  agents artifacts share ./out/plan.html
443
493
 
444
- # Hide from the public gallery (direct URL still works) and expire sooner
445
- agents artifacts share ./out/report.html --unlisted --expire 12h
494
+ # Hide from the public gallery (direct URL still works, noindex) and expire sooner
495
+ agents artifacts share ./out/report.html --visibility unlisted --expire 12h
446
496
 
447
497
  # Permanent public page (opt out of the default 30d expiry)
448
498
  agents artifacts share ./out/landing.html --slug landing --expire never
@@ -464,10 +514,18 @@ ${SHARE_DELETE_EXAMPLES}
464
514
  agents artifacts share update
465
515
  `,
466
516
  notes: `
517
+ Signed-in users publish to the managed endpoint (share.agents-cli.sh/<handle>/…)
518
+ with the Phoenix session — no Cloudflare account, bucket, or write token. The
519
+ handle is the local-part of the signed-in email; the default slug is
520
+ <readable>-<16hex>. Without a session, the existing BYO Cloudflare path still
521
+ applies (setup / join). HTML is stored as one object: local images are inlined
522
+ and Chrome-saved file:// TOC links become in-page hashes.
523
+
467
524
  Default expiry is 30d so an accidental publish decays. Pass --expire never for
468
- a permanent link. --unlisted / --private hides the page from the public gallery
469
- and agents artifacts share list; the direct URL is still world-readable
470
- (unlisted, not secret). A pre-publish scan refuses emails and credential-shaped
525
+ a permanent link. --visibility unlisted (hidden aliases: --unlisted / --private)
526
+ hides the page from the public gallery and agents artifacts share list; the
527
+ direct URL is still world-readable (unlisted, not secret) and GET sends
528
+ X-Robots-Tag: noindex. A pre-publish scan refuses emails and credential-shaped
471
529
  strings unless --force is passed.
472
530
 
473
531
  Every publish carries provenance auto-captured from the exec env/git/clock
@@ -549,11 +607,27 @@ ${SHARE_DELETE_NOTES}
549
607
  .command('status')
550
608
  .description('Show the configured share endpoint and namespace.')
551
609
  .action(async () => {
610
+ let backend;
611
+ try {
612
+ backend = resolveShareBackend({ requireToken: false });
613
+ }
614
+ catch (e) {
615
+ console.log(chalk.dim(e.message));
616
+ return;
617
+ }
618
+ if (backend.kind === 'managed') {
619
+ console.log(`${chalk.bold('backend')} ${chalk.green('managed')}`);
620
+ console.log(`${chalk.bold('endpoint')} ${chalk.green(backend.baseUrl)}`);
621
+ console.log(`${chalk.bold('namespace')} ${chalk.cyan(`${backend.baseUrl}/${backend.namespace}`)}`);
622
+ console.log(chalk.dim("Phoenix session — no Cloudflare setup. Force BYO with AGENTS_SHARE_BACKEND=byo."));
623
+ return;
624
+ }
552
625
  const cfg = readShareConfig();
553
626
  if (!cfg) {
554
627
  console.log(chalk.dim("Not configured. Run 'agents artifacts setup' or 'agents artifacts share join'."));
555
628
  return;
556
629
  }
630
+ console.log(`${chalk.bold('backend')} ${chalk.green('byo')}`);
557
631
  console.log(`${chalk.bold('endpoint')} ${chalk.green(cfg.baseUrl)}`);
558
632
  console.log(chalk.dim(`worker ${cfg.workerName} · bucket ${cfg.bucketName} · account ${cfg.accountId || 'missing'}`));
559
633
  if (!cfg.accountId) {
@@ -620,9 +694,10 @@ ${SHARE_DELETE_NOTES}
620
694
  `,
621
695
  notes: `
622
696
  Lists the ACTIVE pages in your namespace — expired links and the sibling .png OG
623
- covers are omitted (it mirrors the public gallery). It reads the endpoint's JSON
624
- listing route, which ships with the current Worker template. If your deployed
625
- Worker predates this feature the command says so and points you at 'agents
697
+ covers are omitted (it mirrors the public gallery). Signed-in users list the
698
+ managed endpoint (share.agents-cli.sh/<handle>); otherwise BYO. It reads the
699
+ endpoint's JSON listing route, which ships with the current Worker template. If
700
+ a BYO Worker predates this feature the command says so and points you at 'agents
626
701
  artifacts share update' (RUSH-2449) rather than returning a wrong or empty result
627
702
  — see 'agents artifacts share status' for whether an update is due.
628
703
 
@@ -669,13 +744,27 @@ ${SHARE_DELETE_NOTES}
669
744
  (pass --no-revision at publish time to skip it) — this is history, never
670
745
  shown on the public gallery or in 'agents artifacts share list' beyond its
671
746
  count. Each revision's own URL is still directly reachable and honors its own
672
- recorded expiry.
747
+ recorded expiry. Signed-in users resolve a bare slug against the managed
748
+ Phoenix namespace; otherwise BYO (GitHub username / --for-user).
673
749
  `,
674
750
  });
675
751
  shareCmd
676
752
  .command('analytics')
677
753
  .description('Show the Cloudflare Web Analytics status for this share endpoint.')
678
754
  .action(async () => {
755
+ let backend;
756
+ try {
757
+ backend = resolveShareBackend({ requireToken: false });
758
+ }
759
+ catch (e) {
760
+ console.log(chalk.dim(e.message));
761
+ return;
762
+ }
763
+ if (backend.kind === 'managed') {
764
+ console.log(chalk.yellow('Cloudflare Web Analytics is a BYO feature.'));
765
+ console.log(chalk.dim(`You're on the managed endpoint (${backend.baseUrl}). Per-page analytics for the managed endpoint is not in this release — provision your own Cloudflare with 'agents artifacts setup --analytics-token <token>', or force BYO with AGENTS_SHARE_BACKEND=byo.`));
766
+ return;
767
+ }
679
768
  const cfg = readShareConfig();
680
769
  if (!cfg) {
681
770
  console.log(chalk.dim("Not configured. Run 'agents artifacts setup' or 'agents artifacts share join'."));
@@ -755,6 +844,11 @@ export async function runShareProvision(opts) {
755
844
  spin.warn(`Zone for ${requestedDomain} not visible to this token — staying on workers.dev`);
756
845
  }
757
846
  }
847
+ const phoenixIdBase = phoenixIdBaseForDeploy({ managed: opts.managed }, { baseUrl, domain });
848
+ if (phoenixIdBase) {
849
+ await putWorkerSecret(apiToken, accountId, workerName, WORKER_PHOENIX_ID_BASE_SECRET, phoenixIdBase, provisionOpts);
850
+ spin.text = `Worker '${workerName}' Phoenix ID base set`;
851
+ }
758
852
  spin.succeed('Provisioned');
759
853
  const cfg = {
760
854
  baseUrl,
@@ -780,9 +874,10 @@ export async function runShareProvision(opts) {
780
874
  * current `worker-template.ts`. Reuses the existing account/worker/bucket and
781
875
  * write token from `readShareConfig()`/the `share` bundle — never creates a
782
876
  * bucket, touches routes/domains, or regenerates the token (see
783
- * `updateWorker` in `lib/share/provision.ts` for how the token survives the
784
- * re-upload). Idempotent: no-ops when the deployed hash already matches
785
- * unless `force`. */
877
+ * `updateWorker` in `lib/share/provision.ts` for how the token and, on a
878
+ * managed endpoint, `PHOENIX_ID_BASE` — survives the re-upload). Idempotent:
879
+ * no-ops the script upload when the deployed hash already matches unless
880
+ * `force`; a managed endpoint still re-applies `PHOENIX_ID_BASE`. */
786
881
  export async function runShareUpdate(opts = {}) {
787
882
  const cfg = readShareConfig();
788
883
  if (!cfg) {
@@ -798,7 +893,12 @@ export async function runShareUpdate(opts = {}) {
798
893
  }
799
894
  const writeToken = readWriteToken();
800
895
  const script = renderWorkerScript();
801
- const provisionOpts = { ...(opts.request ? { request: opts.request } : {}), force: opts.force };
896
+ const phoenixIdBase = phoenixIdBaseForDeploy({ managed: opts.managed }, cfg);
897
+ const provisionOpts = {
898
+ ...(opts.request ? { request: opts.request } : {}),
899
+ force: opts.force,
900
+ ...(phoenixIdBase !== undefined ? { phoenixIdBase } : {}),
901
+ };
802
902
  const result = await updateWorker(apiToken, accountId, cfg.workerName, cfg.bucketName, script, writeToken, cfg.templateHash, provisionOpts);
803
903
  if (!result.skipped) {
804
904
  writeShareConfig({ ...cfg, accountId, templateHash: result.templateHash });
@@ -124,11 +124,18 @@ function pctCell(v, width) {
124
124
  * header — which defeats the scannability this column exists for. */
125
125
  const SPEC_WIDTH_MIN = 12;
126
126
  /** The static hardware as one compact cell — `12c 64G 1T`: cores, total RAM,
127
- * total root disk via fmtBytes. `—` when the probe never saw the box (specs
128
- * ride the long-TTL static tier, so a warm cache almost always has them).
127
+ * total root disk via fmtBytes. `—` only when no probe has ever seen the box.
128
+ *
129
+ * Deliberately NOT gated on `reachable`: hardware does not change while a
130
+ * machine is down, so an offline device keeps rendering the spec from its last
131
+ * successful probe (`retainHardwareFacts`, RUSH-3096) rather than blanking a
132
+ * fact that is still true. The volatile columns beside it — load, mem, disk
133
+ * used — stay `—`, and `fleetCapacity` still counts only reachable boxes, so
134
+ * a down machine never contributes to usable capacity.
135
+ *
129
136
  * Unpadded; {@link renderDeviceTable} pads to the measured column width. */
130
137
  function specText(stats) {
131
- if (!stats?.reachable || !stats.ncpu)
138
+ if (!stats?.ncpu)
132
139
  return '—';
133
140
  return `${stats.ncpu}c ${fmtBytes(stats.memTotalBytes)} ${fmtBytes(stats.diskTotalBytes)}`;
134
141
  }
@@ -225,7 +232,17 @@ export function renderDeviceTable(reg, names, self, statsMap, full = false, inte
225
232
  // "offline" while its live load/mem sit one column over (RUSH-1965).
226
233
  const offline = deviceOnlineState(d, stats) === 'offline';
227
234
  if (offline) {
228
- lines.push(fitDeviceRow(`${marker}${label}${plat} ${chalk.gray('offline')}`, role, desc, width));
235
+ // The spec cell rides the offline row: hardware is what the box IS, not
236
+ // what it is doing, so it stays legible while the machine is down
237
+ // (RUSH-3096). The live columns are omitted rather than dashed — there is
238
+ // no current load/mem/disk to report for a box that did not answer.
239
+ //
240
+ // The explicit gap is load-bearing: `specCell` pads to the MEASURED
241
+ // column width, so the widest spec on the fleet pads to nothing and the
242
+ // marker would collide with it ("455Goffline"). An online row gets its
243
+ // separation for free from `pctCell`'s right-alignment; this one has no
244
+ // numeric cell to lean on.
245
+ lines.push(fitDeviceRow(`${marker}${label}${plat} ${specCell(stats, specWidth)} ${chalk.gray('offline')}`, role, desc, width));
229
246
  continue;
230
247
  }
231
248
  const relay = !isSelf && d.tailscale?.online && !d.tailscale.direct ? chalk.yellow(' relay') : '';
@@ -1085,7 +1102,7 @@ function registerDevicesCommands(program) {
1085
1102
  registerLeaseCommand(devicesCmd);
1086
1103
  registerSnapshotCommand(devicesCmd);
1087
1104
  registerCommandGroups(devicesCmd, [
1088
- { title: 'Discover & register', names: ['sync', 'register', 'add', 'ignore', 'unignore', 'ignored', 'rm'] },
1105
+ { title: 'Discover & register', names: ['sync', 'register', 'add', 'ignore', 'unignore', 'ignored', 'remove'] },
1089
1106
  { title: 'Inspect', names: ['list', 'show', 'status', 'ping', 'harnesses', 'accounts', 'snapshot'] },
1090
1107
  { title: 'Disposable devices', names: ['lease'] },
1091
1108
  { title: 'Configure a device', names: ['config', 'describe', 'render'] },
@@ -1997,6 +2014,36 @@ function registerDevicesCommands(program) {
1997
2014
  await runFleetPing({ ...opts, verbose });
1998
2015
  });
1999
2016
  const csvList = (s) => s ? s.split(',').map((x) => x.trim()).filter(Boolean) : undefined;
2017
+ devicesCmd
2018
+ .command('pick')
2019
+ .description('Print the device automatic placement would choose for offloaded machine work (the suite, a build) — least-loaded, reachable, POSIX, never the box you are sitting at. Writes just the name to stdout so scripts can consume it.')
2020
+ .option('--json', 'output the pick plus every candidate and exclusion reason')
2021
+ .option('--platform <list>', 'comma-separated platforms to allow (default: linux,macos)')
2022
+ .action(async (opts) => {
2023
+ const { resolveWorkerDevice } = await import('../lib/devices/worker-pick.js');
2024
+ let plan;
2025
+ try {
2026
+ plan = await resolveWorkerDevice({ platforms: csvList(opts.platform) });
2027
+ }
2028
+ catch (err) {
2029
+ // Fail loud with the resolver's own message. Never print a device name
2030
+ // we did not actually pick: a caller reading stdout would ship a tree to it.
2031
+ console.error(err instanceof Error ? err.message : String(err));
2032
+ process.exitCode = 1;
2033
+ return;
2034
+ }
2035
+ if (opts.json) {
2036
+ console.log(JSON.stringify(plan, null, 2));
2037
+ return;
2038
+ }
2039
+ // stdout is the name alone (`box="$(agents devices pick)"`); the human
2040
+ // detail goes to stderr so it never pollutes that capture.
2041
+ const detail = plan.candidates
2042
+ .map((c) => `${c.device}:${c.loadPercent === undefined ? '?' : `${Math.round(c.loadPercent)}%`}`)
2043
+ .join(' ');
2044
+ console.error(chalk.gray(`[agents] worker=${plan.device}${plan.isLocal ? ' (this machine)' : ''} candidates: ${detail}`));
2045
+ console.log(plan.device);
2046
+ });
2000
2047
  const harnessInvOpts = (opts) => ({
2001
2048
  agents: csvList(opts.agents),
2002
2049
  devices: csvList(opts.device ?? opts.devices),
@@ -2156,8 +2203,8 @@ email) into a single row. Use \`agents devices harnesses\` for the per-install v
2156
2203
  }
2157
2204
  });
2158
2205
  devicesCmd
2159
- .command('rm <name>')
2160
- .alias('remove')
2206
+ .command('remove <name>')
2207
+ .alias('rm')
2161
2208
  .description('Remove a device from the registry.')
2162
2209
  .action(async (name) => {
2163
2210
  const ok = await removeDevice(name);
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerTracesCommands(program: Command): void;