@phnx-labs/agents-cli 1.22.52 → 1.22.54

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 (180) hide show
  1. package/CHANGELOG.md +336 -0
  2. package/README.md +42 -9
  3. package/dist/bootstrap.js +55 -154
  4. package/dist/cli/command-registry.d.ts +5 -0
  5. package/dist/cli/command-registry.js +8 -1
  6. package/dist/commands/accounts.js +220 -174
  7. package/dist/commands/apply.js +6 -3
  8. package/dist/commands/auth-mint.d.ts +8 -0
  9. package/dist/commands/auth-mint.js +96 -0
  10. package/dist/commands/auth.js +5 -1
  11. package/dist/commands/browser.js +1 -1
  12. package/dist/commands/cost.js +8 -2
  13. package/dist/commands/daemon.js +2 -2
  14. package/dist/commands/doctor.js +6 -1
  15. package/dist/commands/exec.js +26 -17
  16. package/dist/commands/fleet-capture.js +7 -0
  17. package/dist/commands/focus.d.ts +1 -0
  18. package/dist/commands/focus.js +4 -2
  19. package/dist/commands/go.d.ts +5 -4
  20. package/dist/commands/go.js +8 -7
  21. package/dist/commands/insights.js +9 -0
  22. package/dist/commands/monitors.js +85 -30
  23. package/dist/commands/output.js +8 -2
  24. package/dist/commands/repo.js +18 -0
  25. package/dist/commands/secrets.js +33 -14
  26. package/dist/commands/sessions-inject.js +8 -3
  27. package/dist/commands/sessions-picker.js +2 -1
  28. package/dist/commands/sessions.d.ts +20 -12
  29. package/dist/commands/sessions.js +94 -40
  30. package/dist/commands/setup-accounts.d.ts +8 -0
  31. package/dist/commands/setup-accounts.js +47 -0
  32. package/dist/commands/setup.d.ts +1 -1
  33. package/dist/commands/setup.js +11 -2
  34. package/dist/commands/share.d.ts +52 -3
  35. package/dist/commands/share.js +262 -18
  36. package/dist/commands/ssh.d.ts +7 -0
  37. package/dist/commands/ssh.js +53 -14
  38. package/dist/commands/status.js +14 -0
  39. package/dist/commands/sync.js +44 -0
  40. package/dist/commands/view.d.ts +3 -1
  41. package/dist/commands/view.js +5 -4
  42. package/dist/lib/account-registry.d.ts +15 -5
  43. package/dist/lib/account-registry.js +165 -53
  44. package/dist/lib/accounting/rotate.d.ts +20 -6
  45. package/dist/lib/accounting/rotate.js +38 -7
  46. package/dist/lib/accounting/usage.d.ts +37 -1
  47. package/dist/lib/accounting/usage.js +71 -6
  48. package/dist/lib/agent-spec/agents.d.ts +5 -2
  49. package/dist/lib/agent-spec/agents.js +25 -7
  50. package/dist/lib/analytics/mix-commands.js +12 -6
  51. package/dist/lib/answer-router.js +2 -1
  52. package/dist/lib/auth-mint.d.ts +150 -0
  53. package/dist/lib/auth-mint.js +434 -0
  54. package/dist/lib/browser/profiles.d.ts +18 -0
  55. package/dist/lib/browser/profiles.js +26 -1
  56. package/dist/lib/browser/registry.d.ts +44 -14
  57. package/dist/lib/browser/registry.js +141 -45
  58. package/dist/lib/browser/remote-control.d.ts +9 -7
  59. package/dist/lib/browser/remote-control.js +9 -7
  60. package/dist/lib/claude-account-token.d.ts +10 -0
  61. package/dist/lib/claude-account-token.js +14 -4
  62. package/dist/lib/config-drift.d.ts +37 -0
  63. package/dist/lib/config-drift.js +72 -0
  64. package/dist/lib/daemon/auth-sync-service.d.ts +19 -0
  65. package/dist/lib/daemon/auth-sync-service.js +34 -0
  66. package/dist/lib/daemon/daemon.js +30 -4
  67. package/dist/lib/daemon/runner.js +10 -2
  68. package/dist/lib/daemon-services.d.ts +1 -1
  69. package/dist/lib/daemon-services.js +5 -0
  70. package/dist/lib/device-config.d.ts +3 -3
  71. package/dist/lib/device-config.js +8 -7
  72. package/dist/lib/devices/config-migration.js +147 -1
  73. package/dist/lib/devices/connect.d.ts +26 -0
  74. package/dist/lib/devices/connect.js +48 -1
  75. package/dist/lib/devices/device-docs.d.ts +35 -0
  76. package/dist/lib/devices/device-docs.js +163 -0
  77. package/dist/lib/devices/discovery-policy.d.ts +14 -2
  78. package/dist/lib/devices/discovery-policy.js +31 -21
  79. package/dist/lib/devices/doctor-findings.d.ts +5 -1
  80. package/dist/lib/devices/doctor-findings.js +19 -1
  81. package/dist/lib/devices/registry.d.ts +11 -5
  82. package/dist/lib/devices/registry.js +46 -18
  83. package/dist/lib/exec.d.ts +88 -30
  84. package/dist/lib/exec.js +138 -34
  85. package/dist/lib/feed/feed.d.ts +10 -2
  86. package/dist/lib/feed/feed.js +35 -2
  87. package/dist/lib/feed-broadcast.js +1 -1
  88. package/dist/lib/fleet/apply.d.ts +11 -0
  89. package/dist/lib/fleet/apply.js +23 -3
  90. package/dist/lib/fleet/auth-sync.js +5 -3
  91. package/dist/lib/help.d.ts +9 -0
  92. package/dist/lib/help.js +29 -1
  93. package/dist/lib/hosts/dispatch.d.ts +4 -3
  94. package/dist/lib/hosts/dispatch.js +12 -8
  95. package/dist/lib/hosts/passthrough.d.ts +1 -10
  96. package/dist/lib/hosts/passthrough.js +1 -13
  97. package/dist/lib/hosts/providers/local.d.ts +9 -3
  98. package/dist/lib/hosts/providers/local.js +23 -12
  99. package/dist/lib/hosts/reconnect.d.ts +7 -4
  100. package/dist/lib/hosts/reconnect.js +29 -25
  101. package/dist/lib/hosts/registry.js +4 -1
  102. package/dist/lib/hosts/remote-os.js +3 -1
  103. package/dist/lib/installations/versions.js +9 -1
  104. package/dist/lib/linux-userns.d.ts +58 -0
  105. package/dist/lib/linux-userns.js +116 -0
  106. package/dist/lib/memory.d.ts +26 -0
  107. package/dist/lib/memory.js +80 -1
  108. package/dist/lib/monitors/config.d.ts +11 -0
  109. package/dist/lib/monitors/config.js +8 -0
  110. package/dist/lib/monitors/engine.js +8 -1
  111. package/dist/lib/monitors/state.d.ts +37 -1
  112. package/dist/lib/monitors/state.js +79 -4
  113. package/dist/lib/permissions-registry.d.ts +2 -0
  114. package/dist/lib/permissions-registry.js +116 -14
  115. package/dist/lib/permissions.d.ts +5 -3
  116. package/dist/lib/permissions.js +25 -27
  117. package/dist/lib/profiles.d.ts +8 -7
  118. package/dist/lib/profiles.js +12 -0
  119. package/dist/lib/project-key.d.ts +9 -0
  120. package/dist/lib/project-key.js +11 -0
  121. package/dist/lib/secrets/bundles.d.ts +35 -0
  122. package/dist/lib/secrets/bundles.js +78 -1
  123. package/dist/lib/secrets/push.d.ts +3 -8
  124. package/dist/lib/secrets/push.js +18 -14
  125. package/dist/lib/secrets/remote.d.ts +9 -18
  126. package/dist/lib/secrets/remote.js +11 -26
  127. package/dist/lib/secrets/reserved-sync.d.ts +65 -0
  128. package/dist/lib/secrets/reserved-sync.js +129 -0
  129. package/dist/lib/self-heal/checks/hook-manifest.d.ts +2 -0
  130. package/dist/lib/self-heal/checks/hook-manifest.js +56 -0
  131. package/dist/lib/self-heal/registry.js +4 -0
  132. package/dist/lib/self-heal/types.d.ts +1 -1
  133. package/dist/lib/session/active.d.ts +10 -1
  134. package/dist/lib/session/active.js +8 -5
  135. package/dist/lib/session/actor-sidecar.d.ts +7 -0
  136. package/dist/lib/session/actor-sidecar.js +2 -0
  137. package/dist/lib/session/db.d.ts +39 -4
  138. package/dist/lib/session/db.js +168 -31
  139. package/dist/lib/session/discover.d.ts +32 -4
  140. package/dist/lib/session/discover.js +126 -37
  141. package/dist/lib/session/insights.d.ts +14 -0
  142. package/dist/lib/session/insights.js +25 -2
  143. package/dist/lib/session/linear.js +1 -1
  144. package/dist/lib/session/live-metadata.js +1 -0
  145. package/dist/lib/session/pid-registry.d.ts +7 -0
  146. package/dist/lib/session/prompt.d.ts +15 -0
  147. package/dist/lib/session/prompt.js +21 -0
  148. package/dist/lib/session/shell-programs.d.ts +17 -0
  149. package/dist/lib/session/shell-programs.js +21 -0
  150. package/dist/lib/session/state.js +2 -1
  151. package/dist/lib/session/stream-render.js +2 -1
  152. package/dist/lib/session/tool-calls.js +2 -5
  153. package/dist/lib/session/trajectory-html.js +2 -1
  154. package/dist/lib/session/trajectory.js +3 -12
  155. package/dist/lib/session/types.d.ts +25 -0
  156. package/dist/lib/session/types.js +10 -0
  157. package/dist/lib/share/publish.d.ts +53 -5
  158. package/dist/lib/share/publish.js +99 -17
  159. package/dist/lib/share/worker-template.js +594 -64
  160. package/dist/lib/startup/root-command.js +2 -1
  161. package/dist/lib/state.d.ts +24 -0
  162. package/dist/lib/state.js +318 -54
  163. package/dist/lib/sync-status.d.ts +4 -0
  164. package/dist/lib/sync-status.js +3 -0
  165. package/dist/lib/terminal/resolve.d.ts +7 -0
  166. package/dist/lib/terminal/resolve.js +41 -2
  167. package/dist/lib/traces/classify.js +24 -19
  168. package/dist/lib/traces/insights.d.ts +67 -0
  169. package/dist/lib/traces/insights.js +178 -0
  170. package/dist/lib/traces/phenotype.d.ts +67 -0
  171. package/dist/lib/traces/phenotype.js +437 -0
  172. package/dist/lib/traces/segments.d.ts +133 -0
  173. package/dist/lib/traces/segments.js +301 -0
  174. package/dist/lib/traces/sync.d.ts +33 -0
  175. package/dist/lib/traces/sync.js +11 -2
  176. package/dist/lib/types.d.ts +47 -1
  177. package/dist/lib/usage-refresh.js +2 -1
  178. package/dist/lib/view-types.d.ts +2 -0
  179. package/dist/lib/watchdog/runner.js +18 -4
  180. package/package.json +2 -1
@@ -18,7 +18,7 @@ import { sanitizeForTerminal, redactSecrets } from '../lib/redact.js';
18
18
  import { resolveProjectKey } from '../lib/project-key.js';
19
19
  import { listProjectDefs, resolveProjectNameForCwd } from '../lib/projects.js';
20
20
  import ora from 'ora';
21
- import { SESSION_AGENTS, isAgentTmuxAlias } from '../lib/session/types.js';
21
+ import { SESSION_AGENTS, isAgentTmuxAlias, sessionDisplayAgent } from '../lib/session/types.js';
22
22
  import { discoverArtifacts, readArtifact, resolveArtifact } from '../lib/session/artifacts.js';
23
23
  import { looksLikePath, toComparablePath, homeDir, needsWindowsShell, composeWin32CommandLine } from '../lib/platform/index.js';
24
24
  import { getActiveSessions, describeActiveDiscoveryHealth, sessionProcessIsLocal, backfillActiveRowsFromIndex, isRunningLiveSession, serializeActiveSessionsForJson, serializeSessionsJson, shortIdFromName } from '../lib/session/active.js';
@@ -233,9 +233,10 @@ async function renderArtifactsForSession(session, listAll, name) {
233
233
  console.log(chalk.gray('No file-write artifacts found in this session.'));
234
234
  return;
235
235
  }
236
- const agentColor = colorAgent(session.agent);
236
+ const shown = sessionDisplayAgent(session);
237
+ const agentColor = colorAgent(shown);
237
238
  console.log('');
238
- console.log(agentColor(session.agent) +
239
+ console.log(agentColor(shown) +
239
240
  chalk.gray(` · ${session.shortId} · ${formatRelativeTime(session.timestamp)}`));
240
241
  console.log(chalk.gray('─'.repeat(72)));
241
242
  for (const a of artifacts) {
@@ -726,7 +727,8 @@ function fitCell(content, room) {
726
727
  */
727
728
  export function renderActiveRowLines(s, indent, termW) {
728
729
  const idCol = chalk.dim(padToWidth((s.sessionId?.slice(0, 8)) ?? '-', ROW_ID_W));
729
- const kindCol = colorAgent(s.kind)(padToWidth(truncateToWidth(s.kind, ROW_AGENT_W), ROW_AGENT_W + 1));
730
+ const shownKind = sessionDisplayAgent({ agent: s.kind, harness: s.harness });
731
+ const kindCol = colorAgent(shownKind)(padToWidth(truncateToWidth(shownKind, ROW_AGENT_W), ROW_AGENT_W + 1));
730
732
  const versionCol = chalk.gray(padToWidth(truncateToWidth(s.version ?? '', ROW_VERSION_W), ROW_VERSION_W + 1));
731
733
  const statusCol = statusColor(s.status)(padToWidth(truncateToWidth(activityLabel(s), ROW_STATUS_W - 1), ROW_STATUS_W));
732
734
  const ownerCol = chalk.cyan(padToWidth(truncateToWidth(ownerLabel(s), ROW_OWNER_W - 1), ROW_OWNER_W));
@@ -1010,6 +1012,7 @@ export function serializeResolvedSessionsJson(sessions) {
1010
1012
  id: session.id,
1011
1013
  shortId: session.shortId,
1012
1014
  agent: session.agent,
1015
+ harness: session.harness,
1013
1016
  origin: session.origin,
1014
1017
  timestamp: session.timestamp,
1015
1018
  lastActivity: session.lastActivity,
@@ -1805,7 +1808,8 @@ function runOutcomeDetail(meta) {
1805
1808
  /** Compact per-session metadata line under a linked session row. */
1806
1809
  function linkedSessionMeta(session) {
1807
1810
  const parts = [];
1808
- parts.push(session.version ? `${session.agent} v${session.version}` : session.agent);
1811
+ const shown = sessionDisplayAgent(session);
1812
+ parts.push(session.version ? `${shown} v${session.version}` : shown);
1809
1813
  if (session.account)
1810
1814
  parts.push(session.account);
1811
1815
  if (session.model)
@@ -1974,7 +1978,7 @@ export async function renderSessionPreview(query, scope) {
1974
1978
  if (outcome.kind !== 'resolved'
1975
1979
  && (!scope.hosts?.length || shouldIncludeLocal(scope.hosts, machineId()))) {
1976
1980
  const discovered = applyScopeFilters(await discoverSessions({ all: true, cwd: process.cwd(), limit: 5000, waitForScan: true }), scope);
1977
- const localMatches = resolveSessionQuery(discovered, query, { indexFallback: false }).matches
1981
+ const localMatches = resolveSessionQuery(discovered, query, { indexFallback: false, scope }).matches
1978
1982
  .map(session => ({ ...session, machine: session.machine || machineId() }));
1979
1983
  const exact = localMatches.find(session => selectorAllowsEarlyExit(query)
1980
1984
  && session.id.toLowerCase() === query.trim().toLowerCase());
@@ -2670,7 +2674,11 @@ limitSource) {
2670
2674
  if (toolEvidenceMode) {
2671
2675
  const self = toolSelf;
2672
2676
  const selectedSessions = searchQuery
2673
- ? filterSessionsByQuery(sessions, searchQuery)
2677
+ ? filterSessionsByQuery(sessions, searchQuery, {
2678
+ agent: options.agent,
2679
+ project: options.project,
2680
+ routine: options.routine,
2681
+ })
2674
2682
  : sessions;
2675
2683
  const localSessions = selectedSessions;
2676
2684
  const mayFanOut = options.local !== true && process.env[NO_FANOUT_ENV] !== '1';
@@ -2767,7 +2775,9 @@ limitSource) {
2767
2775
  // that merely MENTIONS the id, defeating exact remote resolution. A genuine
2768
2776
  // search phrase keeps the ranked metadata+content path.
2769
2777
  const filtered = searchQuery
2770
- ? resolveSessionQuery(sessions, searchQuery).matches
2778
+ ? resolveSessionQuery(sessions, searchQuery, {
2779
+ scope: { agent: options.agent, project: options.project, routine: options.routine },
2780
+ }).matches
2771
2781
  : sessions;
2772
2782
  // JSON is the canonical picker contract: enrich the durable rows once
2773
2783
  // from the shared live cache so every consumer gets lifecycle/recovery
@@ -2876,7 +2886,7 @@ limitSource) {
2876
2886
  const message = pathFilter
2877
2887
  ? `Search sessions (${path.basename(pathFilter)}):`
2878
2888
  : formatSearchMessage(options);
2879
- const picked = await pickSessionInteractive(sessions, message, searchQuery, hiddenCount);
2889
+ const picked = await pickSessionInteractive(sessions, message, searchQuery, hiddenCount, undefined, { agent: options.agent, project: options.project, routine: options.routine });
2880
2890
  if (picked) {
2881
2891
  await handlePickedSession(picked);
2882
2892
  return;
@@ -2884,7 +2894,13 @@ limitSource) {
2884
2894
  return;
2885
2895
  }
2886
2896
  // Non-interactive fallback (piped output) or --flat/--tree.
2887
- const filtered = searchQuery ? filterSessionsByQuery(sessions, searchQuery) : sessions;
2897
+ const filtered = searchQuery
2898
+ ? filterSessionsByQuery(sessions, searchQuery, {
2899
+ agent: options.agent,
2900
+ project: options.project,
2901
+ routine: options.routine,
2902
+ })
2903
+ : sessions;
2888
2904
  const liveIndex = await maybeLiveIndex(options);
2889
2905
  printSessionTable(filtered, hiddenCount, options.tree === true, liveIndex);
2890
2906
  // Every listing path must say what it dropped — a hidden default that stays
@@ -2996,7 +3012,8 @@ function timeCell(age, topicSlack) {
2996
3012
  * when `showTicket` — otherwise a listing with no refs would waste a column of
2997
3013
  * dashes and needlessly truncate the topic. Worktree stays a trailing badge. */
2998
3014
  export function flatSessionRow(session, live, showTicket = false, cols = {}, bookmarked = false) {
2999
- const agentColor = colorAgent(session.agent);
3015
+ const shown = sessionDisplayAgent(session);
3016
+ const agentColor = colorAgent(shown);
3000
3017
  const age = sessionAgeParts(session.timestamp, session.lastActivity);
3001
3018
  const project = session.project || '-';
3002
3019
  const tag = originTag(session) || teamTag(session);
@@ -3045,7 +3062,7 @@ export function flatSessionRow(session, live, showTicket = false, cols = {}, boo
3045
3062
  const topicW = Math.max(MIN_TOPIC_W, width - fixedW - modelW - when.extraW);
3046
3063
  return (bookmarkCell +
3047
3064
  chalk.white(padToWidth(truncateToWidth(session.shortId, 9), 10)) +
3048
- agentColor(padToWidth(truncateToWidth(session.agent, 8), 9)) +
3065
+ agentColor(padToWidth(truncateToWidth(shown, 8), 9)) +
3049
3066
  chalk.yellow(padToWidth(truncateToWidth(session.version || '-', 7), 8)) +
3050
3067
  (modelW ? chalk.yellow(padToWidth(truncateToWidth(modelLabel(session.model), modelW - 1), modelW)) : '') +
3051
3068
  machineCell +
@@ -3060,7 +3077,8 @@ export function flatSessionRow(session, live, showTicket = false, cols = {}, boo
3060
3077
  }
3061
3078
  /** One tree-mode row (grouped under a dir header): id · agent · badges · topic · time. No version/project column. */
3062
3079
  function treeSessionRow(session, live) {
3063
- const agentColor = colorAgent(session.agent);
3080
+ const shown = sessionDisplayAgent(session);
3081
+ const agentColor = colorAgent(shown);
3064
3082
  const age = sessionAgeParts(session.timestamp, session.lastActivity);
3065
3083
  const tag = originTag(session) || teamTag(session);
3066
3084
  const label = session.label;
@@ -3082,7 +3100,7 @@ function treeSessionRow(session, live) {
3082
3100
  const topicW = Math.max(12, baseTopicW - when.extraW);
3083
3101
  return (' ' +
3084
3102
  chalk.dim(padToWidth(session.shortId, 9)) +
3085
- agentColor(padToWidth(truncateToWidth(session.agent, 7), 8)) +
3103
+ agentColor(padToWidth(truncateToWidth(shown, 7), 8)) +
3086
3104
  (badges ? badges + ' ' : '') +
3087
3105
  (glyph ? glyph + ' ' : '') +
3088
3106
  statusCell +
@@ -3246,7 +3264,8 @@ const TEAM_HANDLE_W = 16;
3246
3264
  * identity the `--teams` view exists to show (RUSH-1997).
3247
3265
  */
3248
3266
  function teamMemberRow(session, live) {
3249
- const agentColor = colorAgent(session.agent);
3267
+ const shown = sessionDisplayAgent(session);
3268
+ const agentColor = colorAgent(shown);
3250
3269
  const origin = session.teamOrigin;
3251
3270
  const age = sessionAgeParts(session.timestamp, session.lastActivity);
3252
3271
  const handle = safeTeamText(origin?.handle) ?? session.shortId;
@@ -3267,7 +3286,7 @@ function teamMemberRow(session, live) {
3267
3286
  const topicW = Math.max(12, baseTopicW - when.extraW);
3268
3287
  return (' ' +
3269
3288
  chalk.dim(padToWidth(session.shortId, 9)) +
3270
- agentColor(padToWidth(truncateToWidth(session.agent, 7), 8)) +
3289
+ agentColor(padToWidth(truncateToWidth(shown, 7), 8)) +
3271
3290
  chalk.yellow(padToWidth(truncateToWidth(mode || '-', TEAM_MODE_W), TEAM_MODE_W + 1)) +
3272
3291
  (badges ? badges + ' ' : '') +
3273
3292
  (glyph ? glyph + ' ' : '') +
@@ -3444,13 +3463,14 @@ function renderArchivedSession(session, mode, options = {}) {
3444
3463
  }, null, 2));
3445
3464
  return;
3446
3465
  }
3447
- const agentColor = colorAgent(session.agent);
3466
+ const shown = sessionDisplayAgent(session);
3467
+ const agentColor = colorAgent(shown);
3448
3468
  const absTime = formatAbsoluteTime(session.timestamp);
3449
3469
  const title = session.label || session.topic;
3450
3470
  console.log('');
3451
3471
  if (title)
3452
3472
  console.log(chalk.bold.white(title));
3453
- console.log(agentColor(session.agent) +
3473
+ console.log(agentColor(shown) +
3454
3474
  (session.version ? chalk.yellow(` ${session.version}`) : '') +
3455
3475
  (session.project ? chalk.cyan(` ${session.project}`) : '') +
3456
3476
  chalk.gray(` ${absTime} (${formatRelativeTime(session.timestamp)})`) +
@@ -3481,7 +3501,7 @@ async function renderSession(session, mode, filters, options = {}) {
3481
3501
  console.log(chalk.yellow('Session transcript not available (file no longer exists).'));
3482
3502
  console.log(chalk.gray(`Path: ${session.filePath}`));
3483
3503
  if (session.version)
3484
- console.log(chalk.gray(`Version: ${session.agent} ${session.version}`));
3504
+ console.log(chalk.gray(`Version: ${sessionDisplayAgent(session)} ${session.version}`));
3485
3505
  if (session.project)
3486
3506
  console.log(chalk.gray(`Project: ${session.project}`));
3487
3507
  if (session.account)
@@ -3489,11 +3509,12 @@ async function renderSession(session, mode, filters, options = {}) {
3489
3509
  console.log(chalk.gray(`Time: ${session.timestamp}`));
3490
3510
  return;
3491
3511
  }
3492
- const spinner = ora(`Parsing ${session.agent} session...`).start();
3512
+ const spinner = ora(`Parsing ${sessionDisplayAgent(session)} session...`).start();
3493
3513
  const parsedEvents = parseSession(session.filePath, session.agent);
3494
3514
  spinner.stop();
3495
3515
  let events = filterEvents(parsedEvents, filters);
3496
- const agentColor = colorAgent(session.agent);
3516
+ const shown = sessionDisplayAgent(session);
3517
+ const agentColor = colorAgent(shown);
3497
3518
  console.log('');
3498
3519
  if (mode === 'summary') {
3499
3520
  const stats = computeSummaryStats(events);
@@ -3507,7 +3528,7 @@ async function renderSession(session, mode, filters, options = {}) {
3507
3528
  const badges = signalBadges(metaSignals(session));
3508
3529
  console.log(chalk.bold.white(title) + (badges ? ' ' + badges : ''));
3509
3530
  }
3510
- console.log(agentColor(session.agent) +
3531
+ console.log(agentColor(shown) +
3511
3532
  (session.version ? chalk.yellow(` ${session.version}`) : '') +
3512
3533
  modelStr +
3513
3534
  (session.project ? chalk.cyan(` ${session.project}`) + branchStr : branchStr) +
@@ -3521,7 +3542,7 @@ async function renderSession(session, mode, filters, options = {}) {
3521
3542
  return;
3522
3543
  }
3523
3544
  if (mode === 'markdown') {
3524
- console.log(agentColor(session.agent) +
3545
+ console.log(agentColor(shown) +
3525
3546
  (session.version ? chalk.yellow(` ${session.version}`) : '') +
3526
3547
  (session.project ? chalk.cyan(` ${session.project}`) : '') +
3527
3548
  chalk.gray(` ${formatRelativeTime(session.timestamp)}`) +
@@ -3662,7 +3683,8 @@ export function liveHostLabel(a) {
3662
3683
  return viewer && viewer !== a.host ? `${a.host}→${viewer}` : a.host;
3663
3684
  }
3664
3685
  export function formatPickerLabel(s, query, cols = {}, ssh, host = '', bookmarked = false, live) {
3665
- const agentColor = colorAgent(s.agent);
3686
+ const shown = sessionDisplayAgent(s);
3687
+ const agentColor = colorAgent(shown);
3666
3688
  const age = sessionAgeParts(s.timestamp, s.lastActivity);
3667
3689
  const project = s.project || '-';
3668
3690
  // SSH-launch origin (live rows only): mirrors the flat listing's `ssh←<device>`
@@ -3727,7 +3749,7 @@ export function formatPickerLabel(s, query, cols = {}, ssh, host = '', bookmarke
3727
3749
  // live row with no session id is named by its pid or cloud task, which can
3728
3750
  // run past the column and shunt every later column out of alignment.
3729
3751
  chalk.white(padRight(truncate(s.shortId, 9), 10)) +
3730
- agentColor(padRight(truncate(s.agent, 8), 9)) +
3752
+ agentColor(padRight(truncate(shown, 8), 9)) +
3731
3753
  chalk.yellow(padRight(truncate(versionStr, 7), 8)) +
3732
3754
  machineCell +
3733
3755
  hostCell +
@@ -3754,7 +3776,7 @@ const PICKER_TIPS = [
3754
3776
  export function formatPickerTip(sessions) {
3755
3777
  return chalk.gray(PICKER_TIPS[sessions.length % PICKER_TIPS.length]);
3756
3778
  }
3757
- export async function pickSessionInteractive(sessions, message = 'Search sessions:', initialSearch, hiddenCount = 0, enterHint) {
3779
+ export async function pickSessionInteractive(sessions, message = 'Search sessions:', initialSearch, hiddenCount = 0, enterHint, scope) {
3758
3780
  // The hidden-session footer is console.log'd above the Inquirer prompt, so it
3759
3781
  // scrolls the viewport the picker can't measure; tell the picker to reserve for
3760
3782
  // it (see pickerPageSize) so the preview and the footer stay on screen together.
@@ -3774,7 +3796,7 @@ export async function pickSessionInteractive(sessions, message = 'Search session
3774
3796
  // Typing: search the full pool.
3775
3797
  if (!query.trim())
3776
3798
  return sessions;
3777
- return filterSessionsByQuery(sessions, query);
3799
+ return filterSessionsByQuery(sessions, query, scope);
3778
3800
  },
3779
3801
  labelFor: (s, query) => formatPickerLabel(s, query, cols),
3780
3802
  pageSize: PICKER_RECENT_COUNT,
@@ -4169,7 +4191,7 @@ export function resolveSessionQuery(pool, query, options = {}) {
4169
4191
  const matches = options.indexFallback === false ? [] : findSessionsById(normalized);
4170
4192
  return { matches, byId: true, completeId };
4171
4193
  }
4172
- return { matches: filterSessionsByQuery(pool, normalized), byId: false, completeId };
4194
+ return { matches: filterSessionsByQuery(pool, normalized, options.scope), byId: false, completeId };
4173
4195
  }
4174
4196
  /** Explain an ambiguous resolution. Only a short id can be lengthened: a complete
4175
4197
  * id is already maximal, and a search phrase was never an id to begin with. */
@@ -4208,7 +4230,7 @@ export function fleetNotFoundMessage(query, deviceCount, unreachable) {
4208
4230
  return lines;
4209
4231
  }
4210
4232
  /** Filter and rank sessions by a multi-term search query across metadata and content. */
4211
- export function filterSessionsByQuery(sessions, query) {
4233
+ export function filterSessionsByQuery(sessions, query, scope) {
4212
4234
  const trimmed = query?.trim().toLowerCase() || '';
4213
4235
  if (!trimmed)
4214
4236
  return sessions;
@@ -4218,7 +4240,9 @@ export function filterSessionsByQuery(sessions, query) {
4218
4240
  return sessions.filter((session) => session.agent === agent && session.version === version);
4219
4241
  }
4220
4242
  const terms = trimmed.split(/\s+/).filter(Boolean);
4221
- const contentIndex = searchContentIndex(sessions, trimmed);
4243
+ // Hydrate FTS hits missing from the pool (PHNX-2767), then drop hits that
4244
+ // fail --project/--agent/--routine so those flags are not undone by the union.
4245
+ const contentIndex = scopedContentIndex(sessions, trimmed, scope);
4222
4246
  // If the query exactly matches a session label, short-circuit the structural
4223
4247
  // scorer (which would otherwise surface every session whose topic happens to
4224
4248
  // contain the same words) and return only the label hits.
@@ -4227,7 +4251,15 @@ export function filterSessionsByQuery(sessions, query) {
4227
4251
  if (exactLabelHits.length > 0) {
4228
4252
  return exactLabelHits.sort((a, b) => (b._bm25Score ?? 0) - (a._bm25Score ?? 0));
4229
4253
  }
4230
- return sessions
4254
+ // The listing pool is a page of the index. FTS hits the whole index. Union
4255
+ // so a grep-visible transcript is returned even when it missed the page
4256
+ // (PHNX-2767).
4257
+ const poolById = new Map(sessions.map(s => [s.id, s]));
4258
+ for (const [id, hit] of contentIndex) {
4259
+ if (!poolById.has(id))
4260
+ poolById.set(id, hit);
4261
+ }
4262
+ return [...poolById.values()]
4231
4263
  .map(session => ({ session, score: scoreSessionQuery(session, terms) }))
4232
4264
  .filter(entry => {
4233
4265
  // Include if scored by topic/project/etc, or matched by content search
@@ -4304,6 +4336,11 @@ function scoreSessionQuery(session, terms) {
4304
4336
  * Without this, a query like "scoped search" could match sessions in BOTH
4305
4337
  * the project you specified AND elsewhere, producing an ambiguity error
4306
4338
  * even though the user already pointed at the correct scope.
4339
+ *
4340
+ * PHNX-2767 hydrates FTS hits missing from the listing pool *after* this
4341
+ * runs, so callers that search must also pass the same scope into
4342
+ * `filterSessionsByQuery` / `scopedContentIndex` or the union
4343
+ * reintroduces the rows this function just dropped.
4307
4344
  */
4308
4345
  export function applyScopeFilters(sessions, scope) {
4309
4346
  let filtered = sessions;
@@ -4340,6 +4377,23 @@ export function applyScopeFilters(sessions, scope) {
4340
4377
  }
4341
4378
  return filtered;
4342
4379
  }
4380
+ /**
4381
+ * PHNX-2767 hydrates FTS hits missing from the listing pool. That union is
4382
+ * unscoped on purpose (cwd/limit are a page of the index, not a filter).
4383
+ * `--project` / `--agent` / `--routine` ARE filters: drop hydrated hits that
4384
+ * fail them so content search cannot undo a scope the user already set.
4385
+ */
4386
+ function scopedContentIndex(sessions, query, scope) {
4387
+ const hits = searchContentIndex(sessions, query);
4388
+ if (!scope || (!scope.agent && !scope.project && !scope.routine))
4389
+ return hits;
4390
+ const kept = new Map();
4391
+ for (const [id, session] of hits) {
4392
+ if (applyScopeFilters([session], scope).length > 0)
4393
+ kept.set(id, session);
4394
+ }
4395
+ return kept;
4396
+ }
4343
4397
  export function artifactLookupScope(agent, project, routine) {
4344
4398
  return { agent, project, routine };
4345
4399
  }
@@ -4355,7 +4409,7 @@ async function renderArtifactsGlobal(query, listAll, name, scope) {
4355
4409
  });
4356
4410
  tracker.stop();
4357
4411
  const allSessions = applyScopeFilters(discovered, scope);
4358
- const { matches: queryMatches, byId, completeId } = resolveSessionQuery(allSessions, query);
4412
+ const { matches: queryMatches, byId, completeId } = resolveSessionQuery(allSessions, query, { scope });
4359
4413
  if (queryMatches.length === 0) {
4360
4414
  spinner.stop();
4361
4415
  if (byId)
@@ -4452,7 +4506,7 @@ async function renderOneSession(query, mode, scope) {
4452
4506
  tracker.stop();
4453
4507
  const allSessions = applyScopeFilters(discovered, scope);
4454
4508
  let session;
4455
- const resolution = resolveSessionQuery(allSessions, query);
4509
+ const resolution = resolveSessionQuery(allSessions, query, { scope });
4456
4510
  let queryMatches = resolution.matches;
4457
4511
  let byId = resolution.byId;
4458
4512
  const completeId = resolution.completeId;
@@ -4464,7 +4518,7 @@ async function renderOneSession(query, mode, scope) {
4464
4518
  // echoes the id in a resume prompt. Gate on looksLikeSessionId, not just
4465
4519
  // completeId, so a short id resolves to "no match" rather than fuzzy content.
4466
4520
  if (queryMatches.length === 0 && !looksLikeSessionId(query)) {
4467
- const contentResults = searchContentIndex(allSessions, query);
4521
+ const contentResults = scopedContentIndex(allSessions, query, scope);
4468
4522
  if (contentResults.size > 0) {
4469
4523
  const matchedSessions = Array.from(contentResults.values())
4470
4524
  .sort((a, b) => (b._bm25Score ?? 0) - (a._bm25Score ?? 0));
@@ -4659,16 +4713,16 @@ export function fleetCandidatesByQuery(rows, query, trustResolvedRows = false) {
4659
4713
  });
4660
4714
  }
4661
4715
  /** Resolve through the canonical metadata+content union used by keyword search. */
4662
- function resolveIndexedMetadataRows(indexed, selector) {
4716
+ function resolveIndexedMetadataRows(indexed, selector, scope) {
4663
4717
  const alias = resolveSessionAlias(selector);
4664
4718
  if (alias.kind === 'resolved') {
4665
- return resolveSessionQuery(indexed, alias.sessionId, { indexFallback: false }).matches;
4719
+ return resolveSessionQuery(indexed, alias.sessionId, { indexFallback: false, scope }).matches;
4666
4720
  }
4667
4721
  if (alias.kind === 'ambiguous') {
4668
4722
  const ids = new Set(alias.sessionIds.map(id => id.toLowerCase()));
4669
4723
  return indexed.filter(session => ids.has(session.id.toLowerCase()));
4670
4724
  }
4671
- return resolveSessionQuery(indexed, selector, { indexFallback: false }).matches;
4725
+ return resolveSessionQuery(indexed, selector, { indexFallback: false, scope }).matches;
4672
4726
  }
4673
4727
  /**
4674
4728
  * ID-shaped selectors go straight to SQLite's primary-key/short-id lookup.
@@ -4779,7 +4833,7 @@ export async function computeLocalMetadataMatches(selector, scope, deps = {}) {
4779
4833
  const includeLocal = !scope.hosts?.length || shouldIncludeLocal(scope.hosts, localMachine);
4780
4834
  if (!includeLocal)
4781
4835
  return [];
4782
- const indexed = resolveIndexedMetadataRows(indexedRowsForSelector(selector, scope), selector);
4836
+ const indexed = resolveIndexedMetadataRows(indexedRowsForSelector(selector, scope), selector, scope);
4783
4837
  if (indexed.length > 0 || !looksLikeSessionId(selector)) {
4784
4838
  return indexed.map(session => ({ ...session, machine: session.machine || localMachine }));
4785
4839
  }
@@ -4796,7 +4850,7 @@ export async function computeLocalMetadataMatches(selector, scope, deps = {}) {
4796
4850
  */
4797
4851
  export async function liveMetadataMatches(selector, scope, self, deps = {}) {
4798
4852
  const load = deps.loadActive ?? loadLocalActiveSessions;
4799
- const match = (metas) => resolveIndexedMetadataRows(applyScopeFilters(metas, scope), selector);
4853
+ const match = (metas) => resolveIndexedMetadataRows(applyScopeFilters(metas, scope), selector, scope);
4800
4854
  try {
4801
4855
  const cached = liveSessionMetas((await load()).sessions, self, Date.now());
4802
4856
  const hit = match(cached);
@@ -5013,7 +5067,7 @@ export function registerSessionsCommands(program) {
5013
5067
  .option('--preview', 'With a session id/query: print a compact preview and exit (no pager)');
5014
5068
  setHelpSections(sessionsCmd, {
5015
5069
  examples: `
5016
- # Search prior sessions in this project by topic, file path, or command
5070
+ # Search indexed transcripts by topic, file path, or command
5017
5071
  agents sessions "add auth middleware"
5018
5072
 
5019
5073
  # Read a session as markdown (user + assistant + thinking + tools)
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `agents setup accounts` — mint a Claude setup-token so unattended usage/probe
3
+ * is not stuck on "usage pending". Delegates to the same mint engine as
4
+ * `agents accounts mint` / `agents auth mint`.
5
+ */
6
+ import type { Command } from 'commander';
7
+ export declare function runAccountsSetupWizard(): Promise<void>;
8
+ export declare function registerSetupAccountsCommand(setupCmd: Command): void;
@@ -0,0 +1,47 @@
1
+ import chalk from 'chalk';
2
+ import { confirm } from '@inquirer/prompts';
3
+ import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
4
+ import { hasMintedSetupToken, mintAndSeed } from '../lib/auth-mint.js';
5
+ export async function runAccountsSetupWizard() {
6
+ const status = hasMintedSetupToken();
7
+ if (status.ready) {
8
+ console.log(chalk.green(`Setup-token already minted (${status.detail}).`));
9
+ console.log(chalk.gray('Re-mint with: agents accounts mint claude'));
10
+ return;
11
+ }
12
+ if (!isInteractiveTerminal()) {
13
+ console.log(chalk.yellow('No Claude setup-token on this machine.'));
14
+ console.log(chalk.gray('Mint one with: agents accounts mint claude'));
15
+ console.log(chalk.gray('Already have a token: agents accounts mint claude --token-stdin'));
16
+ process.exitCode = 1;
17
+ return;
18
+ }
19
+ const proceed = await confirm({
20
+ message: 'Mint a Claude setup-token now? (opens a browser; seeds a named account + the reserved auth bundle)',
21
+ default: true,
22
+ });
23
+ if (!proceed) {
24
+ console.log(chalk.gray('Skipped. Run `agents accounts mint claude` when ready.'));
25
+ return;
26
+ }
27
+ const result = await mintAndSeed({ harness: 'claude' });
28
+ console.log(chalk.green(`Minted account '${result.account}' for ${result.email}.`));
29
+ }
30
+ export function registerSetupAccountsCommand(setupCmd) {
31
+ setupCmd
32
+ .command('accounts')
33
+ .description('Mint a Claude setup-token into a named account (unattended usage/probe).')
34
+ .action(async () => {
35
+ try {
36
+ await runAccountsSetupWizard();
37
+ }
38
+ catch (err) {
39
+ if (isPromptCancelled(err)) {
40
+ console.log(chalk.yellow('\nCancelled'));
41
+ return;
42
+ }
43
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
44
+ process.exitCode = 1;
45
+ }
46
+ });
47
+ }
@@ -31,7 +31,7 @@ export declare function ensureInitialized(program: Command): Promise<void>;
31
31
  * wizard. Never throws — a cancel or an optional wizard's error just skips the
32
32
  * rest and lets core setup complete.
33
33
  */
34
- export type SetupPhase = 'browser' | 'computer' | 'share' | 'secrets' | 'fleet' | 'watchdog' | 'preferences';
34
+ export type SetupPhase = 'browser' | 'computer' | 'share' | 'secrets' | 'accounts' | 'fleet' | 'watchdog' | 'preferences';
35
35
  export type SetupStatusState = 'ready' | 'missing' | 'n/a';
36
36
  export interface SetupStatusRow {
37
37
  phase: 'core' | SetupPhase;
@@ -24,7 +24,9 @@ import { runShareWizard } from './artifacts-setup.js';
24
24
  import { registerSetupMineCommand } from './setup-mine.js';
25
25
  import { registerSetupSecretsCommand } from './setup-secrets.js';
26
26
  import { registerSetupFleetCommand } from './setup-fleet.js';
27
+ import { registerSetupAccountsCommand, runAccountsSetupWizard } from './setup-accounts.js';
27
28
  import { registerSetupWatchdogCommand, runWatchdogSetupWizard } from './setup-watchdog.js';
29
+ import { hasMintedSetupToken } from '../lib/auth-mint.js';
28
30
  import { registerAliasCommand } from './alias.js';
29
31
  import { registerBetaCommands } from './beta.js';
30
32
  import { runPreferencesStep } from './setup-preferences.js';
@@ -276,6 +278,7 @@ export async function getSetupStatus() {
276
278
  const devices = await loadDevices();
277
279
  const coreReady = isGitRepo(getAgentsDir());
278
280
  const secretsReady = fs.existsSync(setupSecretsPrefsPath());
281
+ const minted = hasMintedSetupToken();
279
282
  const shareConfig = readShareConfig();
280
283
  const watchdogEnabled = getConfigValue('watchdog.enabled').value === true;
281
284
  const interactiveHost = getConfigValue('interactive.host').value;
@@ -285,6 +288,7 @@ export async function getSetupStatus() {
285
288
  { phase: 'browser', state: browserReady ? 'ready' : 'missing', detail: browserReady ? `profile ${browserProfile.name}` : browserProfile ? `profile ${browserProfile.name} cannot launch here` : installedBrowsers.length ? 'no default profile' : 'no supported browser found' },
286
289
  { phase: 'computer', state: computerState, detail: computerState === 'ready' ? 'helper trusted' : computerState === 'n/a' ? 'macOS local setup only' : 'helper not running or not trusted' },
287
290
  { phase: 'secrets', state: secretsReady ? 'ready' : 'missing', detail: secretsReady ? 'defaults chosen' : 'defaults not chosen' },
291
+ { phase: 'accounts', state: minted.ready ? 'ready' : 'missing', detail: minted.detail },
288
292
  { phase: 'fleet', state: Object.keys(devices).length ? 'ready' : 'missing', detail: Object.keys(devices).length ? `${Object.keys(devices).length} device${Object.keys(devices).length === 1 ? '' : 's'} registered` : 'no devices registered' },
289
293
  { phase: 'share', state: shareConfig ? 'ready' : 'missing', detail: shareConfig?.baseUrl ?? 'endpoint not configured' },
290
294
  { phase: 'watchdog', state: watchdogEnabled ? 'ready' : 'missing', detail: watchdogEnabled ? 'enabled on this device' : 'disabled on this device' },
@@ -308,6 +312,8 @@ async function runSetupPhase(phase) {
308
312
  await runShareWizard();
309
313
  else if (phase === 'secrets')
310
314
  await import('./setup-secrets.js').then((m) => m.runSecretsSetupWizard());
315
+ else if (phase === 'accounts')
316
+ await runAccountsSetupWizard();
311
317
  else if (phase === 'fleet')
312
318
  await import('./setup-fleet.js').then((m) => m.runFleetSetupWizard());
313
319
  else if (phase === 'watchdog')
@@ -364,19 +370,20 @@ export function registerSetupCommand(program) {
364
370
  .description('Set up agents-cli, or re-open the capability onboarding hub.')
365
371
  .option('-f, --force', 'Re-run setup even if ~/.agents/.system/ already exists (use with caution)')
366
372
  .option('--no-system-repo', 'Skip cloning the system repo (you must populate ~/.agents/.system/ yourself)');
367
- // Capability subcommands: `agents setup browser|computer|mine|secrets|fleet|alias|beta`.
373
+ // Capability subcommands: `agents setup browser|computer|mine|secrets|accounts|fleet|alias|beta`.
368
374
  // Share/artifact publishing is set up by `agents artifacts setup` (RUSH-2580);
369
375
  // the hub below still offers it as a phase via runShareWizard.
370
376
  registerSetupBrowserCommand(setupCmd);
371
377
  registerSetupComputerCommand(setupCmd);
372
378
  registerSetupMineCommand(setupCmd);
373
379
  registerSetupSecretsCommand(setupCmd);
380
+ registerSetupAccountsCommand(setupCmd);
374
381
  registerSetupFleetCommand(setupCmd);
375
382
  registerSetupWatchdogCommand(setupCmd);
376
383
  registerAliasCommand(setupCmd);
377
384
  registerBetaCommands(setupCmd);
378
385
  setupCmd.command('status')
379
- .description('Show setup readiness for core, browser, computer, secrets, fleet, share, watchdog, and preferences.')
386
+ .description('Show setup readiness for core, browser, computer, secrets, accounts, fleet, share, watchdog, and preferences.')
380
387
  .option('--json', 'print machine-readable JSON')
381
388
  .action(async (options) => {
382
389
  const rows = await getSetupStatus();
@@ -399,6 +406,7 @@ export function registerSetupCommand(program) {
399
406
  agents setup browser
400
407
  agents setup computer
401
408
  agents setup secrets
409
+ agents setup accounts
402
410
  agents setup fleet
403
411
  agents setup watchdog
404
412
  agents setup alias add teams
@@ -416,6 +424,7 @@ export function registerSetupCommand(program) {
416
424
  agents setup computer # install the signed macOS helper + grant permissions
417
425
  agents artifacts setup # provision or join a Cloudflare share endpoint
418
426
  agents setup secrets # choose secrets backend/policy defaults + import
427
+ agents setup accounts # mint a Claude setup-token for unattended usage/probe
419
428
  agents setup fleet # discover Tailscale devices + configure SSH access
420
429
  agents setup watchdog # choose which devices run the daemon watchdog pass
421
430
  agents setup alias add teams # PATH shorthand: teams runs agents teams
@@ -1,7 +1,7 @@
1
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
- import { type PublishResult } from '../lib/share/publish.js';
4
+ import { type PublishResult, type ShareVisibility } from '../lib/share/publish.js';
5
5
  import { deleteShare, type DeleteShareResult } from '../lib/share/delete.js';
6
6
  import type { PhoenixSession } from '../lib/identity/client.js';
7
7
  export declare function formatSharePublishResult(result: PublishResult, json?: boolean): string;
@@ -36,6 +36,9 @@ export interface ShareListItem {
36
36
  repo: string | null;
37
37
  /** Count of retained prior versions under this slug (see `share revisions`). */
38
38
  revisionCount: number;
39
+ /** The visibility stamped at publish time: public | unlisted | me | org.
40
+ * Missing on Workers that predate this field → treated as public. */
41
+ visibility: ShareVisibility;
39
42
  /** Arbitrary `--meta key=value` entries attached at publish time (RUSH-2683)
40
43
  * — everything that isn't a reserved provenance/label key. `{}` when none
41
44
  * were set, or when the deployed Worker predates this field. */
@@ -47,12 +50,41 @@ export interface ShareListResult {
47
50
  count: number;
48
51
  objects: ShareListItem[];
49
52
  }
50
- /** DI seam for tests — override the real HTTP GET of the JSON listing route. */
51
- export type ListingFetchFn = (url: string) => Promise<{
53
+ /** DI seam for tests — override the real HTTP GET of the JSON listing route.
54
+ * The optional `headers` carry the owner bearer for hidden-visibility listings. */
55
+ export type ListingFetchFn = (url: string, headers?: Record<string, string>) => Promise<{
52
56
  status: number;
53
57
  contentType: string;
54
58
  body: string;
55
59
  }>;
60
+ export interface ShareEditResult {
61
+ ok: true;
62
+ url: string;
63
+ label: string | null;
64
+ meta: Record<string, string>;
65
+ /** The visibility the page now carries (present on Workers that support the
66
+ * visibility edit; absent from an older deployed template). */
67
+ visibility?: ShareVisibility;
68
+ /** The visibility the page had before this edit, when the Worker reports it. */
69
+ previousVisibility?: ShareVisibility;
70
+ }
71
+ export declare function runShareEdit(target: string, opts: {
72
+ githubUser?: string;
73
+ config?: ShareConfig;
74
+ writeToken?: string;
75
+ byo?: boolean;
76
+ session?: PhoenixSession | null;
77
+ label?: string | null;
78
+ meta?: Record<string, string>;
79
+ metaMode?: 'merge' | 'replace';
80
+ removeMeta?: string[];
81
+ /** Change the page's visibility in place (public | unlisted | me | org).
82
+ * A metadata-only rewrite like label/meta — the body is untouched, so no
83
+ * revision is created. me/org require a Phoenix session. */
84
+ visibility?: ShareVisibility;
85
+ force?: boolean;
86
+ fetchEdit?: typeof fetch;
87
+ }): Promise<ShareEditResult>;
56
88
  /** Parse the Worker's listing JSON into a validated result, failing loud (with the
57
89
  * outdated-template hint) on any body that isn't the expected shape — an old Worker
58
90
  * serves the HTML gallery for a non-empty namespace, which must not be silently
@@ -79,7 +111,24 @@ export declare function runShareList(opts?: {
79
111
  session?: string;
80
112
  /** Only shares whose label contains this text (case-insensitive substring). */
81
113
  label?: string;
114
+ /** Only shares matching every provided `--meta key=value` entry. */
115
+ meta?: Record<string, string>;
116
+ /** Visibility filter. `public` (default) lists only public gallery pages;
117
+ * `unlisted`/`me`/`org` include that owner's hidden pages; `all` includes
118
+ * every active page. Hidden scopes send the owner's bearer and `scope=mine`. */
119
+ scope?: 'public' | 'unlisted' | 'me' | 'org' | 'all';
82
120
  }): Promise<ShareListResult>;
121
+ /** Client-side filtering over an already-fetched listing (RUSH-2683) — the
122
+ * Worker has no query surface for this, so it narrows the fetched set instead
123
+ * of a second round trip. `count` reflects the FILTERED set, matching what the
124
+ * caller actually sees. */
125
+ export declare function applyShareListFilters(result: ShareListResult, filters: {
126
+ agent?: string;
127
+ session?: string;
128
+ label?: string;
129
+ meta?: Record<string, string>;
130
+ scope?: 'public' | 'unlisted' | 'me' | 'org' | 'all';
131
+ }): ShareListResult;
83
132
  export declare function formatShareList(result: ShareListResult, json?: boolean): string;
84
133
  /** One retained prior version of a slug, as reported by the Worker's
85
134
  * `?revisions=json` route. */