@phnx-labs/agents-cli 1.20.71 → 1.20.73

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 (238) hide show
  1. package/CHANGELOG.md +325 -1
  2. package/README.md +40 -4
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/activity.d.ts +12 -0
  5. package/dist/commands/activity.js +68 -0
  6. package/dist/commands/apply.d.ts +13 -0
  7. package/dist/commands/apply.js +28 -3
  8. package/dist/commands/browser.js +1 -1
  9. package/dist/commands/cloud.js +21 -11
  10. package/dist/commands/commands.js +2 -0
  11. package/dist/commands/doctor.js +8 -4
  12. package/dist/commands/events.d.ts +11 -10
  13. package/dist/commands/events.js +30 -21
  14. package/dist/commands/exec.d.ts +34 -0
  15. package/dist/commands/exec.js +337 -39
  16. package/dist/commands/feed.d.ts +13 -0
  17. package/dist/commands/feed.js +130 -29
  18. package/dist/commands/hq.d.ts +2 -0
  19. package/dist/commands/hq.js +58 -0
  20. package/dist/commands/lease.d.ts +19 -0
  21. package/dist/commands/lease.js +138 -11
  22. package/dist/commands/login.d.ts +2 -0
  23. package/dist/commands/login.js +123 -0
  24. package/dist/commands/logs.js +39 -10
  25. package/dist/commands/mcp.js +2 -0
  26. package/dist/commands/mine.d.ts +27 -0
  27. package/dist/commands/mine.js +207 -0
  28. package/dist/commands/monitors.js +79 -49
  29. package/dist/commands/plugins.js +1 -0
  30. package/dist/commands/profiles.js +158 -0
  31. package/dist/commands/repo.js +2 -60
  32. package/dist/commands/resource-view.d.ts +2 -0
  33. package/dist/commands/resource-view.js +8 -0
  34. package/dist/commands/resources.d.ts +5 -0
  35. package/dist/commands/resources.js +95 -0
  36. package/dist/commands/routines.d.ts +2 -0
  37. package/dist/commands/routines.js +139 -37
  38. package/dist/commands/secrets.js +234 -47
  39. package/dist/commands/send.d.ts +15 -0
  40. package/dist/commands/send.js +62 -0
  41. package/dist/commands/sessions-tail.d.ts +1 -0
  42. package/dist/commands/sessions-tail.js +20 -9
  43. package/dist/commands/sessions.d.ts +6 -0
  44. package/dist/commands/sessions.js +26 -3
  45. package/dist/commands/setup-mine.d.ts +18 -0
  46. package/dist/commands/setup-mine.js +106 -0
  47. package/dist/commands/setup-share.js +2 -2
  48. package/dist/commands/setup.js +3 -1
  49. package/dist/commands/share.d.ts +8 -1
  50. package/dist/commands/share.js +122 -37
  51. package/dist/commands/skills.js +2 -0
  52. package/dist/commands/ssh.d.ts +9 -0
  53. package/dist/commands/ssh.js +196 -26
  54. package/dist/commands/status.js +8 -2
  55. package/dist/commands/subagents.js +3 -1
  56. package/dist/commands/sync.js +1 -1
  57. package/dist/commands/teams.js +11 -2
  58. package/dist/commands/uninstall.d.ts +11 -0
  59. package/dist/commands/uninstall.js +158 -0
  60. package/dist/commands/utils.d.ts +29 -0
  61. package/dist/commands/utils.js +24 -0
  62. package/dist/commands/versions.js +120 -11
  63. package/dist/index.js +83 -12
  64. package/dist/lib/activity.d.ts +124 -0
  65. package/dist/lib/activity.js +542 -0
  66. package/dist/lib/agents.js +16 -5
  67. package/dist/lib/artifact-actions.d.ts +1 -1
  68. package/dist/lib/artifact-actions.js +1 -1
  69. package/dist/lib/ask-classifier.d.ts +2 -1
  70. package/dist/lib/ask-classifier.js +3 -3
  71. package/dist/lib/auth-health.d.ts +32 -4
  72. package/dist/lib/auth-health.js +40 -1
  73. package/dist/lib/auto-pull.js +8 -1
  74. package/dist/lib/brand.d.ts +40 -0
  75. package/dist/lib/brand.js +122 -0
  76. package/dist/lib/browser/drivers/ssh.js +4 -7
  77. package/dist/lib/browser/service.js +6 -8
  78. package/dist/lib/channels/providers/index.d.ts +2 -0
  79. package/dist/lib/channels/providers/index.js +20 -0
  80. package/dist/lib/channels/providers/mailbox.d.ts +2 -0
  81. package/dist/lib/channels/providers/mailbox.js +26 -0
  82. package/dist/lib/channels/providers/openclaw-telegram.d.ts +2 -0
  83. package/dist/lib/channels/providers/openclaw-telegram.js +34 -0
  84. package/dist/lib/channels/providers/rush.d.ts +6 -0
  85. package/dist/lib/channels/providers/rush.js +55 -0
  86. package/dist/lib/channels/registry.d.ts +42 -0
  87. package/dist/lib/channels/registry.js +20 -0
  88. package/dist/lib/channels/resolve.d.ts +11 -0
  89. package/dist/lib/channels/resolve.js +12 -0
  90. package/dist/lib/cli-resources.d.ts +18 -0
  91. package/dist/lib/cli-resources.js +53 -4
  92. package/dist/lib/cloud/codex.js +6 -3
  93. package/dist/lib/cloud/factory.d.ts +1 -0
  94. package/dist/lib/cloud/factory.js +10 -4
  95. package/dist/lib/cloud/rush.d.ts +3 -1
  96. package/dist/lib/cloud/rush.js +6 -1
  97. package/dist/lib/cloud/types.d.ts +2 -0
  98. package/dist/lib/codex-home.d.ts +35 -0
  99. package/dist/lib/codex-home.js +136 -0
  100. package/dist/lib/crabbox/cli.d.ts +44 -0
  101. package/dist/lib/crabbox/cli.js +120 -20
  102. package/dist/lib/crabbox/lease.d.ts +52 -1
  103. package/dist/lib/crabbox/lease.js +117 -16
  104. package/dist/lib/crabbox/progress.d.ts +31 -0
  105. package/dist/lib/crabbox/progress.js +76 -0
  106. package/dist/lib/crabbox/runtimes.d.ts +4 -0
  107. package/dist/lib/crabbox/runtimes.js +33 -8
  108. package/dist/lib/crabbox/setup-copy.d.ts +87 -0
  109. package/dist/lib/crabbox/setup-copy.js +127 -0
  110. package/dist/lib/daemon.d.ts +14 -5
  111. package/dist/lib/daemon.js +52 -9
  112. package/dist/lib/drive-sync.js +6 -3
  113. package/dist/lib/event-stream.d.ts +36 -0
  114. package/dist/lib/event-stream.js +68 -0
  115. package/dist/lib/events.d.ts +1 -1
  116. package/dist/lib/exec.d.ts +11 -0
  117. package/dist/lib/exec.js +23 -2
  118. package/dist/lib/feed-policy.d.ts +1 -1
  119. package/dist/lib/feed-policy.js +14 -9
  120. package/dist/lib/feed-ranking.d.ts +32 -0
  121. package/dist/lib/feed-ranking.js +224 -0
  122. package/dist/lib/feed.d.ts +35 -2
  123. package/dist/lib/feed.js +66 -3
  124. package/dist/lib/fleet/auth-sync.d.ts +53 -0
  125. package/dist/lib/fleet/auth-sync.js +92 -0
  126. package/dist/lib/fleet/remote-login.d.ts +187 -0
  127. package/dist/lib/fleet/remote-login.js +556 -0
  128. package/dist/lib/format.d.ts +30 -3
  129. package/dist/lib/format.js +34 -5
  130. package/dist/lib/fs-atomic.d.ts +7 -2
  131. package/dist/lib/fs-atomic.js +8 -12
  132. package/dist/lib/git.d.ts +16 -0
  133. package/dist/lib/git.js +79 -2
  134. package/dist/lib/heal.js +11 -3
  135. package/dist/lib/hosts/logs.d.ts +12 -0
  136. package/dist/lib/hosts/logs.js +18 -0
  137. package/dist/lib/hosts/progress.d.ts +28 -10
  138. package/dist/lib/hosts/progress.js +79 -22
  139. package/dist/lib/hosts/remote-cmd.js +4 -0
  140. package/dist/lib/hq/floor.d.ts +87 -0
  141. package/dist/lib/hq/floor.js +226 -0
  142. package/dist/lib/menubar/install-menubar.js +14 -20
  143. package/dist/lib/notify.d.ts +1 -0
  144. package/dist/lib/notify.js +3 -3
  145. package/dist/lib/open-url.d.ts +2 -0
  146. package/dist/lib/open-url.js +19 -0
  147. package/dist/lib/openclaw-keychain.d.ts +56 -0
  148. package/dist/lib/openclaw-keychain.js +236 -0
  149. package/dist/lib/overdue.js +10 -0
  150. package/dist/lib/permissions.d.ts +44 -1
  151. package/dist/lib/permissions.js +284 -7
  152. package/dist/lib/project-launch.d.ts +6 -12
  153. package/dist/lib/project-launch.js +13 -228
  154. package/dist/lib/project-resources.d.ts +7 -0
  155. package/dist/lib/project-resources.js +291 -0
  156. package/dist/lib/pty-client.d.ts +27 -0
  157. package/dist/lib/pty-client.js +136 -10
  158. package/dist/lib/refresh.js +14 -10
  159. package/dist/lib/resource-profiles.d.ts +26 -0
  160. package/dist/lib/resource-profiles.js +157 -0
  161. package/dist/lib/resources/permissions.js +7 -1
  162. package/dist/lib/resources/types.d.ts +1 -1
  163. package/dist/lib/resources.d.ts +1 -1
  164. package/dist/lib/resources.js +32 -3
  165. package/dist/lib/routines.d.ts +11 -0
  166. package/dist/lib/routines.js +56 -15
  167. package/dist/lib/runner.d.ts +1 -0
  168. package/dist/lib/runner.js +66 -5
  169. package/dist/lib/secrets/bundles.d.ts +16 -3
  170. package/dist/lib/secrets/bundles.js +206 -37
  171. package/dist/lib/secrets/icloud-import.d.ts +2 -2
  172. package/dist/lib/secrets/icloud-import.js +9 -6
  173. package/dist/lib/secrets/mcp.js +1 -1
  174. package/dist/lib/secrets/vault-age-helper.d.ts +1 -0
  175. package/dist/lib/secrets/vault-age-helper.js +34 -0
  176. package/dist/lib/secrets/vault.d.ts +49 -0
  177. package/dist/lib/secrets/vault.js +397 -0
  178. package/dist/lib/self-heal/checks/path.js +3 -1
  179. package/dist/lib/self-heal/checks/shadowing.js +10 -2
  180. package/dist/lib/self-heal/checks/shims.js +19 -11
  181. package/dist/lib/session/cloud.d.ts +2 -2
  182. package/dist/lib/session/cloud.js +2 -2
  183. package/dist/lib/session/db.d.ts +4 -0
  184. package/dist/lib/session/db.js +44 -7
  185. package/dist/lib/session/discover.d.ts +2 -0
  186. package/dist/lib/session/discover.js +114 -3
  187. package/dist/lib/session/stream-render.d.ts +3 -0
  188. package/dist/lib/session/stream-render.js +130 -0
  189. package/dist/lib/session/types.d.ts +6 -0
  190. package/dist/lib/share/analytics.d.ts +13 -0
  191. package/dist/lib/share/analytics.js +45 -0
  192. package/dist/lib/share/config.d.ts +19 -5
  193. package/dist/lib/share/config.js +44 -8
  194. package/dist/lib/share/provision.d.ts +58 -6
  195. package/dist/lib/share/provision.js +97 -22
  196. package/dist/lib/share/publish.d.ts +36 -13
  197. package/dist/lib/share/publish.js +55 -8
  198. package/dist/lib/share/worker-template.js +83 -17
  199. package/dist/lib/shims.d.ts +36 -0
  200. package/dist/lib/shims.js +120 -16
  201. package/dist/lib/ssh-exec.d.ts +14 -0
  202. package/dist/lib/ssh-exec.js +57 -0
  203. package/dist/lib/staleness/detectors/hooks.js +25 -1
  204. package/dist/lib/staleness/detectors/permissions.js +66 -0
  205. package/dist/lib/staleness/detectors/workflows.js +20 -0
  206. package/dist/lib/staleness/writers/hooks.js +58 -4
  207. package/dist/lib/staleness/writers/permissions.js +2 -2
  208. package/dist/lib/staleness/writers/skills.js +1 -1
  209. package/dist/lib/staleness/writers/sources.d.ts +10 -1
  210. package/dist/lib/staleness/writers/sources.js +68 -1
  211. package/dist/lib/star-nudge.d.ts +45 -0
  212. package/dist/lib/star-nudge.js +91 -0
  213. package/dist/lib/startup/command-registry.d.ts +7 -1
  214. package/dist/lib/startup/command-registry.js +19 -3
  215. package/dist/lib/state.d.ts +2 -0
  216. package/dist/lib/state.js +3 -0
  217. package/dist/lib/subagents-registry.d.ts +2 -0
  218. package/dist/lib/subagents-registry.js +3 -0
  219. package/dist/lib/teams/parsers.js +214 -6
  220. package/dist/lib/teams/supervisor.d.ts +7 -0
  221. package/dist/lib/teams/supervisor.js +2 -1
  222. package/dist/lib/template.d.ts +1 -1
  223. package/dist/lib/template.js +1 -1
  224. package/dist/lib/triggers/webhook.js +36 -3
  225. package/dist/lib/types.d.ts +73 -0
  226. package/dist/lib/uninstall.d.ts +84 -0
  227. package/dist/lib/uninstall.js +347 -0
  228. package/dist/lib/usage.d.ts +13 -1
  229. package/dist/lib/usage.js +32 -14
  230. package/dist/lib/version.d.ts +40 -0
  231. package/dist/lib/version.js +94 -16
  232. package/dist/lib/versions.d.ts +27 -0
  233. package/dist/lib/versions.js +248 -79
  234. package/dist/lib/workflows.d.ts +2 -0
  235. package/dist/lib/workflows.js +88 -0
  236. package/package.json +2 -1
  237. package/dist/commands/daemon.d.ts +0 -10
  238. package/dist/commands/daemon.js +0 -121
@@ -24,6 +24,7 @@ export declare function tailFile(filePath: string, onLine: (line: string) => voi
24
24
  */
25
25
  export declare function streamSessionTail(session: SessionMeta, options?: {
26
26
  fromStart?: boolean;
27
+ raw?: boolean;
27
28
  }): Promise<void>;
28
29
  /** Attach the `tail` subcommand to an existing `sessions` command. */
29
30
  export declare function registerSessionsTailCommand(sessionsCmd: Command): void;
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Implements `agents sessions tail` — position-tracked reader on the session
5
5
  * JSONL, driven by an fs.watch on the parent directory. Claude and Codex
6
- * only for v1 (both use append-only JSONL).
6
+ * only for v1 (both use append-only JSONL). Output is compact by default;
7
+ * --json keeps the raw JSONL stream.
7
8
  */
8
9
  import * as fs from 'fs';
9
10
  import * as fsp from 'fs/promises';
@@ -11,6 +12,7 @@ import * as path from 'path';
11
12
  import chalk from 'chalk';
12
13
  import { discoverSessions, resolveSessionById } from '../lib/session/discover.js';
13
14
  import { setHelpSections } from '../lib/help.js';
15
+ import { makeStreamRenderer } from '../lib/session/stream-render.js';
14
16
  const TAIL_SUPPORTED = ['claude', 'codex'];
15
17
  /** Whether a session's transcript can be live-tailed (append-only JSONL agents). */
16
18
  export function isTailable(agent) {
@@ -186,7 +188,7 @@ async function runTail(sessionId, options) {
186
188
  }
187
189
  session = resolved;
188
190
  }
189
- await streamSessionTail(session, { fromStart: options.fromStart });
191
+ await streamSessionTail(session, { fromStart: options.fromStart, raw: options.json });
190
192
  }
191
193
  /**
192
194
  * Live-tail one already-resolved session's transcript to stdout until Ctrl+C.
@@ -194,6 +196,7 @@ async function runTail(sessionId, options) {
194
196
  */
195
197
  export async function streamSessionTail(session, options = {}) {
196
198
  const filePath = session.filePath.split('#')[0];
199
+ const render = options.raw ? undefined : makeStreamRenderer(session.agent, session.cwd);
197
200
  if (process.stderr.isTTY) {
198
201
  process.stderr.write(chalk.gray(`Tailing ${session.agent} ${session.shortId} — ${filePath}\n`) +
199
202
  chalk.gray('Ctrl+C to stop.\n'));
@@ -204,7 +207,13 @@ export async function streamSessionTail(session, options = {}) {
204
207
  process.on('SIGTERM', onSig);
205
208
  try {
206
209
  await tailFile(filePath, (line) => {
207
- process.stdout.write(line + '\n');
210
+ if (!render) {
211
+ process.stdout.write(line + '\n');
212
+ return;
213
+ }
214
+ const out = render(line);
215
+ if (out)
216
+ process.stdout.write(out + '\n');
208
217
  }, ac, { fromStart: options.fromStart });
209
218
  }
210
219
  finally {
@@ -216,9 +225,10 @@ export async function streamSessionTail(session, options = {}) {
216
225
  export function registerSessionsTailCommand(sessionsCmd) {
217
226
  const tailCmd = sessionsCmd
218
227
  .command('tail [sessionId]')
219
- .description('Stream JSONL events from a session file as they are written, one event per line. Long-running: Ctrl+C to stop. Claude and Codex only.')
228
+ .description('Stream compact live lines from a session file as events are written. Long-running: Ctrl+C to stop. Claude and Codex only.')
220
229
  .option('--latest', 'Tail the most recent tailable session (claude or codex)')
221
- .option('--from-start', 'Emit the full file first, then follow (default: start at EOF)');
230
+ .option('--from-start', 'Emit the full file first, then follow (default: start at EOF)')
231
+ .option('--json', 'Emit raw JSONL events instead of compact live lines');
222
232
  setHelpSections(tailCmd, {
223
233
  examples: `
224
234
  # Follow the most recent active Claude or Codex session
@@ -230,15 +240,16 @@ export function registerSessionsTailCommand(sessionsCmd) {
230
240
  # Replay from the beginning, then follow
231
241
  agents sessions tail a1b2c3d4 --from-start
232
242
 
233
- # Pipe through jq to extract just user messages
234
- agents sessions tail --latest | jq 'select(.type == "user")'
243
+ # Pipe raw events through jq to extract just user messages
244
+ agents sessions tail --latest --json | jq 'select(.type == "user")'
235
245
  `,
236
246
  notes: `
237
247
  - Only Claude and Codex sessions are tailable — they append JSONL one event per line.
248
+ - Live tail is compact by default; pass --json for the raw JSONL stream.
238
249
  - Gemini, OpenCode, and OpenClaw use formats that rewrite the file or store state elsewhere.
239
250
  `,
240
251
  });
241
- tailCmd.action(async (sessionId, options) => {
242
- await runTail(sessionId, options);
252
+ tailCmd.action(async (sessionId, _options, command) => {
253
+ await runTail(sessionId, command.optsWithGlobals());
243
254
  });
244
255
  }
@@ -155,6 +155,12 @@ export declare function buildOverviewGroups(pool: SessionMeta[], perProjectCap:
155
155
  * (`agents logs <id> --full`). Reuses the shared `renderSession` renderer.
156
156
  */
157
157
  export declare function renderSessionLog(session: SessionMeta, mode?: ViewMode): Promise<void>;
158
+ /**
159
+ * Emit one session exactly as `agents sessions <id> --json` does — the same
160
+ * redact-by-default `{ session, events }` shape — so `agents logs <id> --json`
161
+ * shares one machine-readable session contract instead of inventing another.
162
+ */
163
+ export declare function renderSessionLogJson(session: SessionMeta): Promise<void>;
158
164
  /** Column-visibility flags for the picker row, computed once over the whole pool. */
159
165
  export interface PickerColumns {
160
166
  /** Render the machine column (only when the pool spans more than one machine). */
@@ -807,6 +807,8 @@ function canonicalSessionsCommand(query, options) {
807
807
  a.push('--active');
808
808
  if (options.teams)
809
809
  a.push('--teams');
810
+ if (options.routine)
811
+ a.push('--routine');
810
812
  if (options.agent)
811
813
  a.push('-a', options.agent);
812
814
  for (const h of options.host ?? [])
@@ -941,6 +943,7 @@ async function sessionsAction(query, options) {
941
943
  // the browser's window so the flag is honored, not swallowed.
942
944
  if (useInteractiveBrowser(options) &&
943
945
  !query &&
946
+ !options.routine &&
944
947
  !options.flat &&
945
948
  !options.tree &&
946
949
  !options.markdown &&
@@ -1038,6 +1041,7 @@ async function sessionsAction(query, options) {
1038
1041
  since,
1039
1042
  until: options.until,
1040
1043
  sortBy,
1044
+ origin: options.routine ? 'routine' : undefined,
1041
1045
  };
1042
1046
  let sessions = await discoverSessions({
1043
1047
  ...scope,
@@ -1164,6 +1168,11 @@ function teamTag(session) {
1164
1168
  const parts = [origin.handle, origin.mode].filter(Boolean).join(' · ');
1165
1169
  return parts ? `[${parts}] ` : '[team] ';
1166
1170
  }
1171
+ function originTag(session) {
1172
+ if (session.origin !== 'routine')
1173
+ return '';
1174
+ return `[routine${session.routineName ? ` · ${session.routineName}` : ''}] `;
1175
+ }
1167
1176
  /** Adapt a SessionMeta's persisted signals to the badge renderer's shape. */
1168
1177
  function metaSignals(s) {
1169
1178
  return {
@@ -1182,7 +1191,7 @@ function flatSessionRow(session, live, showTicket = false, cols = {}) {
1182
1191
  const agentColor = colorAgent(session.agent);
1183
1192
  const when = formatRelativeTime(session.lastActivity ?? session.timestamp);
1184
1193
  const project = session.project || '-';
1185
- const tag = teamTag(session);
1194
+ const tag = originTag(session) || teamTag(session);
1186
1195
  const label = session.label;
1187
1196
  const { glyph, preview } = liveGlyphAndPreview(live);
1188
1197
  // A running session's live preview says what the agent is doing now; a
@@ -1220,7 +1229,7 @@ function flatSessionRow(session, live, showTicket = false, cols = {}) {
1220
1229
  function treeSessionRow(session, live) {
1221
1230
  const agentColor = colorAgent(session.agent);
1222
1231
  const when = formatRelativeTime(session.lastActivity ?? session.timestamp);
1223
- const tag = teamTag(session);
1232
+ const tag = originTag(session) || teamTag(session);
1224
1233
  const label = session.label;
1225
1234
  const { glyph, preview } = liveGlyphAndPreview(live);
1226
1235
  const topic = (preview || (tag ? `${tag}${session.topic ?? ''}` : session.topic)) || '-';
@@ -1422,6 +1431,14 @@ function resolveViewMode(options, filters) {
1422
1431
  export async function renderSessionLog(session, mode = 'summary') {
1423
1432
  await renderSession(session, mode, {});
1424
1433
  }
1434
+ /**
1435
+ * Emit one session exactly as `agents sessions <id> --json` does — the same
1436
+ * redact-by-default `{ session, events }` shape — so `agents logs <id> --json`
1437
+ * shares one machine-readable session contract instead of inventing another.
1438
+ */
1439
+ export async function renderSessionLogJson(session) {
1440
+ await renderSession(session, 'json', {});
1441
+ }
1425
1442
  async function renderSession(session, mode, filters, options = {}) {
1426
1443
  // OpenCode stores sessions in SQLite; filePath is "db_path#session_id"
1427
1444
  const realPath = session.filePath.split('#')[0];
@@ -1584,7 +1601,7 @@ export function formatPickerLabel(s, query, cols = {}) {
1584
1601
  const agentColor = colorAgent(s.agent);
1585
1602
  const when = formatRelativeTime(s.lastActivity ?? s.timestamp);
1586
1603
  const project = s.project || '-';
1587
- const tag = teamTag(s);
1604
+ const tag = originTag(s) || teamTag(s);
1588
1605
  const label = s.label;
1589
1606
  const topic = tag ? `${tag}${s.topic ?? ''}` : s.topic;
1590
1607
  const versionStr = s.version || '-';
@@ -2189,6 +2206,7 @@ export function registerSessionsCommands(program) {
2189
2206
  .option('--opencode', 'Shorthand for --agent opencode')
2190
2207
  .option('--all', 'Include sessions from every directory (not just current project)')
2191
2208
  .option('--teams', 'Include team-spawned sessions (hidden by default)')
2209
+ .option('--routine', 'Show only sessions archived from routine runs')
2192
2210
  .option('-p, --project <name>', 'Filter by project name (searches across all directories)')
2193
2211
  .option('--since <time>', 'Only sessions newer than this (e.g., 2h, 7d, 4w, or ISO date)')
2194
2212
  .option('--until <time>', 'Only sessions older than this (ISO timestamp)')
@@ -2238,6 +2256,10 @@ export function registerSessionsCommands(program) {
2238
2256
  # Search across every directory, not just this project
2239
2257
  agents sessions "topic" --all
2240
2258
 
2259
+ # Show routine-run sessions and open one by routine run id
2260
+ agents sessions --routine --all
2261
+ agents sessions 2026-07-21T10-30-00-000Z
2262
+
2241
2263
  # Export for analysis
2242
2264
  agents sessions --since 30d --limit 200 --json > sessions.json
2243
2265
 
@@ -2254,6 +2276,7 @@ export function registerSessionsCommands(program) {
2254
2276
  - --first and --last are mutually exclusive.
2255
2277
  - A filter flag (--include/--exclude/--first/--last) without --markdown/--json defaults to --markdown output.
2256
2278
  - --cloud sources from Rush Cloud captured runs instead of local disk.
2279
+ - --routine shows only transcripts archived from routine run directories; routine rows also resolve by run id.
2257
2280
  - Without --teams, team-spawned sessions are hidden by default.
2258
2281
  `,
2259
2282
  });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `agents setup mine` — interactive wizard to white-label the CLI under your own
3
+ * name. Pick a name, choose which features to turn off, and get a personally-
4
+ * named binary (e.g. `jack`) that runs every agents verb as yours. Delegates the
5
+ * actual minting to `initBrand` (see mine.ts); the `agents mine` group manages
6
+ * brands afterward.
7
+ *
8
+ * Idempotent: re-running for an existing brand offers to re-mint it.
9
+ */
10
+ import type { Command } from 'commander';
11
+ /**
12
+ * Interactive white-label setup. Returns true when a brand exists afterward,
13
+ * false if the user backed out. Never throws on cancel — the `agents setup` hub
14
+ * relies on that.
15
+ */
16
+ export declare function runMineWizard(): Promise<boolean>;
17
+ /** Register `agents setup mine` under the parent `setup` command. */
18
+ export declare function registerSetupMineCommand(setupCmd: Command): void;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * `agents setup mine` — interactive wizard to white-label the CLI under your own
3
+ * name. Pick a name, choose which features to turn off, and get a personally-
4
+ * named binary (e.g. `jack`) that runs every agents verb as yours. Delegates the
5
+ * actual minting to `initBrand` (see mine.ts); the `agents mine` group manages
6
+ * brands afterward.
7
+ *
8
+ * Idempotent: re-running for an existing brand offers to re-mint it.
9
+ */
10
+ import chalk from 'chalk';
11
+ import { initBrand } from './mine.js';
12
+ import { validateBrandName, getBrandConfig } from '../lib/brand.js';
13
+ import { isShimsInPath } from '../lib/shims.js';
14
+ import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
15
+ /**
16
+ * Optional/heavier top-level commands a brand commonly turns off. Kept short so
17
+ * the checkbox is scannable — anything can still be toggled later with
18
+ * `agents mine toggle <name> --disable <cmd>`.
19
+ */
20
+ const DISABLEABLE_FEATURES = [
21
+ { name: 'teams', hint: 'coordinate multiple agents on shared work' },
22
+ { name: 'cloud', hint: 'dispatch agent tasks to the cloud' },
23
+ { name: 'browser', hint: 'automate a browser' },
24
+ { name: 'computer', hint: 'drive native desktop apps' },
25
+ { name: 'secrets', hint: 'keychain-backed env bundles' },
26
+ { name: 'routines', hint: 'run agents on a cron schedule' },
27
+ { name: 'monitors', hint: 'event-triggered watchers' },
28
+ { name: 'wallet', hint: 'payment cards' },
29
+ ];
30
+ /**
31
+ * Interactive white-label setup. Returns true when a brand exists afterward,
32
+ * false if the user backed out. Never throws on cancel — the `agents setup` hub
33
+ * relies on that.
34
+ */
35
+ export async function runMineWizard() {
36
+ if (!isInteractiveTerminal()) {
37
+ console.log(chalk.dim('Non-interactive shell. Create a brand directly, e.g.:\n') +
38
+ chalk.cyan(' agents mine init jack --disable teams cloud'));
39
+ return false;
40
+ }
41
+ const { input, checkbox, confirm } = await import('@inquirer/prompts');
42
+ console.log(chalk.bold('Make it yours — a personally-named CLI that IS agents-cli.\n'));
43
+ const name = (await input({
44
+ message: 'What should your CLI be called? (e.g. jack)',
45
+ validate: (v) => {
46
+ const err = validateBrandName(v.trim());
47
+ return err ?? true;
48
+ },
49
+ })).trim();
50
+ // Existing brand → offer to re-mint rather than error out.
51
+ let force = false;
52
+ if (getBrandConfig(name)) {
53
+ const again = await confirm({
54
+ message: `Brand "${name}" already exists. Re-mint it?`,
55
+ default: false,
56
+ });
57
+ if (!again) {
58
+ printNextSteps(name, !isShimsInPath());
59
+ return true;
60
+ }
61
+ force = true;
62
+ }
63
+ const disabled = await checkbox({
64
+ message: `Any features to turn OFF for ${name}? (space to toggle, enter to accept)`,
65
+ choices: DISABLEABLE_FEATURES.map((f) => ({
66
+ name: `${f.name} ${chalk.dim(f.hint)}`,
67
+ value: f.name,
68
+ })),
69
+ required: false,
70
+ });
71
+ const { pathWarning } = initBrand(name, { disabledCommands: disabled, force });
72
+ console.log(chalk.green(`\nMinted ${chalk.bold(name)} — your own agents CLI.`));
73
+ if (disabled.length > 0) {
74
+ console.log(chalk.dim(` turned off: ${disabled.join(', ')}`));
75
+ }
76
+ printNextSteps(name, pathWarning);
77
+ return true;
78
+ }
79
+ function printNextSteps(name, pathWarning) {
80
+ console.log(chalk.bold('\nTry it:'));
81
+ console.log(' ' + chalk.cyan(`${name} --help`) + chalk.dim(` # your CLI, your name`));
82
+ console.log(' ' + chalk.cyan(`${name} run claude "hello"`));
83
+ console.log(chalk.dim(` Manage it later: agents mine toggle ${name} --disable <cmd> · agents mine list`));
84
+ if (pathWarning) {
85
+ console.log(chalk.yellow(`\n note: the shims dir isn't on your PATH yet — run 'agents setup' or open a new shell.`));
86
+ }
87
+ }
88
+ /** Register `agents setup mine` under the parent `setup` command. */
89
+ export function registerSetupMineCommand(setupCmd) {
90
+ setupCmd
91
+ .command('mine')
92
+ .description('White-label the CLI — mint your own personally-named binary (e.g. `jack`).')
93
+ .action(async () => {
94
+ try {
95
+ await runMineWizard();
96
+ }
97
+ catch (err) {
98
+ if (isPromptCancelled(err)) {
99
+ console.log(chalk.yellow('\nCancelled'));
100
+ return;
101
+ }
102
+ console.error(chalk.red(err.message));
103
+ process.exitCode = 1;
104
+ }
105
+ });
106
+ }
@@ -18,7 +18,7 @@ import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
18
18
  export async function runShareWizard() {
19
19
  if (!isInteractiveTerminal()) {
20
20
  console.error(chalk.red('agents setup share needs an interactive terminal. ' +
21
- 'Non-interactively, use `agents share setup` (provision) or `agents share join <url>`.'));
21
+ 'Non-interactively, use `agents share setup` (provision) or `agents share join [url]`.'));
22
22
  return false;
23
23
  }
24
24
  const existing = readShareConfig();
@@ -44,7 +44,7 @@ export async function runShareWizard() {
44
44
  {
45
45
  name: 'Join an existing endpoint (a teammate already provisioned one)',
46
46
  value: 'join',
47
- description: 'Paste the base URL + write token from whoever owns the endpoint.',
47
+ description: 'Use synced config when present, or paste the base URL + write token from the endpoint owner.',
48
48
  },
49
49
  ],
50
50
  });
@@ -21,6 +21,7 @@ import { setHelpSections } from '../lib/help.js';
21
21
  import { registerSetupBrowserCommand, runBrowserWizard } from './setup-browser.js';
22
22
  import { registerSetupComputerCommand, runComputerWizard } from './setup-computer.js';
23
23
  import { registerSetupShareCommand, runShareWizard } from './setup-share.js';
24
+ import { registerSetupMineCommand } from './setup-mine.js';
24
25
  const HOME = os.homedir();
25
26
  /**
26
27
  * Import an existing unmanaged agent installation into agents-cli.
@@ -266,10 +267,11 @@ export function registerSetupCommand(program) {
266
267
  .description('First-time setup. Clones a config repo and installs agent CLIs.')
267
268
  .option('-f, --force', 'Re-run setup even if ~/.agents/.system/ already exists (use with caution)')
268
269
  .option('--no-system-repo', 'Skip cloning the system repo (you must populate ~/.agents/.system/ yourself)');
269
- // Capability subcommands: `agents setup browser|computer|share`.
270
+ // Capability subcommands: `agents setup browser|computer|share|mine`.
270
271
  registerSetupBrowserCommand(setupCmd);
271
272
  registerSetupComputerCommand(setupCmd);
272
273
  registerSetupShareCommand(setupCmd);
274
+ registerSetupMineCommand(setupCmd);
273
275
  setHelpSections(setupCmd, {
274
276
  examples: `
275
277
  # First-time setup (clones the system repo into ~/.agents/.system/)
@@ -1,4 +1,7 @@
1
1
  import type { Command } from 'commander';
2
+ import { type CloudflareRequester } from '../lib/share/provision.js';
3
+ import { type PublishResult } from '../lib/share/publish.js';
4
+ export declare function formatSharePublishResult(result: PublishResult, json?: boolean): string;
2
5
  export declare function registerShareCommands(program: Command): void;
3
6
  /** Provision a fresh R2 bucket + Worker on the user's Cloudflare and persist the
4
7
  * endpoint config + write token. Shared by `agents share setup` and the unified
@@ -10,8 +13,12 @@ export declare function runShareProvision(opts: {
10
13
  account?: string;
11
14
  token?: string;
12
15
  domain?: string;
16
+ analyticsToken?: string;
17
+ request?: CloudflareRequester;
13
18
  }): Promise<void>;
14
19
  /** Join an existing share endpoint (no provisioning): prompt for the endpoint
15
20
  * details + write token and persist them. Shared by `agents share join` and the
16
21
  * unified `agents setup share` wizard. */
17
- export declare function runShareJoin(baseUrl: string): Promise<void>;
22
+ export declare function runShareJoin(baseUrl?: string, opts?: {
23
+ token?: string;
24
+ }): Promise<void>;
@@ -2,18 +2,33 @@
2
2
  // Worker, and get a shareable link (~$0). See apps/cli/docs/share.md.
3
3
  import { existsSync } from 'node:fs';
4
4
  import chalk from 'chalk';
5
- import { DEFAULT_BUCKET_NAME, DEFAULT_CF_BUNDLE, DEFAULT_WORKER_NAME, generateWriteToken, readCloudflareCreds, readShareConfig, storeWriteToken, writeShareConfig, } from '../lib/share/config.js';
6
- import { addCustomDomain, createBucket, deployWorker, enableWorkersDev, findZoneId, } from '../lib/share/provision.js';
5
+ import { DEFAULT_BUCKET_NAME, DEFAULT_CF_BUNDLE, DEFAULT_SHARE_DOMAIN, DEFAULT_WORKER_NAME, generateWriteToken, readCloudflareCreds, readShareConfig, readWriteTokenEnv, readWriteTokenFromBundle, storeWriteToken, writeShareConfig, } from '../lib/share/config.js';
6
+ import { addCustomDomain, configureBucketLifecycle, createBucket, deployWorker, enableWorkersDev, findZoneId, setWorkerSecret, } from '../lib/share/provision.js';
7
7
  import { publishFile } from '../lib/share/publish.js';
8
8
  import { renderWorkerScript } from '../lib/share/worker-template.js';
9
+ import { analyticsEnabled } from '../lib/share/analytics.js';
10
+ import { resolveGitHubUsername } from '../lib/git.js';
11
+ export function formatSharePublishResult(result, json = false) {
12
+ if (json)
13
+ return JSON.stringify(result, null, 2);
14
+ const lines = [chalk.green(result.url)];
15
+ if (result.coverUrl)
16
+ lines.push(chalk.dim(` cover ${result.coverUrl}`));
17
+ if (result.expiresAt)
18
+ lines.push(chalk.dim(` expires ${new Date(result.expiresAt).toLocaleString()}`));
19
+ return lines.join('\n');
20
+ }
9
21
  export function registerShareCommands(program) {
10
22
  const shareCmd = program
11
23
  .command('share')
12
24
  .description('Publish an HTML file to your own Cloudflare R2 and get a shareable link (~$0).')
13
25
  .argument('[file]', 'file to publish (HTML or any static asset)')
14
- .option('--slug <slug>', 'custom URL slug (default: <project>-<feature>-<hash>)')
26
+ .option('--slug <slug>', 'custom URL slug under your namespace (default: <project>-<feature>-<hash>)')
27
+ .option('--github-user <user>', 'GitHub username for the share namespace (default: resolved from gh/git config)')
15
28
  .option('--expire <spec>', 'auto-expire, e.g. 30d, 12h, or 2026-08-01')
16
29
  .option('--no-cover', 'skip the OG preview image (HTML pages get one by default)')
30
+ .option('--no-analytics', 'skip injecting the Cloudflare Web Analytics beacon')
31
+ .option('--json', 'emit machine-readable publish result for plan-render hooks and scripts')
17
32
  .action(async (file, opts) => {
18
33
  if (!file) {
19
34
  shareCmd.help();
@@ -27,14 +42,12 @@ export function registerShareCommands(program) {
27
42
  try {
28
43
  const { url, expiresAt, coverUrl } = await publishFile(file, {
29
44
  slug: opts.slug,
45
+ githubUser: opts.githubUser,
30
46
  expire: opts.expire,
31
47
  cover: opts.cover,
48
+ analytics: opts.analytics,
32
49
  });
33
- console.log(chalk.green(url));
34
- if (coverUrl)
35
- console.log(chalk.dim(` cover ${coverUrl}`));
36
- if (expiresAt)
37
- console.log(chalk.dim(` expires ${new Date(expiresAt).toLocaleString()}`));
50
+ console.log(formatSharePublishResult({ url, expiresAt, coverUrl }, Boolean(opts.json)));
38
51
  }
39
52
  catch (e) {
40
53
  console.error(chalk.red(e.message));
@@ -49,7 +62,8 @@ export function registerShareCommands(program) {
49
62
  .option('--bucket <name>', 'R2 bucket name', DEFAULT_BUCKET_NAME)
50
63
  .option('--account <id>', 'Cloudflare account id (else read from the bundle / prompt)')
51
64
  .option('--token <t>', 'Cloudflare API token (else read from the --bundle)')
52
- .option('--domain <host>', 'also map a custom domain (e.g. share.agents-cli.sh) if the token owns the zone')
65
+ .option('--domain <host>', `custom domain to map (default: ${DEFAULT_SHARE_DOMAIN}; workers.dev if zone is not visible)`)
66
+ .option('--analytics-token <token>', 'Cloudflare Web Analytics token to inject into published HTML pages')
53
67
  .action(async (opts) => {
54
68
  try {
55
69
  await runShareProvision(opts);
@@ -61,11 +75,12 @@ export function registerShareCommands(program) {
61
75
  });
62
76
  shareCmd
63
77
  .command('join')
64
- .description('Use an existing share endpoint (no provisioning) — pass the base URL and paste the write token.')
65
- .argument('<baseUrl>', 'base URL of the endpoint, e.g. https://share.agents-cli.sh')
66
- .action(async (baseUrl) => {
78
+ .description('Use an existing synced share endpoint and write token (no provisioning).')
79
+ .argument('[baseUrl]', 'base URL of the endpoint, e.g. https://share.agents-cli.sh')
80
+ .option('--token <token>', 'write token (else SHARE_WRITE_TOKEN env, existing bundle, or prompt)')
81
+ .action(async (baseUrl, opts) => {
67
82
  try {
68
- await runShareJoin(baseUrl);
83
+ await runShareJoin(baseUrl, opts);
69
84
  }
70
85
  catch (e) {
71
86
  console.error(chalk.red(e.message));
@@ -74,8 +89,8 @@ export function registerShareCommands(program) {
74
89
  });
75
90
  shareCmd
76
91
  .command('status')
77
- .description('Show the configured share endpoint.')
78
- .action(() => {
92
+ .description('Show the configured share endpoint and namespace.')
93
+ .action(async () => {
79
94
  const cfg = readShareConfig();
80
95
  if (!cfg) {
81
96
  console.log(chalk.dim("Not configured. Run 'agents share setup' or 'agents share join'."));
@@ -83,6 +98,34 @@ export function registerShareCommands(program) {
83
98
  }
84
99
  console.log(`${chalk.bold('endpoint')} ${chalk.green(cfg.baseUrl)}`);
85
100
  console.log(chalk.dim(`worker ${cfg.workerName} · bucket ${cfg.bucketName} · account ${cfg.accountId}`));
101
+ const user = await resolveGitHubUsername();
102
+ console.log(`${chalk.bold('namespace')} ${user ? chalk.cyan(`${cfg.baseUrl}/${user}`) : chalk.yellow('unknown — set gh auth or github.user')}`);
103
+ console.log(`${chalk.bold('analytics')} ${analyticsEnabled(cfg) ? chalk.green('enabled') : chalk.dim('not configured')}`);
104
+ });
105
+ shareCmd
106
+ .command('analytics')
107
+ .description('Show the Cloudflare Web Analytics status for this share endpoint.')
108
+ .action(async () => {
109
+ const cfg = readShareConfig();
110
+ if (!cfg) {
111
+ console.log(chalk.dim("Not configured. Run 'agents share setup' or 'agents share join'."));
112
+ return;
113
+ }
114
+ if (!analyticsEnabled(cfg)) {
115
+ console.log(chalk.yellow('Cloudflare Web Analytics is not configured.'));
116
+ console.log(chalk.dim("Re-run setup with --analytics-token <token> or add analyticsToken to agents.yaml share:."));
117
+ return;
118
+ }
119
+ const dashboard = cfg.domain
120
+ ? `https://dash.cloudflare.com/${cfg.accountId}/web-analytics/${cfg.domain}`
121
+ : `https://dash.cloudflare.com/${cfg.accountId}/web-analytics`;
122
+ console.log(`${chalk.bold('analytics')} ${chalk.green('enabled')}`);
123
+ console.log(`${chalk.bold('token')} ${chalk.dim(cfg.analyticsToken.slice(0, 8) + '…')}`);
124
+ console.log(`${chalk.bold('dashboard')} ${chalk.cyan(dashboard)}`);
125
+ const user = await resolveGitHubUsername();
126
+ if (user) {
127
+ console.log(chalk.dim(`Per-page breakdown is available under Paths in the dashboard (filter by /${user}/).`));
128
+ }
86
129
  });
87
130
  }
88
131
  /** Provision a fresh R2 bucket + Worker on the user's Cloudflare and persist the
@@ -101,29 +144,35 @@ export async function runShareProvision(opts) {
101
144
  const workerName = opts.worker;
102
145
  const bucketName = opts.bucket;
103
146
  const token = generateWriteToken();
147
+ const requestedDomain = cleanHostname(opts.domain) ?? DEFAULT_SHARE_DOMAIN;
104
148
  const spin = ora('Provisioning on Cloudflare…').start();
105
149
  try {
106
- await createBucket(apiToken, accountId, bucketName);
150
+ const provisionOpts = opts.request ? { request: opts.request } : {};
151
+ await createBucket(apiToken, accountId, bucketName, provisionOpts);
107
152
  spin.text = `R2 bucket '${bucketName}' ready`;
108
- await deployWorker(apiToken, accountId, workerName, renderWorkerScript(), bucketName, token);
153
+ await configureBucketLifecycle(apiToken, accountId, bucketName, provisionOpts);
154
+ spin.text = `R2 bucket '${bucketName}' lifecycle ready`;
155
+ await deployWorker(apiToken, accountId, workerName, renderWorkerScript(), bucketName, provisionOpts);
109
156
  spin.text = `Worker '${workerName}' deployed`;
110
- const subdomain = await enableWorkersDev(apiToken, accountId, workerName);
157
+ await setWorkerSecret(apiToken, accountId, workerName, token, provisionOpts);
158
+ spin.text = `Worker '${workerName}' write token set`;
159
+ const subdomain = await enableWorkersDev(apiToken, accountId, workerName, provisionOpts);
111
160
  let baseUrl = `https://${workerName}.${subdomain}.workers.dev`;
112
161
  let domain;
113
- if (opts.domain) {
114
- spin.text = `Mapping ${opts.domain}…`;
115
- const zoneId = await findZoneId(apiToken, opts.domain);
162
+ if (requestedDomain) {
163
+ spin.text = `Mapping ${requestedDomain}…`;
164
+ const zoneId = await findZoneId(apiToken, requestedDomain, provisionOpts);
116
165
  if (zoneId) {
117
- await addCustomDomain(apiToken, accountId, workerName, zoneId, opts.domain);
118
- baseUrl = `https://${opts.domain}`;
119
- domain = opts.domain;
166
+ await addCustomDomain(apiToken, accountId, workerName, zoneId, requestedDomain, provisionOpts);
167
+ baseUrl = `https://${requestedDomain}`;
168
+ domain = requestedDomain;
120
169
  }
121
170
  else {
122
- spin.warn(`Zone for ${opts.domain} not visible to this token — staying on workers.dev`);
171
+ spin.warn(`Zone for ${requestedDomain} not visible to this token — staying on workers.dev`);
123
172
  }
124
173
  }
125
174
  spin.succeed('Provisioned');
126
- const cfg = { baseUrl, accountId, workerName, bucketName, domain };
175
+ const cfg = { baseUrl, accountId, workerName, bucketName, domain, analyticsToken: opts.analyticsToken };
127
176
  writeShareConfig(cfg);
128
177
  storeWriteToken(token);
129
178
  console.log(chalk.green(`\nShare endpoint ready → ${chalk.bold(baseUrl)}`));
@@ -135,22 +184,58 @@ export async function runShareProvision(opts) {
135
184
  throw e;
136
185
  }
137
186
  }
187
+ function cleanHostname(domain) {
188
+ const raw = domain?.trim().replace(/\/+$/, '');
189
+ if (!raw)
190
+ return undefined;
191
+ try {
192
+ const url = new URL(raw.includes('://') ? raw : `https://${raw}`);
193
+ return url.hostname || undefined;
194
+ }
195
+ catch {
196
+ return raw;
197
+ }
198
+ }
138
199
  /** Join an existing share endpoint (no provisioning): prompt for the endpoint
139
200
  * details + write token and persist them. Shared by `agents share join` and the
140
201
  * unified `agents setup share` wizard. */
141
- export async function runShareJoin(baseUrl) {
202
+ export async function runShareJoin(baseUrl, opts = {}) {
142
203
  const { password, input } = await import('@inquirer/prompts');
143
- const clean = baseUrl.replace(/\/+$/, '');
144
- const workerName = await input({ message: 'Worker name', default: DEFAULT_WORKER_NAME });
145
- const bucketName = await input({ message: 'Bucket name', default: DEFAULT_BUCKET_NAME });
146
- const accountId = await input({ message: 'Cloudflare account id' });
147
- const token = await password({ message: 'Write token (from the endpoint owner)', mask: true });
204
+ const existing = readShareConfig();
205
+ const clean = baseUrl?.replace(/\/+$/, '');
206
+ if (!clean && !existing) {
207
+ throw new Error("No synced share endpoint found. Pull config first with 'agents repo pull', or pass the endpoint URL: agents share join <baseUrl>.");
208
+ }
209
+ let cfg;
210
+ if (existing && (!clean || clean === existing.baseUrl)) {
211
+ cfg = existing;
212
+ }
213
+ else {
214
+ if (!clean)
215
+ throw new Error('Share endpoint URL is required.');
216
+ const workerName = await input({ message: 'Worker name', default: DEFAULT_WORKER_NAME });
217
+ const bucketName = await input({ message: 'Bucket name', default: DEFAULT_BUCKET_NAME });
218
+ const accountId = await input({ message: 'Cloudflare account id' });
219
+ const domain = clean.startsWith('https://') && !clean.includes('.workers.dev')
220
+ ? clean.replace(/^https:\/\//, '')
221
+ : undefined;
222
+ cfg = { baseUrl: clean, accountId, workerName, bucketName, domain };
223
+ }
224
+ let token = opts.token?.trim() || readWriteTokenEnv() || '';
225
+ if (!token) {
226
+ try {
227
+ token = readWriteTokenFromBundle();
228
+ }
229
+ catch {
230
+ token = '';
231
+ }
232
+ }
233
+ if (!token) {
234
+ token = await password({ message: 'Write token (from the endpoint owner)', mask: true });
235
+ }
148
236
  if (!token)
149
237
  throw new Error('A write token is required to join.');
150
- const domain = clean.startsWith('https://') && !clean.includes('.workers.dev')
151
- ? clean.replace(/^https:\/\//, '')
152
- : undefined;
153
- writeShareConfig({ baseUrl: clean, accountId, workerName, bucketName, domain });
238
+ writeShareConfig(cfg);
154
239
  storeWriteToken(token);
155
- console.log(chalk.green(`Joined ${chalk.bold(clean)} — publish with `) + chalk.cyan('agents share <file>'));
240
+ console.log(chalk.green(`Joined ${chalk.bold(cfg.baseUrl)} — publish with `) + chalk.cyan('agents share <file>'));
156
241
  }