@phnx-labs/agents-cli 1.22.47 → 1.22.50

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 (272) hide show
  1. package/CHANGELOG.md +503 -17
  2. package/README.md +76 -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 +201 -39
  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-test-harness.d.ts +42 -0
  14. package/dist/commands/daemon-test-harness.js +90 -0
  15. package/dist/commands/daemon.d.ts +21 -0
  16. package/dist/commands/daemon.js +147 -12
  17. package/dist/commands/device-config-test-harness.d.ts +11 -0
  18. package/dist/commands/device-config-test-harness.js +59 -0
  19. package/dist/commands/exec.js +76 -16
  20. package/dist/commands/focus.d.ts +14 -0
  21. package/dist/commands/focus.js +48 -5
  22. package/dist/commands/go.d.ts +3 -0
  23. package/dist/commands/go.js +16 -3
  24. package/dist/commands/inspect.js +2 -2
  25. package/dist/commands/lease.d.ts +1 -1
  26. package/dist/commands/lease.js +4 -3
  27. package/dist/commands/mailboxes.js +4 -3
  28. package/dist/commands/menubar.js +11 -4
  29. package/dist/commands/projects.d.ts +16 -1
  30. package/dist/commands/projects.js +58 -64
  31. package/dist/commands/resume.js +1 -1
  32. package/dist/commands/route.js +4 -4
  33. package/dist/commands/routines.js +3 -2
  34. package/dist/commands/run-broadcast.d.ts +1 -1
  35. package/dist/commands/run-broadcast.js +1 -1
  36. package/dist/commands/sessions-picker.d.ts +22 -0
  37. package/dist/commands/sessions-picker.js +48 -9
  38. package/dist/commands/sessions-trace.js +1 -1
  39. package/dist/commands/sessions.js +1 -0
  40. package/dist/commands/setup-secrets.js +23 -0
  41. package/dist/commands/share.d.ts +20 -5
  42. package/dist/commands/share.js +158 -53
  43. package/dist/commands/ssh.js +54 -7
  44. package/dist/commands/teams.d.ts +12 -0
  45. package/dist/commands/teams.js +44 -67
  46. package/dist/commands/traces.d.ts +2 -0
  47. package/dist/commands/traces.js +188 -0
  48. package/dist/commands/view.d.ts +1 -1
  49. package/dist/commands/view.js +17 -34
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.js +5 -0
  52. package/dist/lib/account-provider-registry.d.ts +9 -0
  53. package/dist/lib/account-provider-registry.js +20 -0
  54. package/dist/lib/accounting/account-pool-collect.d.ts +34 -0
  55. package/dist/lib/accounting/account-pool-collect.js +71 -0
  56. package/dist/lib/accounting/account-pool.d.ts +46 -0
  57. package/dist/lib/accounting/account-pool.js +33 -0
  58. package/dist/lib/accounting/capacity.d.ts +24 -0
  59. package/dist/lib/accounting/capacity.js +30 -0
  60. package/dist/lib/accounting/rotate.d.ts +11 -18
  61. package/dist/lib/accounting/rotate.js +6 -24
  62. package/dist/lib/accounting/usage.d.ts +14 -3
  63. package/dist/lib/accounting/usage.js +64 -18
  64. package/dist/lib/agent-spec/agents.js +11 -9
  65. package/dist/lib/browser/drivers/ssh.d.ts +16 -2
  66. package/dist/lib/browser/drivers/ssh.js +36 -9
  67. package/dist/lib/browser/ipc.js +8 -3
  68. package/dist/lib/browser/profiles.d.ts +35 -101
  69. package/dist/lib/browser/profiles.js +130 -301
  70. package/dist/lib/browser/registry.d.ts +39 -0
  71. package/dist/lib/browser/registry.js +149 -0
  72. package/dist/lib/browser/remote-control.d.ts +8 -6
  73. package/dist/lib/browser/remote-control.js +8 -6
  74. package/dist/lib/browser/resolve-target.d.ts +68 -0
  75. package/dist/lib/browser/resolve-target.js +250 -0
  76. package/dist/lib/browser/runtime-state.d.ts +40 -16
  77. package/dist/lib/browser/runtime-state.js +92 -25
  78. package/dist/lib/browser/service.d.ts +28 -9
  79. package/dist/lib/browser/service.js +139 -62
  80. package/dist/lib/browser/task-index.d.ts +70 -0
  81. package/dist/lib/browser/task-index.js +180 -0
  82. package/dist/lib/byok-usage.js +2 -2
  83. package/dist/lib/claude-statusline.d.ts +29 -0
  84. package/dist/lib/claude-statusline.js +139 -0
  85. package/dist/lib/cloud/host.d.ts +1 -1
  86. package/dist/lib/cloud/host.js +1 -1
  87. package/dist/lib/cloud/rush.js +1 -1
  88. package/dist/lib/computer/computer-rpc.js +2 -2
  89. package/dist/lib/computer/download.d.ts +15 -15
  90. package/dist/lib/computer/download.js +35 -98
  91. package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
  92. package/dist/lib/computer/ssh-tunnel.js +12 -22
  93. package/dist/lib/config-keys.d.ts +2 -8
  94. package/dist/lib/config-keys.js +2 -13
  95. package/dist/lib/crabbox/cli.d.ts +1 -1
  96. package/dist/lib/crabbox/cli.js +2 -2
  97. package/dist/lib/crabbox/lease.d.ts +9 -0
  98. package/dist/lib/crabbox/lease.js +70 -15
  99. package/dist/lib/crabbox/runtimes.js +1 -1
  100. package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
  101. package/dist/lib/daemon/account-state-daemon-service.js +26 -0
  102. package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
  103. package/dist/lib/daemon/browser-ipc-service.js +46 -0
  104. package/dist/lib/daemon/daemon.d.ts +3 -0
  105. package/dist/lib/daemon/daemon.js +212 -444
  106. package/dist/lib/daemon/device-probe-service.d.ts +26 -0
  107. package/dist/lib/daemon/device-probe-service.js +47 -0
  108. package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
  109. package/dist/lib/daemon/keychain-reap-service.js +32 -0
  110. package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
  111. package/dist/lib/daemon/monitor-engine-service.js +29 -0
  112. package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
  113. package/dist/lib/daemon/secrets-broker-service.js +66 -0
  114. package/dist/lib/daemon/self-heal-service.d.ts +27 -0
  115. package/dist/lib/daemon/self-heal-service.js +48 -0
  116. package/dist/lib/daemon/service.d.ts +102 -0
  117. package/dist/lib/daemon/service.js +98 -0
  118. package/dist/lib/daemon/session-index-service.d.ts +22 -0
  119. package/dist/lib/daemon/session-index-service.js +40 -0
  120. package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
  121. package/dist/lib/daemon/state-dir-check-service.js +63 -0
  122. package/dist/lib/daemon/supervisor.d.ts +87 -0
  123. package/dist/lib/daemon/supervisor.js +298 -0
  124. package/dist/lib/daemon/watchdog-service.d.ts +19 -0
  125. package/dist/lib/daemon/watchdog-service.js +40 -0
  126. package/dist/lib/daemon-health.d.ts +15 -0
  127. package/dist/lib/daemon-health.js +29 -5
  128. package/dist/lib/daemon-services.d.ts +8 -1
  129. package/dist/lib/daemon-services.js +55 -0
  130. package/dist/lib/daemon-ticks.d.ts +13 -2
  131. package/dist/lib/daemon-ticks.js +20 -18
  132. package/dist/lib/device-config.d.ts +0 -2
  133. package/dist/lib/device-config.js +1 -23
  134. package/dist/lib/devices/registry.d.ts +1 -1
  135. package/dist/lib/devices/registry.js +1 -1
  136. package/dist/lib/devices/stats-cache.d.ts +20 -0
  137. package/dist/lib/devices/stats-cache.js +49 -5
  138. package/dist/lib/devices/worker-pick.d.ts +48 -0
  139. package/dist/lib/devices/worker-pick.js +113 -0
  140. package/dist/lib/exec.d.ts +67 -13
  141. package/dist/lib/exec.js +85 -29
  142. package/dist/lib/harness/adapters/cursor.js +8 -11
  143. package/dist/lib/helper-download.d.ts +108 -0
  144. package/dist/lib/helper-download.js +194 -0
  145. package/dist/lib/helper-versions.d.ts +52 -0
  146. package/dist/lib/helper-versions.js +58 -0
  147. package/dist/lib/hooks/cache.js +11 -0
  148. package/dist/lib/hooks/install.js +2 -2
  149. package/dist/lib/hosts/credentials.d.ts +1 -1
  150. package/dist/lib/hosts/credentials.js +1 -1
  151. package/dist/lib/hosts/dispatch.d.ts +7 -1
  152. package/dist/lib/hosts/dispatch.js +24 -5
  153. package/dist/lib/hosts/passthrough.d.ts +8 -15
  154. package/dist/lib/hosts/passthrough.js +19 -16
  155. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  156. package/dist/lib/hosts/providers/devices.js +1 -1
  157. package/dist/lib/hosts/reconnect.d.ts +179 -21
  158. package/dist/lib/hosts/reconnect.js +266 -45
  159. package/dist/lib/installations/migrate.d.ts +4 -5
  160. package/dist/lib/installations/migrate.js +6 -7
  161. package/dist/lib/installations/shims.d.ts +1 -1
  162. package/dist/lib/installations/shims.js +23 -13
  163. package/dist/lib/installations/store.js +6 -3
  164. package/dist/lib/installations/versions.js +73 -48
  165. package/dist/lib/linear-autoclose.d.ts +1 -1
  166. package/dist/lib/linear-autoclose.js +1 -1
  167. package/dist/lib/loop.d.ts +2 -2
  168. package/dist/lib/loop.js +1 -1
  169. package/dist/lib/menubar/download-menubar.d.ts +47 -0
  170. package/dist/lib/menubar/download-menubar.js +60 -0
  171. package/dist/lib/menubar/install-menubar.d.ts +31 -7
  172. package/dist/lib/menubar/install-menubar.js +79 -21
  173. package/dist/lib/menubar/notify-desktop.d.ts +3 -3
  174. package/dist/lib/menubar/notify-desktop.js +6 -6
  175. package/dist/lib/model-tiers.d.ts +1 -1
  176. package/dist/lib/project-probe.d.ts +1 -1
  177. package/dist/lib/project-probe.js +1 -1
  178. package/dist/lib/project-pull.d.ts +1 -1
  179. package/dist/lib/project-pull.js +2 -2
  180. package/dist/lib/projects.d.ts +1 -1
  181. package/dist/lib/projects.js +1 -1
  182. package/dist/lib/sandbox.js +6 -7
  183. package/dist/lib/secrets/download-keychain.d.ts +47 -0
  184. package/dist/lib/secrets/download-keychain.js +70 -0
  185. package/dist/lib/secrets/install-helper.d.ts +20 -1
  186. package/dist/lib/secrets/install-helper.js +44 -3
  187. package/dist/lib/session/active.js +17 -1
  188. package/dist/lib/session/db.d.ts +11 -1
  189. package/dist/lib/session/db.js +138 -6
  190. package/dist/lib/session/discover.d.ts +21 -1
  191. package/dist/lib/session/discover.js +33 -5
  192. package/dist/lib/session/highlights.d.ts +49 -1
  193. package/dist/lib/session/highlights.js +95 -0
  194. package/dist/lib/session/host-link.d.ts +16 -2
  195. package/dist/lib/session/host-link.js +7 -1
  196. package/dist/lib/session/parse.js +33 -1
  197. package/dist/lib/session/remote/remote-list.d.ts +9 -0
  198. package/dist/lib/session/remote/remote-list.js +19 -2
  199. package/dist/lib/session/remote/watch.js +11 -2
  200. package/dist/lib/session/session-cache.d.ts +53 -0
  201. package/dist/lib/session/session-cache.js +88 -0
  202. package/dist/lib/session/state.js +1 -1
  203. package/dist/lib/session/tool-calls.d.ts +2 -0
  204. package/dist/lib/session/tool-calls.js +1 -1
  205. package/dist/lib/session/trajectory-html.d.ts +5 -1
  206. package/dist/lib/session/trajectory-html.js +42 -22
  207. package/dist/lib/session/trajectory-text.js +9 -2
  208. package/dist/lib/session/trajectory.js +53 -4
  209. package/dist/lib/session/types.d.ts +15 -0
  210. package/dist/lib/sha256-asset.d.ts +26 -0
  211. package/dist/lib/sha256-asset.js +41 -0
  212. package/dist/lib/share/backend.d.ts +95 -0
  213. package/dist/lib/share/backend.js +139 -0
  214. package/dist/lib/share/delete.d.ts +6 -2
  215. package/dist/lib/share/delete.js +8 -9
  216. package/dist/lib/share/html.d.ts +22 -0
  217. package/dist/lib/share/html.js +88 -0
  218. package/dist/lib/share/provision.d.ts +18 -4
  219. package/dist/lib/share/provision.js +38 -4
  220. package/dist/lib/share/publish.d.ts +44 -15
  221. package/dist/lib/share/publish.js +58 -26
  222. package/dist/lib/share/worker-template.js +402 -25
  223. package/dist/lib/sqlite.js +16 -3
  224. package/dist/lib/ssh-exec.d.ts +65 -0
  225. package/dist/lib/ssh-exec.js +120 -4
  226. package/dist/lib/startup/command-registry.js +1 -1
  227. package/dist/lib/startup/dev-build.js +3 -3
  228. package/dist/lib/state.d.ts +1 -1
  229. package/dist/lib/state.js +7 -3
  230. package/dist/lib/subagents-registry.js +1 -1
  231. package/dist/lib/teams/agents.d.ts +14 -12
  232. package/dist/lib/teams/agents.js +188 -73
  233. package/dist/lib/teams/api.d.ts +6 -1
  234. package/dist/lib/teams/api.js +2 -0
  235. package/dist/lib/teams/scheduler.d.ts +7 -0
  236. package/dist/lib/teams/scheduler.js +26 -8
  237. package/dist/lib/terminal/index.d.ts +1 -1
  238. package/dist/lib/terminal/types.d.ts +1 -1
  239. package/dist/lib/terminal/types.js +1 -1
  240. package/dist/lib/tmux/orphan-reap.js +1 -1
  241. package/dist/lib/tmux/session.js +31 -0
  242. package/dist/lib/traces/backend.d.ts +29 -0
  243. package/dist/lib/traces/backend.js +46 -0
  244. package/dist/lib/traces/classify.d.ts +52 -0
  245. package/dist/lib/traces/classify.js +72 -0
  246. package/dist/lib/traces/config.d.ts +10 -0
  247. package/dist/lib/traces/config.js +2 -0
  248. package/dist/lib/traces/provision.d.ts +19 -0
  249. package/dist/lib/traces/provision.js +30 -0
  250. package/dist/lib/traces/sync.d.ts +150 -0
  251. package/dist/lib/traces/sync.js +459 -0
  252. package/dist/lib/traces/worker-template.d.ts +4 -0
  253. package/dist/lib/traces/worker-template.js +159 -0
  254. package/dist/lib/types.d.ts +18 -19
  255. package/dist/lib/types.js +14 -0
  256. package/dist/lib/usage-refresh.d.ts +1 -1
  257. package/dist/lib/usage-refresh.js +11 -8
  258. package/dist/lib/workflows.d.ts +1 -1
  259. package/package.json +4 -6
  260. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  261. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
  262. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  263. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  264. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
  265. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  266. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -26
  267. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  268. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
  269. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -135
  270. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  271. package/dist/lib/usage-fleet.d.ts +0 -32
  272. package/dist/lib/usage-fleet.js +0 -125
@@ -18,10 +18,20 @@ export interface PublishOptions {
18
18
  */
19
19
  expire?: string;
20
20
  contentType?: string;
21
+ /**
22
+ * Server-enforced visibility (RUSH-3135). `public` (default) is listed in
23
+ * the gallery; `unlisted` is a capability URL (GET still 200, X-Robots-Tag:
24
+ * noindex, hidden from gallery/list). `me` requires a Phoenix session and is
25
+ * visible only to the signed-in owner; `org` requires the same and is visible
26
+ * to members of the same Phoenix organization.
27
+ */
28
+ visibility?: ShareVisibility;
21
29
  /**
22
30
  * Hide this page from the public `/<user>` gallery and `agents artifacts share list`
23
31
  * (metadata `visibility=unlisted`). The direct URL is still world-readable —
24
- * unlisted, not secret (RUSH-2443). Alias of `--private` on the CLI.
32
+ * unlisted, not secret (RUSH-2443). Alias of `--visibility unlisted` /
33
+ * `--private` on the CLI. Kept so `sessions share` and existing callers do
34
+ * not break.
25
35
  */
26
36
  unlisted?: boolean;
27
37
  /**
@@ -39,8 +49,12 @@ export interface PublishOptions {
39
49
  githubUser?: string;
40
50
  /** DI seam for tests — override the persisted share endpoint config. */
41
51
  config?: ShareConfig;
42
- /** DI seam for tests — override the keychain-backed write token. */
52
+ /** DI seam for tests — override the keychain-backed write token. Selects BYO. */
43
53
  writeToken?: string;
54
+ /** DI seam for tests — override `readSession()`. `null` means signed out. */
55
+ session?: import('../identity/client.js').PhoenixSession | null;
56
+ /** Force the BYO Cloudflare path even when signed in. */
57
+ byo?: boolean;
44
58
  /** DI seam for tests — override the real HTTP PUT. */
45
59
  uploader?: PutFn;
46
60
  /** DI seam for tests — override cover capture (returns a PNG buffer or null). */
@@ -55,22 +69,26 @@ export interface PublishOptions {
55
69
  /**
56
70
  * Structured metadata (`--meta key=value`, repeatable). Keys are validated by
57
71
  * {@link parseMetaEntries} — lowercase `[a-z0-9-]`, and may not collide with
58
- * {@link RESERVED_META_KEYS}, which the CLI sets automatically.
72
+ * {@link RESERVED_META_KEYS} (Worker-stamped owner/visibility/expires-at,
73
+ * plus provenance the CLI sets automatically).
59
74
  */
60
75
  meta?: Record<string, string>;
61
76
  /**
62
77
  * Skip revision retention on this publish — overwrite an existing slug's
63
78
  * object in place with no `<slug>/rev-<ts>` backup of the version it replaces
64
- * (default: keep it; see docs/share.md §Revisions).
79
+ * (default: keep it; see docs/distribution.md).
65
80
  */
66
81
  noRevision?: boolean;
67
82
  /** DI seam for tests — override provenance auto-capture (agent/session/host/repo/date). */
68
83
  provenance?: ShareProvenance;
69
84
  }
85
+ export type ShareVisibility = 'public' | 'unlisted' | 'me' | 'org';
70
86
  export interface PublishResult {
71
87
  url: string;
72
88
  expiresAt?: string;
73
89
  coverUrl?: string;
90
+ /** Server-enforced visibility stamped on the object. */
91
+ visibility?: ShareVisibility;
74
92
  /** True when the page was published with `visibility=unlisted`. */
75
93
  unlisted?: boolean;
76
94
  /** The label stored with this share — explicit (`--label`) or derived. */
@@ -78,6 +96,14 @@ export interface PublishResult {
78
96
  /** Whether `label` came from `--label` or was auto-derived. */
79
97
  labelSource?: 'explicit' | 'derived';
80
98
  }
99
+ /**
100
+ * `--unlisted` / `{ unlisted: true }` map to `unlisted`; `--visibility me|org`
101
+ * passes through; otherwise `visibility` (default public).
102
+ */
103
+ export declare function resolveShareVisibility(opts?: {
104
+ visibility?: ShareVisibility;
105
+ unlisted?: boolean;
106
+ }): ShareVisibility;
81
107
  export interface ShareProvenance {
82
108
  /** Harness/agent name (`AGENTS_AGENT_NAME`), when publishing from an agent run. */
83
109
  agent?: string;
@@ -91,11 +117,12 @@ export interface ShareProvenance {
91
117
  date?: string;
92
118
  }
93
119
  /**
94
- * Reserved `customMetadata` keys the CLI sets automatically from the exec env,
95
- * git, and the local clock (plus `label`/`label-source`) — a `--meta key=value`
96
- * may not target any of these (see {@link parseMetaEntries}).
120
+ * Reserved `customMetadata` keys a `--meta key=value` may not target (see
121
+ * {@link parseMetaEntries}). Matches the Worker's `RESERVED_METADATA_KEYS`:
122
+ * provenance the CLI sets automatically, plus `expires-at` / `visibility` /
123
+ * `owner` which the Worker stamps itself.
97
124
  */
98
- export declare const RESERVED_META_KEYS: readonly ["agent", "session", "host", "repo", "date", "label", "label-source"];
125
+ export declare const RESERVED_META_KEYS: readonly ["expires-at", "visibility", "owner", "agent", "session", "host", "repo", "date", "label", "label-source"];
99
126
  /**
100
127
  * Auto-capture publish provenance from the exec env, git, and the local clock.
101
128
  * Every field is present only when the environment genuinely carries it — a
@@ -113,8 +140,9 @@ export declare function resolveShareProvenance(opts?: {
113
140
  /**
114
141
  * Parse repeated `--meta key=value` CLI args into a validated metadata record.
115
142
  * Keys are lowercase `[a-z0-9-]`, up to 64 characters, and may not collide with
116
- * {@link RESERVED_META_KEYS} (the provenance the CLI sets automatically, plus
117
- * label/label-source) — throws naming the offending pair on any violation.
143
+ * {@link RESERVED_META_KEYS} (Worker-stamped owner/visibility/expires-at, plus
144
+ * the provenance the CLI sets automatically, plus label/label-source) — throws
145
+ * naming the offending pair on any violation.
118
146
  */
119
147
  export declare function parseMetaEntries(pairs: string[]): Record<string, string>;
120
148
  /** Throws when the combined `customMetadata` payload would exceed {@link MAX_METADATA_BYTES}. */
@@ -201,16 +229,17 @@ export declare function slugify(name: string): string;
201
229
  /** The project the file belongs to — git repo name, else the cwd's basename. */
202
230
  export declare function detectProject(dir?: string): string;
203
231
  /**
204
- * Notion-style default slug: `<project>-<feature>-<16hex>`. Project scopes the link
205
- * to the repo the agent is in; the random tail keeps it unguessable + collision-free.
206
- * A leading `plan-` on the filename is dropped (it's redundant under the project).
232
+ * Readable slug + view id: `<feature>-<16hex>`. A leading `plan-` on the
233
+ * filename is dropped. The project no longer prefixes the slug — the URL
234
+ * namespace is already the publisher's handle (`/<you>/<slug>`).
207
235
  *
208
236
  * The tail is 8 random bytes (64-bit, 16 hex chars). Reads are public — the URL is
209
237
  * the only capability — so the nonce must be genuinely infeasible to brute-force,
210
- * not merely unlisted; 64 bits puts a blind guess out of reach. (See docs/share.md
238
+ * not merely unlisted; 64 bits puts a blind guess out of reach. (See docs/distribution.md
211
239
  * §Security for the threat model and `--expire` for sensitive content.)
240
+ * `--slug` still publishes that exact slug, no view-id appended.
212
241
  */
213
- export declare function defaultSlug(filePath: string, dir?: string): string;
242
+ export declare function defaultSlug(filePath: string, _dir?: string): string;
214
243
  /**
215
244
  * Best-effort OG cover: capture a screenshot, upload it as `<slug>.png`, and return
216
245
  * the page body with og:image meta injected (+ the cover URL). All IO is injected
@@ -10,17 +10,42 @@ import { basename } from 'node:path';
10
10
  import { execFileSync } from 'node:child_process';
11
11
  import { randomBytes } from 'node:crypto';
12
12
  import { hostname as osHostname } from 'node:os';
13
- import { readShareConfig, readWriteToken } from './config.js';
13
+ import { readShareConfig } from './config.js';
14
14
  import { resolveGitHubUsername } from '../git.js';
15
+ import { resolveShareBackend, sanitizeShareNamespace } from './backend.js';
15
16
  import { captureCover, OG_WIDTH, OG_HEIGHT, OG_SCALE } from './capture.js';
16
17
  import { deriveMeta, injectOgMeta } from './og.js';
17
18
  import { injectAnalyticsBeacon } from './analytics.js';
19
+ import { prepareShareHtml } from './html.js';
18
20
  /**
19
- * Reserved `customMetadata` keys the CLI sets automatically from the exec env,
20
- * git, and the local clock (plus `label`/`label-source`) — a `--meta key=value`
21
- * may not target any of these (see {@link parseMetaEntries}).
21
+ * `--unlisted` / `{ unlisted: true }` map to `unlisted`; `--visibility me|org`
22
+ * passes through; otherwise `visibility` (default public).
22
23
  */
23
- export const RESERVED_META_KEYS = ['agent', 'session', 'host', 'repo', 'date', 'label', 'label-source'];
24
+ export function resolveShareVisibility(opts = {}) {
25
+ if (opts.unlisted === true)
26
+ return 'unlisted';
27
+ if (opts.visibility === 'unlisted' || opts.visibility === 'me' || opts.visibility === 'org')
28
+ return opts.visibility;
29
+ return 'public';
30
+ }
31
+ /**
32
+ * Reserved `customMetadata` keys a `--meta key=value` may not target (see
33
+ * {@link parseMetaEntries}). Matches the Worker's `RESERVED_METADATA_KEYS`:
34
+ * provenance the CLI sets automatically, plus `expires-at` / `visibility` /
35
+ * `owner` which the Worker stamps itself.
36
+ */
37
+ export const RESERVED_META_KEYS = [
38
+ 'expires-at',
39
+ 'visibility',
40
+ 'owner',
41
+ 'agent',
42
+ 'session',
43
+ 'host',
44
+ 'repo',
45
+ 'date',
46
+ 'label',
47
+ 'label-source',
48
+ ];
24
49
  const META_KEY_RE = /^[a-z0-9-]{1,64}$/;
25
50
  /**
26
51
  * Auto-capture publish provenance from the exec env, git, and the local clock.
@@ -43,8 +68,9 @@ export function resolveShareProvenance(opts = {}) {
43
68
  /**
44
69
  * Parse repeated `--meta key=value` CLI args into a validated metadata record.
45
70
  * Keys are lowercase `[a-z0-9-]`, up to 64 characters, and may not collide with
46
- * {@link RESERVED_META_KEYS} (the provenance the CLI sets automatically, plus
47
- * label/label-source) — throws naming the offending pair on any violation.
71
+ * {@link RESERVED_META_KEYS} (Worker-stamped owner/visibility/expires-at, plus
72
+ * the provenance the CLI sets automatically, plus label/label-source) — throws
73
+ * naming the offending pair on any violation.
48
74
  */
49
75
  export function parseMetaEntries(pairs) {
50
76
  const meta = {};
@@ -59,7 +85,7 @@ export function parseMetaEntries(pairs) {
59
85
  throw new Error(`Bad --meta key '${key}'. Keys are lowercase letters, digits, and hyphens, up to 64 characters.`);
60
86
  }
61
87
  if (RESERVED_META_KEYS.includes(key)) {
62
- throw new Error(`--meta ${key}=… is reserved (set automatically from your session/git) — pass a different key.`);
88
+ throw new Error(`--meta ${key}=… is reserved (Worker-stamped, or set automatically from your session/git) — pass a different key.`);
63
89
  }
64
90
  meta[key] = value;
65
91
  }
@@ -293,7 +319,7 @@ export function slugify(name) {
293
319
  .slice(0, 60) || 'page');
294
320
  }
295
321
  function sanitizeSlugPart(s) {
296
- return s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
322
+ return sanitizeShareNamespace(s);
297
323
  }
298
324
  /** The current repo's name, or undefined outside a git checkout — never a
299
325
  * fallback guess, since callers that want one (detectProject) supply it themselves. */
@@ -317,18 +343,19 @@ export function detectProject(dir = process.cwd()) {
317
343
  return gitRepoName(dir) ?? (sanitizeSlugPart(basename(dir)) || 'share');
318
344
  }
319
345
  /**
320
- * Notion-style default slug: `<project>-<feature>-<16hex>`. Project scopes the link
321
- * to the repo the agent is in; the random tail keeps it unguessable + collision-free.
322
- * A leading `plan-` on the filename is dropped (it's redundant under the project).
346
+ * Readable slug + view id: `<feature>-<16hex>`. A leading `plan-` on the
347
+ * filename is dropped. The project no longer prefixes the slug — the URL
348
+ * namespace is already the publisher's handle (`/<you>/<slug>`).
323
349
  *
324
350
  * The tail is 8 random bytes (64-bit, 16 hex chars). Reads are public — the URL is
325
351
  * the only capability — so the nonce must be genuinely infeasible to brute-force,
326
- * not merely unlisted; 64 bits puts a blind guess out of reach. (See docs/share.md
352
+ * not merely unlisted; 64 bits puts a blind guess out of reach. (See docs/distribution.md
327
353
  * §Security for the threat model and `--expire` for sensitive content.)
354
+ * `--slug` still publishes that exact slug, no view-id appended.
328
355
  */
329
- export function defaultSlug(filePath, dir) {
356
+ export function defaultSlug(filePath, _dir) {
330
357
  const feature = slugify(filePath).replace(/^plan-/, '') || 'page';
331
- return `${detectProject(dir)}-${feature}-${randomBytes(8).toString('hex')}`;
358
+ return `${feature}-${randomBytes(8).toString('hex')}`;
332
359
  }
333
360
  function guessContentType(filePath) {
334
361
  if (/\.html?$/i.test(filePath))
@@ -419,14 +446,12 @@ export function buildShareKey(username, slugPart) {
419
446
  return `${user}/${part}`;
420
447
  }
421
448
  export async function publishFile(filePath, opts = {}) {
422
- const cfg = opts.config ?? readShareConfig();
423
- if (!cfg) {
424
- throw new Error("Not set up yet. Run 'agents artifacts setup' (provision your own endpoint) or 'agents artifacts share join' (use an existing one).");
425
- }
426
- const token = opts.writeToken ?? readWriteToken();
427
- const username = await resolveShareUsername(opts);
428
- const analyticsToken = opts.analyticsToken ?? cfg.analyticsToken;
429
- return publishToEndpoint(filePath, { baseUrl: cfg.baseUrl, token }, {
449
+ const backend = resolveShareBackend(opts);
450
+ const username = backend.kind === 'managed'
451
+ ? backend.namespace
452
+ : await resolveShareUsername({ githubUser: opts.githubUser || backend.namespace || undefined });
453
+ const analyticsToken = opts.analyticsToken ?? (backend.kind === 'byo' ? (opts.config ?? readShareConfig())?.analyticsToken : undefined);
454
+ return publishToEndpoint(filePath, { baseUrl: backend.baseUrl, token: backend.token }, {
430
455
  ...opts,
431
456
  githubUser: username,
432
457
  analyticsToken,
@@ -437,7 +462,8 @@ export async function publishToEndpoint(filePath, endpoint, opts = {}) {
437
462
  const slugPart = (opts.slug ?? defaultSlug(filePath)).replace(/^\/+/, '');
438
463
  const key = buildShareKey(username, slugPart);
439
464
  const expiresAt = resolveExpire(opts.expire);
440
- const unlisted = opts.unlisted === true;
465
+ const visibility = resolveShareVisibility(opts);
466
+ const unlisted = visibility === 'unlisted';
441
467
  const pageUrl = `${endpoint.baseUrl.replace(/\/+$/, '')}/${key}`;
442
468
  const provenance = opts.provenance ?? resolveShareProvenance();
443
469
  const meta = opts.meta ?? {};
@@ -449,6 +475,9 @@ export async function publishToEndpoint(filePath, endpoint, opts = {}) {
449
475
  let body = readFileSync(filePath);
450
476
  let coverUrl;
451
477
  const isHtml = /\.html?$/i.test(filePath);
478
+ if (isHtml) {
479
+ body = Buffer.from(prepareShareHtml(body.toString('utf8'), filePath), 'utf8');
480
+ }
452
481
  const explicitLabel = opts.label?.trim();
453
482
  // sanitizeLabel here (not just inside deriveLabel) covers an explicit
454
483
  // --label/--title the caller typed with an embedded newline — deriveLabel
@@ -492,8 +521,7 @@ export async function publishToEndpoint(filePath, endpoint, opts = {}) {
492
521
  const h = { authorization: `Bearer ${endpoint.token}`, 'content-type': contentType };
493
522
  if (expiresAt)
494
523
  h['x-share-expires-at'] = expiresAt;
495
- if (unlisted)
496
- h['x-share-visibility'] = 'unlisted';
524
+ h['x-share-visibility'] = visibility;
497
525
  // Every free-text header goes through toHeaderValue: a non-latin1 code point
498
526
  // anywhere in a label, a repo name, or a --meta value throws inside fetch and
499
527
  // crashes the publish outright.
@@ -541,6 +569,9 @@ export async function publishToEndpoint(filePath, endpoint, opts = {}) {
541
569
  }
542
570
  const r = await put(pageUrl, body, authHeaders(opts.contentType ?? guessContentType(filePath)));
543
571
  if (!r.ok) {
572
+ if (r.status === 409) {
573
+ throw new Error(`Handle '${username}' is already claimed by another account. The public URL namespace is the email local-part; two Phoenix users cannot share it.`);
574
+ }
544
575
  throw new Error(`Publish failed (${r.status}) for ${pageUrl}. Check the write token, or that 'agents artifacts setup' completed.`);
545
576
  }
546
577
  return {
@@ -549,6 +580,7 @@ export async function publishToEndpoint(filePath, endpoint, opts = {}) {
549
580
  coverUrl,
550
581
  label,
551
582
  labelSource,
583
+ visibility,
552
584
  ...(unlisted ? { unlisted: true } : {}),
553
585
  };
554
586
  }