@wirecat/cli-core 0.399.0 → 0.401.0

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 (259) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +1 -1
  3. package/dist/analysis/consents.d.ts +11 -6
  4. package/dist/analysis/consents.js +5 -2
  5. package/dist/cli/bot/mcp.js +2 -1
  6. package/dist/cli/bot/store.js +1 -1
  7. package/dist/cli/config-command.js +20 -1
  8. package/dist/cli/context.d.ts +2 -0
  9. package/dist/cli/context.js +1 -1
  10. package/dist/cli/effects.d.ts +4 -1
  11. package/dist/cli/effects.js +10 -2
  12. package/dist/cli/index.d.ts +3 -2
  13. package/dist/cli/index.js +3 -2
  14. package/dist/cli/json-fields.js +12 -1
  15. package/dist/cli/meeting-evidence-answer.d.ts +46 -0
  16. package/dist/cli/meeting-evidence-answer.js +22 -0
  17. package/dist/cli/meeting-preparation-answer.js +1 -13
  18. package/dist/cli/messenger/account-command.js +15 -6
  19. package/dist/cli/messenger/account-profile-query.d.ts +2 -0
  20. package/dist/cli/messenger/account-profile-query.js +13 -0
  21. package/dist/cli/messenger/account-sessions-command.js +1 -2
  22. package/dist/cli/messenger/admin-commands.js +12 -12
  23. package/dist/cli/messenger/admin-contacts-command.js +6 -3
  24. package/dist/cli/messenger/admin-folders-command.js +5 -5
  25. package/dist/cli/messenger/admin-moderation-command.js +4 -4
  26. package/dist/cli/messenger/admin-statistics-command.js +6 -14
  27. package/dist/cli/messenger/analysis-consents-query.d.ts +5 -0
  28. package/dist/cli/messenger/analysis-consents-query.js +20 -0
  29. package/dist/cli/messenger/archive-commands.js +3 -3
  30. package/dist/cli/messenger/attachments-command.js +3 -0
  31. package/dist/cli/messenger/backfill-command.js +5 -4
  32. package/dist/cli/messenger/chat-queries.d.ts +1 -0
  33. package/dist/cli/messenger/chat-queries.js +12 -2
  34. package/dist/cli/messenger/chats-command.js +2 -2
  35. package/dist/cli/messenger/chats-documents-command.js +7 -5
  36. package/dist/cli/messenger/chats-members-command.js +7 -3
  37. package/dist/cli/messenger/chats-read-command.js +1 -1
  38. package/dist/cli/messenger/chats-tracking-command.js +14 -6
  39. package/dist/cli/messenger/contacts-command.js +9 -5
  40. package/dist/cli/messenger/conversations-command.js +20 -16
  41. package/dist/cli/messenger/doctor-command.js +4 -1
  42. package/dist/cli/messenger/document-links-query.d.ts +2 -0
  43. package/dist/cli/messenger/document-links-query.js +9 -0
  44. package/dist/cli/messenger/download-command.js +1 -0
  45. package/dist/cli/messenger/flood-command.d.ts +2 -2
  46. package/dist/cli/messenger/flood-command.js +3 -3
  47. package/dist/cli/messenger/gaps-command.js +2 -2
  48. package/dist/cli/messenger/guard-commands.js +35 -13
  49. package/dist/cli/messenger/images-command.js +1 -0
  50. package/dist/cli/messenger/media-archive-command.js +2 -0
  51. package/dist/cli/messenger/media-archive-move-command.js +1 -0
  52. package/dist/cli/messenger/media-archive-timer-command.js +1 -1
  53. package/dist/cli/messenger/mentions-command.js +15 -2
  54. package/dist/cli/messenger/message-writes.d.ts +192 -2
  55. package/dist/cli/messenger/message-writes.js +6 -2
  56. package/dist/cli/messenger/messages-command.js +2 -0
  57. package/dist/cli/messenger/messages-delete-command.js +1 -0
  58. package/dist/cli/messenger/messages-edit-command.js +1 -0
  59. package/dist/cli/messenger/messages-evidence-command.js +3 -1
  60. package/dist/cli/messenger/messages-forward-command.js +1 -0
  61. package/dist/cli/messenger/messages-pin-command.js +3 -0
  62. package/dist/cli/messenger/messages-send-command.js +10 -6
  63. package/dist/cli/messenger/metadata-command.js +6 -1
  64. package/dist/cli/messenger/models-command.js +10 -5
  65. package/dist/cli/messenger/polls-command.js +4 -0
  66. package/dist/cli/messenger/private-people-command.js +3 -3
  67. package/dist/cli/messenger/rankings-command.js +6 -11
  68. package/dist/cli/messenger/reactions-command.js +2 -0
  69. package/dist/cli/messenger/recipients-query.d.ts +5 -0
  70. package/dist/cli/messenger/recipients-query.js +18 -0
  71. package/dist/cli/messenger/records-command.js +13 -8
  72. package/dist/cli/messenger/review.js +1 -1
  73. package/dist/cli/messenger/schedule-command.js +2 -2
  74. package/dist/cli/messenger/search-answers.js +2 -0
  75. package/dist/cli/messenger/search-command.js +3 -1
  76. package/dist/cli/messenger/searches-command.js +5 -1
  77. package/dist/cli/messenger/sends-query.d.ts +5 -0
  78. package/dist/cli/messenger/sends-query.js +33 -0
  79. package/dist/cli/messenger/serve-command.js +1 -0
  80. package/dist/cli/messenger/server-command.js +0 -3
  81. package/dist/cli/messenger/statistics-query.d.ts +14 -0
  82. package/dist/cli/messenger/statistics-query.js +143 -0
  83. package/dist/cli/messenger/stats-command.js +5 -5
  84. package/dist/cli/messenger/store-maintenance-command.js +10 -10
  85. package/dist/cli/messenger/stored.js +4 -1
  86. package/dist/cli/messenger/tags-command.js +7 -1
  87. package/dist/cli/messenger/tasks-command.js +57 -14
  88. package/dist/cli/messenger/transcribe-command.js +1 -0
  89. package/dist/cli/messenger/watch-answers.d.ts +6 -0
  90. package/dist/cli/messenger/watch-answers.js +69 -0
  91. package/dist/cli/messenger/watch-command.d.ts +1 -1
  92. package/dist/cli/messenger/watch-command.js +56 -7
  93. package/dist/cli/program.js +5 -1
  94. package/dist/cli/runs/command.js +25 -38
  95. package/dist/cli/runs/query.d.ts +5 -0
  96. package/dist/cli/runs/query.js +32 -0
  97. package/dist/cli/runs/search-command.d.ts +4 -0
  98. package/dist/cli/runs/search-command.js +27 -0
  99. package/dist/cli/runs/search-query.d.ts +4 -0
  100. package/dist/cli/runs/search-query.js +50 -0
  101. package/dist/cli/runs/search.d.ts +2 -1
  102. package/dist/cli/runs/search.js +6 -6
  103. package/dist/documents/index.d.ts +1 -0
  104. package/dist/documents/index.js +1 -0
  105. package/dist/domain/attachments.js +9 -1
  106. package/dist/domain/rankings-options.js +3 -3
  107. package/dist/entities/account-change.d.ts +89 -0
  108. package/dist/entities/account-change.js +20 -0
  109. package/dist/entities/account-profile.d.ts +7 -0
  110. package/dist/entities/account-profile.js +8 -0
  111. package/dist/entities/analysis-consent.d.ts +9 -0
  112. package/dist/entities/analysis-consent.js +4 -0
  113. package/dist/entities/attachment-results.d.ts +96 -0
  114. package/dist/entities/attachment-results.js +31 -0
  115. package/dist/entities/batch-result.d.ts +27 -0
  116. package/dist/entities/batch-result.js +18 -0
  117. package/dist/entities/chat-admin.d.ts +234 -0
  118. package/dist/entities/chat-admin.js +84 -0
  119. package/dist/entities/chat-change.d.ts +25 -0
  120. package/dist/entities/chat-change.js +11 -0
  121. package/dist/entities/chat-metadata.d.ts +34 -0
  122. package/dist/entities/chat-metadata.js +15 -0
  123. package/dist/entities/chat-moderation.d.ts +44 -0
  124. package/dist/entities/chat-moderation.js +37 -0
  125. package/dist/entities/chat-tracking.d.ts +26 -0
  126. package/dist/entities/chat-tracking.js +23 -0
  127. package/dist/entities/contact-activity.d.ts +52 -0
  128. package/dist/entities/contact-activity.js +42 -0
  129. package/dist/entities/contact-context.d.ts +1568 -0
  130. package/dist/entities/contact-context.js +191 -0
  131. package/dist/entities/contact-write.d.ts +22 -0
  132. package/dist/entities/contact-write.js +6 -0
  133. package/dist/entities/conversation-preparation.d.ts +63 -0
  134. package/dist/entities/conversation-preparation.js +23 -0
  135. package/dist/entities/conversation-results.d.ts +567 -0
  136. package/dist/entities/conversation-results.js +94 -0
  137. package/dist/entities/document-link.d.ts +13 -0
  138. package/dist/entities/document-link.js +11 -0
  139. package/dist/entities/extraction-report.d.ts +95 -0
  140. package/dist/entities/extraction-report.js +34 -0
  141. package/dist/entities/hit.d.ts +4 -0
  142. package/dist/entities/hit.js +4 -0
  143. package/dist/entities/image-embedding.d.ts +9 -0
  144. package/dist/entities/image-embedding.js +10 -0
  145. package/dist/entities/index.d.ts +8284 -1921
  146. package/dist/entities/index.js +178 -3
  147. package/dist/entities/listener.d.ts +2150 -0
  148. package/dist/entities/listener.js +46 -0
  149. package/dist/entities/media-archive.d.ts +43 -0
  150. package/dist/entities/media-archive.js +32 -0
  151. package/dist/entities/meeting-evidence.d.ts +86 -0
  152. package/dist/entities/meeting-evidence.js +25 -0
  153. package/dist/entities/meeting-preparation-parts.d.ts +14 -0
  154. package/dist/entities/meeting-preparation-parts.js +15 -13
  155. package/dist/entities/meeting-task-proposal.d.ts +14 -0
  156. package/dist/entities/meeting-task-proposal.js +19 -0
  157. package/dist/entities/meeting.d.ts +1 -1
  158. package/dist/entities/meeting.js +1 -1
  159. package/dist/entities/mention.d.ts +63 -0
  160. package/dist/entities/mention.js +44 -0
  161. package/dist/entities/message-change.d.ts +19 -0
  162. package/dist/entities/message-change.js +6 -0
  163. package/dist/entities/message-context.d.ts +617 -0
  164. package/dist/entities/message-context.js +20 -0
  165. package/dist/entities/message-read.d.ts +73 -0
  166. package/dist/entities/message-read.js +58 -0
  167. package/dist/entities/message-transfer.d.ts +379 -0
  168. package/dist/entities/message-transfer.js +83 -0
  169. package/dist/entities/message-write.d.ts +378 -0
  170. package/dist/entities/message-write.js +10 -0
  171. package/dist/entities/model-key.d.ts +14 -0
  172. package/dist/entities/model-key.js +10 -0
  173. package/dist/entities/poll.d.ts +40 -0
  174. package/dist/entities/poll.js +16 -0
  175. package/dist/entities/private-contact.d.ts +10 -0
  176. package/dist/entities/private-contact.js +4 -0
  177. package/dist/entities/reaction-change.d.ts +7 -0
  178. package/dist/entities/reaction-change.js +8 -0
  179. package/dist/entities/recipient.d.ts +29 -0
  180. package/dist/entities/recipient.js +11 -0
  181. package/dist/entities/recorded-run.d.ts +103 -0
  182. package/dist/entities/recorded-run.js +44 -0
  183. package/dist/entities/saved-search.d.ts +4 -0
  184. package/dist/entities/saved-search.js +2 -0
  185. package/dist/entities/send-attempt.d.ts +31 -0
  186. package/dist/entities/send-attempt.js +29 -0
  187. package/dist/entities/store-maintenance.d.ts +319 -0
  188. package/dist/entities/store-maintenance.js +145 -0
  189. package/dist/entities/store-records.d.ts +442 -0
  190. package/dist/entities/store-records.js +99 -0
  191. package/dist/entities/tag-change.d.ts +74 -0
  192. package/dist/entities/tag-change.js +31 -0
  193. package/dist/entities/task-pair.d.ts +31 -0
  194. package/dist/entities/task-pair.js +23 -0
  195. package/dist/entities/task-results.d.ts +524 -0
  196. package/dist/entities/task-results.js +142 -0
  197. package/dist/errors/reasons.js +3 -0
  198. package/dist/mcp/agent.d.ts +3 -1
  199. package/dist/mcp/agent.js +17 -10
  200. package/dist/mcp/bot/server.d.ts +2 -0
  201. package/dist/mcp/bot/server.js +1 -0
  202. package/dist/mcp/server.js +3 -1
  203. package/dist/mcp/tools/attachments.js +1 -0
  204. package/dist/meetings/cli/index.js +4 -2
  205. package/dist/meetings/cli/passage.js +1 -0
  206. package/dist/meetings/index.d.ts +2 -1
  207. package/dist/meetings/index.js +1 -0
  208. package/dist/meetings/links.d.ts +16 -0
  209. package/dist/meetings/read-answers.d.ts +185 -0
  210. package/dist/meetings/read-answers.js +213 -0
  211. package/dist/ports/messenger.d.ts +10 -1
  212. package/dist/runs/run.d.ts +10 -2
  213. package/dist/runs/run.js +11 -3
  214. package/dist/sends/guard.d.ts +1 -1
  215. package/dist/sends/guard.js +9 -5
  216. package/dist/sends/journal.d.ts +9 -2
  217. package/dist/sends/journal.js +9 -2
  218. package/dist/sends/permissions.d.ts +5 -1
  219. package/dist/sends/permissions.js +42 -2
  220. package/dist/sends/recipients.d.ts +8 -0
  221. package/dist/sends/recipients.js +9 -0
  222. package/dist/sends/upload.d.ts +4 -4
  223. package/dist/sends/upload.js +24 -20
  224. package/dist/services/admin-statistics.js +3 -3
  225. package/dist/services/chat-documents.d.ts +1 -0
  226. package/dist/services/chat-documents.js +9 -3
  227. package/dist/services/chats.d.ts +1 -1
  228. package/dist/services/chats.js +5 -2
  229. package/dist/services/documents/mirrors.d.ts +98 -0
  230. package/dist/services/documents/mirrors.js +144 -0
  231. package/dist/services/meeting-links.d.ts +2 -17
  232. package/dist/services/messages.js +1 -0
  233. package/dist/services/search-all-meetings.js +21 -8
  234. package/dist/services/task-rules.d.ts +1 -1
  235. package/dist/services/tasks.d.ts +2 -2
  236. package/dist/services/tasks.js +1 -1
  237. package/dist/store/exports.d.ts +1 -1
  238. package/dist/store/exports.js +1 -1
  239. package/dist/store/messages.d.ts +3 -1
  240. package/dist/store/messages.js +2 -2
  241. package/dist/store/roster.d.ts +4 -1
  242. package/dist/store/roster.js +2 -2
  243. package/dist/store/sqlite/chats.d.ts +12 -0
  244. package/dist/store/sqlite/chats.js +1 -0
  245. package/dist/store/sqlite/document-links.d.ts +1 -1
  246. package/dist/store/sqlite/document-links.js +5 -5
  247. package/dist/store/sqlite/drizzle/bun.js +3 -3
  248. package/dist/store/sqlite/drizzle/bun.js.map +1 -1
  249. package/dist/store/sqlite/drizzle/core.js +2 -2
  250. package/dist/store/sqlite/drizzle/core.js.map +1 -1
  251. package/dist/store/sqlite/drizzle/node.js +3 -3
  252. package/dist/store/sqlite/drizzle/node.js.map +1 -1
  253. package/dist/store/sqlite/drizzle/{shared-O7CTKP45.js → shared-HKXF6HKX.js} +57 -57
  254. package/dist/store/sqlite/drizzle/{shared-O7CTKP45.js.map → shared-HKXF6HKX.js.map} +1 -1
  255. package/dist/store/sqlite/involvements.js +2 -1
  256. package/dist/store/sqlite/roster.d.ts +2 -1
  257. package/dist/store/sqlite/roster.js +3 -2
  258. package/package.json +2 -2
  259. package/parity.json +14 -69
@@ -29,7 +29,7 @@ const line = ({ id, key, state, kind, group, createdAt, message, note }) => [
29
29
  : "(not in the store)",
30
30
  ].join(" ");
31
31
  const when = (at) => (at === null ? "—" : at.slice(0, 16).replace("T", " "));
32
- const toReview = (count) => count ? `; ${count} possible duplicate pair(s) went to tasks duplicates` : "";
32
+ const toReview = (count) => count ? `; ${count} possible duplicate pair(s) went to tasks duplicates list` : "";
33
33
  const suggestionLine = ({ as, by, certainty, reason }) => `close suggested: ${as} by ${by} at certainty ${certainty}${reason === null ? "" : ` — ${reason}`}; waits on the owner`;
34
34
  const card = (task) => [
35
35
  `${task.key} ${task.status} ${task.type} ${task.category ?? "—"} ${task.title}`,
@@ -44,7 +44,7 @@ const card = (task) => [
44
44
  ].join("\n");
45
45
  const applyText = (pass, yes, tool) => yes
46
46
  ? `${pass.chat}: ${pass.messages} messages read; ${pass.added} opened, ${pass.closed} closed` +
47
- (pass.review ? `; ${pass.review} may be the same job as another — ${tool} tasks duplicates` : "")
47
+ (pass.review ? `; ${pass.review} may be the same job as another — ${tool} tasks duplicates list` : "")
48
48
  : [
49
49
  `${pass.chat}: ${pass.messages} messages read; would open ${pass.tasks.length}, close ${pass.closed}`,
50
50
  ...pass.tasks.map((task) => ` ${task.kind.padEnd(8)} ${(task.category ?? "—").padEnd(13)} ${task.source}`),
@@ -152,6 +152,7 @@ export const tasksCommand = (messenger) => {
152
152
  context.renderer.note("no tasks — review, inbox and messages sync add them as questions arrive");
153
153
  });
154
154
  planOptions(destroysNothing(annotate(tasks.command("add"), {
155
+ output: { entity: "task_created" },
155
156
  effects: LOCAL_WRITE,
156
157
  examples: [`${tool} personal tasks add note:101 --type promise --category documents --title "Review draft"`],
157
158
  }))
@@ -185,6 +186,7 @@ export const tasksCommand = (messenger) => {
185
186
  context.renderer.note(`${task.key ?? task.id} follows up ${followUpOf.key} (${followUpOf.status}), which stays closed: linked follow-up-of`);
186
187
  });
187
188
  planOptions(destroysNothing(annotate(tasks.command("plan"), {
189
+ output: { entity: "task_card" },
188
190
  effects: LOCAL_WRITE,
189
191
  examples: [`${tool} personal tasks plan DOC-12 --title "Review draft"`],
190
192
  }))
@@ -201,6 +203,7 @@ export const tasksCommand = (messenger) => {
201
203
  renderAnswer(this, context, itemOf(snakeKeys(planned)), () => `${card(planned)}\n`);
202
204
  });
203
205
  destroysNothing(annotate(tasks.command("link"), {
206
+ output: { entity: "task_links" },
204
207
  effects: LOCAL_WRITE,
205
208
  examples: [`${tool} personal tasks link DOC-12 https://example.com/draft`],
206
209
  }))
@@ -221,9 +224,13 @@ export const tasksCommand = (messenger) => {
221
224
  ? `${linked.relations.map((one) => `${one.from} ${one.kind} ${one.to}`).join("\n")}\n`
222
225
  : `${card(linked)}\n`);
223
226
  if (!("relations" in linked) && linked.review && context.format === "pretty")
224
- context.renderer.note(`${linked.review} possible duplicate pair(s) went to tasks duplicates`);
227
+ context.renderer.note(`${linked.review} possible duplicate pair(s) went to tasks duplicates list`);
225
228
  });
226
- annotate(tasks.command("show"), { effects: LOCAL_READ, examples: [`${tool} personal tasks show DOC-12`] })
229
+ annotate(tasks.command("show"), {
230
+ output: { entity: "task_card" },
231
+ effects: LOCAL_READ,
232
+ examples: [`${tool} personal tasks show DOC-12`],
233
+ })
227
234
  .description("one task: its plan and everything linked to it")
228
235
  .argument("<task>", "the task's id or key, as tasks list shows it")
229
236
  .action(async function (id) {
@@ -231,7 +238,11 @@ export const tasksCommand = (messenger) => {
231
238
  const shown = await context.withServices((services) => services.tasks.show(id));
232
239
  renderAnswer(this, context, itemOf(snakeKeys(shown)), () => `${card(shown)}\n`);
233
240
  });
234
- annotate(tasks.command("related"), { effects: LOCAL_READ, examples: [`${tool} tasks related PR-12`] })
241
+ annotate(tasks.command("related"), {
242
+ output: { entity: "task_similar" },
243
+ effects: LOCAL_READ,
244
+ examples: [`${tool} tasks related PR-12`],
245
+ })
235
246
  .summary("tasks that may be the same job as this one, best first; merges nothing")
236
247
  .description("tasks that may be the same job as this one, best first, each with a band — likely, maybe, different — and the " +
237
248
  "reasons; merges nothing")
@@ -255,7 +266,11 @@ export const tasksCommand = (messenger) => {
255
266
  if (found.length === 0 && context.format === "pretty")
256
267
  context.renderer.note("no open or recent task looks alike");
257
268
  });
258
- const duplicates = withQuery(annotate(tasks.command("duplicates"), { effects: LOCAL_READ })
269
+ const duplicates = tasks.command("duplicates").description("pairs of tasks that may be one job");
270
+ withQuery(annotate(duplicates.command("list"), {
271
+ effects: LOCAL_READ,
272
+ examples: [`${tool} tasks duplicates list --limit 10`, `${tool} tasks duplicates list decision:unsure`],
273
+ })
259
274
  .summary("pairs of tasks a rule opened that may be one job, newest first")
260
275
  .description("pairs of tasks a rule opened that may be one job, newest first, with score and reasons: merge, reject or " +
261
276
  "defer each; decision:unsure lists the ones deferred to the owner"), TASK_PAIR_QUERY)
@@ -263,10 +278,10 @@ export const tasksCommand = (messenger) => {
263
278
  .action(async function (words) {
264
279
  const context = messengerContext(this, messenger);
265
280
  const { limit, offset } = pageOf(context.settings);
266
- const decision = queryTerms(words, TASK_PAIR_QUERY, "tasks duplicates").get("decision");
281
+ const decision = queryTerms(words, TASK_PAIR_QUERY, "tasks duplicates list").get("decision");
267
282
  const rows = await context.withServices((services) => services.tasks.duplicates({ decision, limit: limit + 1, offset }));
268
283
  const shown = rows.slice(0, limit);
269
- renderAnswer(this, context, pagedList("tasks duplicates", context.settings, {
284
+ renderAnswer(this, context, pagedList("tasks duplicates list", context.settings, {
270
285
  items: shown.map(snakeKeys),
271
286
  has_more: rows.length > limit,
272
287
  }), () => (shown.length === 0 ? "" : `${shown.map(pairLine).join("\n")}\n`));
@@ -280,6 +295,7 @@ export const tasksCommand = (messenger) => {
280
295
  renderAnswer(this, context, itemOf(snakeKeys(pair)), () => `${pair.decision} ${pairLine(pair)}\n`);
281
296
  };
282
297
  destroysNothing(annotate(duplicates.command("reject"), {
298
+ output: { entity: "task_pair" },
283
299
  effects: LOCAL_WRITE,
284
300
  examples: [`${tool} tasks duplicates reject PR-14 PR-12`],
285
301
  }))
@@ -289,6 +305,7 @@ export const tasksCommand = (messenger) => {
289
305
  .argument("<other-task>", "the task it was paired with")
290
306
  .action(decided("rejected", "tasks.duplicates.reject"));
291
307
  destroysNothing(annotate(duplicates.command("defer"), {
308
+ output: { entity: "task_pair" },
292
309
  effects: LOCAL_WRITE,
293
310
  examples: [`${tool} tasks duplicates defer PR-14 PR-12`],
294
311
  }))
@@ -297,7 +314,11 @@ export const tasksCommand = (messenger) => {
297
314
  .argument("<task>", "the task listed as the duplicate, by its id or key")
298
315
  .argument("<other-task>", "the task it was paired with")
299
316
  .action(decided("unsure", "tasks.duplicates.defer"));
300
- destroysNothing(annotate(tasks.command("merge"), { effects: LOCAL_WRITE, examples: [`${tool} tasks merge PR-14 --into PR-12`] }))
317
+ destroysNothing(annotate(tasks.command("merge"), {
318
+ output: { entity: "task_merge" },
319
+ effects: LOCAL_WRITE,
320
+ examples: [`${tool} tasks merge PR-14 --into PR-12`],
321
+ }))
301
322
  .summary("one job seen twice: fold the duplicate into the task that stays")
302
323
  .description("one job seen twice: link every message of the duplicate to the task and close the duplicate dismissed, " +
303
324
  "duplicate-of the task; nothing is deleted, and tasks unmerge undoes it")
@@ -311,7 +332,11 @@ export const tasksCommand = (messenger) => {
311
332
  renderAnswer(this, context, itemOf(snakeKeys(merged)), () => `${dup} merged into ${merged.task.key}; ${merged.linked} message(s) linked${toReview(merged.review)}\n` +
312
333
  `${card(merged.task)}\n`);
313
334
  });
314
- destroysNothing(annotate(tasks.command("unmerge"), { effects: LOCAL_WRITE, examples: [`${tool} tasks unmerge PR-14`] }))
335
+ destroysNothing(annotate(tasks.command("unmerge"), {
336
+ output: { entity: "task_unmerge" },
337
+ effects: LOCAL_WRITE,
338
+ examples: [`${tool} tasks unmerge PR-14`],
339
+ }))
315
340
  .summary("undo a merge: reopen the task and remove its duplicate-of relation")
316
341
  .description("undo a merge: reopen the task and remove its duplicate-of relation; messages the merge linked stay linked")
317
342
  .argument("<task>", "the merged task, by its id or key")
@@ -344,7 +369,11 @@ export const tasksCommand = (messenger) => {
344
369
  }));
345
370
  renderAnswer(this, context, itemOf(agendaItem(agenda)), () => `${agendaText(agenda)}\n`);
346
371
  });
347
- annotate(tasks.command("categories"), { effects: LOCAL_READ, examples: [`${tool} tasks categories`] })
372
+ annotate(tasks.command("categories"), {
373
+ output: { entity: "task_categories" },
374
+ effects: LOCAL_READ,
375
+ examples: [`${tool} tasks categories`],
376
+ })
348
377
  .summary("the categories a task takes, with what each means")
349
378
  .description("the categories a task takes, with what each means and the words a rule picks it by")
350
379
  .action(async function () {
@@ -365,6 +394,7 @@ export const tasksCommand = (messenger) => {
365
394
  ].join("\n"));
366
395
  });
367
396
  destroysNothing(annotate(tasks.command("scan"), {
397
+ output: { entity: "task_scan" },
368
398
  effects: LOCAL_WRITE,
369
399
  examples: [`${tool} tasks scan <chat>`, `${tool} tasks scan <chat> --last 300 --yes`],
370
400
  }))
@@ -389,7 +419,11 @@ export const tasksCommand = (messenger) => {
389
419
  }, { name: "tasks scan" });
390
420
  renderAnswer(this, context, listOf("tasks scan", { items: passes.map(snakeKeys), limit: passes.length, has_more: false }), () => `${passes.map((pass) => applyText(pass, yes, tool)).join("\n")}\n`);
391
421
  });
392
- destroysNothing(annotate(tasks.command("claim"), { effects: LOCAL_WRITE, examples: [`${tool} tasks claim PR-12 --lease 2h`] }))
422
+ destroysNothing(annotate(tasks.command("claim"), {
423
+ output: { entity: "task_card" },
424
+ effects: LOCAL_WRITE,
425
+ examples: [`${tool} tasks claim PR-12 --lease 2h`],
426
+ }))
393
427
  .description("take an open task so no other agent works it at once; the claim lapses by itself")
394
428
  .argument("<task>", "the task's id or key, as tasks list shows it")
395
429
  .option("--lease <duration>", "how long the claim lasts: 30m, 2h (default 30m); claiming again extends it", durationOf("--lease"))
@@ -401,7 +435,11 @@ export const tasksCommand = (messenger) => {
401
435
  const claimed = await context.withServices((services) => services.tasks.claim(id, holderOf(this), lease));
402
436
  renderAnswer(this, context, itemOf(snakeKeys(claimed)), () => `${card(claimed)}\n`);
403
437
  });
404
- destroysNothing(annotate(tasks.command("release"), { effects: LOCAL_WRITE, examples: [`${tool} tasks release PR-12`] }))
438
+ destroysNothing(annotate(tasks.command("release"), {
439
+ output: { entity: "task_card" },
440
+ effects: LOCAL_WRITE,
441
+ examples: [`${tool} tasks release PR-12`],
442
+ }))
405
443
  .description("give back a task you claimed, before the claim lapses; the owner may release any")
406
444
  .argument("<task>", "the task's id or key, as tasks list shows it")
407
445
  .option("--holder <name>", HOLDER_HELP)
@@ -412,6 +450,7 @@ export const tasksCommand = (messenger) => {
412
450
  renderAnswer(this, context, itemOf(snakeKeys(released)), () => `${card(released)}\n`);
413
451
  });
414
452
  destroysNothing(annotate(tasks.command("close"), {
453
+ output: { entity: "task_closed" },
415
454
  effects: LOCAL_WRITE,
416
455
  examples: [
417
456
  `${tool} tasks close Q-15 --as done --certainty 0.9 --reason answered-in-chat`,
@@ -452,7 +491,11 @@ export const tasksCommand = (messenger) => {
452
491
  context.renderer.note(`${closed.key ?? id} stays open: the close waits on the owner — ${tool} tasks close ${closed.key ?? id} ` +
453
492
  `confirms it, ${tool} tasks reject ${closed.key ?? id} refuses it`);
454
493
  });
455
- annotate(tasks.command("reject"), { effects: LOCAL_WRITE, examples: [`${tool} tasks reject Q-15`] })
494
+ annotate(tasks.command("reject"), {
495
+ output: { entity: "task_card" },
496
+ effects: LOCAL_WRITE,
497
+ examples: [`${tool} tasks reject Q-15`],
498
+ })
456
499
  .summary("refuse an agent's close of a task: it stays open and the suggestion goes")
457
500
  .description("refuse the close an agent suggested: the task stays open and leaves close:suggested; the agent may close it again")
458
501
  .argument("<task>", "the task's id or key, as tasks list shows it")
@@ -9,6 +9,7 @@ import { heardAnswer } from "./message-answers.js";
9
9
  import { keepRefreshedRefs } from "./refreshed-refs.js";
10
10
  /** `messages transcribe`: a voice message as text, by the messenger or by a model on this machine. */
11
11
  export const transcribeSubcommand = (messages, messenger) => annotate(messages.command("transcribe"), {
12
+ output: { entity: "message_transcript" },
12
13
  examples: [`${messenger.app.command} personal messages transcribe "Book club" 101`],
13
14
  effects: REMOTE_READ,
14
15
  })
@@ -0,0 +1,6 @@
1
+ import type * as v from "valibot";
2
+ import type { MessageEvent, MessageHit } from "../../domain/models.js";
3
+ import type { messageEvent, watchedMessage } from "../../entities/listener.js";
4
+ import type { AccountKey } from "../../store/store.js";
5
+ export declare const watchedMessageItem: (value: MessageHit, account: AccountKey) => v.InferOutput<typeof watchedMessage>;
6
+ export declare const watchEventItem: (value: MessageEvent, account: AccountKey) => v.InferOutput<typeof messageEvent>;
@@ -0,0 +1,69 @@
1
+ import { chatEventAnswer, memberAnswer, pollVoteAnswer } from "./chat-answers.js";
2
+ import { messageItem } from "./message-items.js";
3
+ const observed = (value) => ({
4
+ value: value.value,
5
+ observed_at: value.observedAt,
6
+ source: value.source,
7
+ ...(value.reactions === undefined ? {} : { reactions: value.reactions }),
8
+ });
9
+ export const watchedMessageItem = (value, account) => {
10
+ const counters = value.counterObservations;
11
+ return {
12
+ ...messageItem(value, account),
13
+ ...(value.starred === undefined ? {} : { starred: value.starred }),
14
+ ...(counters === undefined
15
+ ? {}
16
+ : {
17
+ counter_observations: {
18
+ ...(counters.views === undefined ? {} : { views: observed(counters.views) }),
19
+ ...(counters.reactions === undefined ? {} : { reactions: observed(counters.reactions) }),
20
+ ...(counters.comments === undefined ? {} : { comments: observed(counters.comments) }),
21
+ },
22
+ }),
23
+ };
24
+ };
25
+ const affected = (value) => ({
26
+ chat_id: value.chatId,
27
+ chat_title: value.chatTitle,
28
+ message_id: value.messageId,
29
+ });
30
+ export const watchEventItem = (value, account) => {
31
+ switch (value.event) {
32
+ case "message":
33
+ case "edit":
34
+ return { event: value.event, message: watchedMessageItem(value.message, account) };
35
+ case "delete":
36
+ return { event: value.event, chat_id: value.chatId, chat_title: value.chatTitle, message_id: value.messageId };
37
+ case "reaction":
38
+ return {
39
+ event: value.event,
40
+ ...affected(value),
41
+ reactions: value.reactions,
42
+ ...(value.counterObservation === undefined ? {} : { counter_observation: observed(value.counterObservation) }),
43
+ };
44
+ case "vote":
45
+ return { event: value.event, ...affected(value), vote: pollVoteAnswer(value.vote) };
46
+ case "person_reaction":
47
+ return {
48
+ event: value.event,
49
+ ...affected(value),
50
+ reaction: {
51
+ person: memberAnswer(value.reaction.person),
52
+ own: value.reaction.own,
53
+ reaction: value.reaction.reaction,
54
+ reacted_at: value.reaction.reactedAt,
55
+ },
56
+ };
57
+ case "star":
58
+ return { event: value.event, ...affected(value), starred: value.starred };
59
+ case "delivery":
60
+ return { event: value.event, ...affected(value), delivery: value.delivery, at: value.at };
61
+ case "chat_event":
62
+ return {
63
+ event: value.event,
64
+ chat_id: value.chatId,
65
+ chat_title: value.chatTitle,
66
+ change: chatEventAnswer(value.chatId, value.change),
67
+ };
68
+ }
69
+ };
@@ -4,7 +4,7 @@ import type { MessengerAdapter } from "../../ports/messenger.js";
4
4
  import { type Messenger, type MessengerContext } from "./context.js";
5
5
  /**
6
6
  * New messages as they arrive, until Ctrl-C or `--timeout` — both a normal end, exit 0: listening
7
- * for a minute is a complete answer. One `MessageHit` per line with `--jsonl`; `--json` is refused,
7
+ * for a minute is a complete answer. Canonical item records and one end record with `--jsonl`; `--json` is refused,
8
8
  * because a stream is not one value. Nothing is marked read.
9
9
  */
10
10
  export declare const watchCommand: (messenger: Messenger) => Command;
@@ -4,21 +4,25 @@ import { Command } from "commander";
4
4
  import { renderMessages } from "../../render/messages.js";
5
5
  import { environmentOf } from "../context.js";
6
6
  import { REMOTE_READ } from "../effects.js";
7
+ import { jsonlRecords, listOf } from "../envelope.js";
7
8
  import { withoutProviderRefs } from "../provider-refs.js";
8
9
  import { messengerContext } from "./context.js";
10
+ import { selfOf } from "./message-items.js";
11
+ import { watchEventItem, watchedMessageItem } from "./watch-answers.js";
9
12
  /**
10
13
  * New messages as they arrive, until Ctrl-C or `--timeout` — both a normal end, exit 0: listening
11
- * for a minute is a complete answer. One `MessageHit` per line with `--jsonl`; `--json` is refused,
14
+ * for a minute is a complete answer. Canonical item records and one end record with `--jsonl`; `--json` is refused,
12
15
  * because a stream is not one value. Nothing is marked read.
13
16
  */
14
17
  export const watchCommand = (messenger) => annotate(new Command("watch"), {
18
+ output: { entity: "watch_item" },
15
19
  effects: REMOTE_READ,
16
20
  lifetime: "persistent",
17
21
  examples: [`${messenger.app.command} personal watch --jsonl --timeout 30s`],
18
22
  })
19
23
  .summary("stream new messages until interrupted or timed out")
20
24
  .description("print new messages as they arrive, until Ctrl-C or --timeout (either ends it normally)")
21
- .option("--events", "also edits, deletions, reactions, poll votes and chat events; every line then names its event")
25
+ .option("--events", "include changes and receipts; each item names its event")
22
26
  .action(async function () {
23
27
  const events = this.opts().events === true;
24
28
  const context = messengerContext(this, messenger);
@@ -35,19 +39,59 @@ export const watchCommand = (messenger) => annotate(new Command("watch"), {
35
39
  provider: messenger.provider,
36
40
  locale: messenger.app.locale,
37
41
  })}`;
38
- // Without --events the stream is bare messages, as it always was: a reader of it never meets
39
- // a line of another shape.
42
+ // Event-tagged payloads are opt-in; ordinary items remain message values.
40
43
  const stop = new AbortController();
44
+ let connection;
45
+ let printed = 0;
46
+ const account = () => {
47
+ if (!connection)
48
+ throw new Error("watch has no connected account");
49
+ return selfOf(messenger, connection);
50
+ };
41
51
  const print = (event) => {
42
52
  if (stop.signal.aborted || (!events && event.event !== "message"))
43
53
  return;
44
54
  if (context.format === "jsonl") {
45
- context.streams.data(JSON.stringify(withoutProviderRefs(events ? event : event.message)));
55
+ const data = events
56
+ ? watchEventItem(event, account())
57
+ : event.event === "message"
58
+ ? watchedMessageItem(event.message, account())
59
+ : undefined;
60
+ if (data === undefined)
61
+ return;
62
+ context.streams.data(JSON.stringify({ type: "item", data: withoutProviderRefs(data) }));
63
+ printed++;
46
64
  }
47
65
  else
48
66
  context.streams.data(describe(event, render));
49
67
  };
50
- await listenUntilStopped(this, context, messenger, print, { stop, pipe: true });
68
+ const finish = (complete, error) => {
69
+ if (context.format !== "jsonl" || environmentOf(this).commandSignal?.aborted)
70
+ return;
71
+ const issues = !complete && error instanceof CliError ? [{ code: error.code, source: "watch" }] : undefined;
72
+ for (const record of jsonlRecords(listOf("watch", {
73
+ items: [],
74
+ limit: Math.max(1, printed),
75
+ has_more: false,
76
+ paging: "cursor",
77
+ meta: { complete, ...(issues ? { issues } : {}) },
78
+ })))
79
+ context.streams.data(JSON.stringify(record));
80
+ };
81
+ try {
82
+ await listenUntilStopped(this, context, messenger, print, {
83
+ stop,
84
+ pipe: true,
85
+ connected: (open) => {
86
+ connection = open;
87
+ },
88
+ });
89
+ }
90
+ catch (error) {
91
+ finish(false, error);
92
+ throw error;
93
+ }
94
+ finish(true);
51
95
  });
52
96
  /**
53
97
  * Listens until Ctrl-C, SIGTERM, `--timeout` or — with `pipe` — a reader that has gone away.
@@ -55,7 +99,11 @@ export const watchCommand = (messenger) => annotate(new Command("watch"), {
55
99
  * `feed` runs it beside `watch`, so what the messenger pushes reaches the store (`stored`).
56
100
  */
57
101
  export const listenUntilStopped = async (command, context, messenger, onEvent, { stop, pipe = false, catchUp = false, onReady = () => context.renderer.note("listening — Ctrl-C to stop"), connected, }) => {
58
- const given = environmentOf(command).signal;
102
+ const environment = environmentOf(command);
103
+ const phase = environment.untilStopped?.();
104
+ const given = phase
105
+ ? AbortSignal.any([phase, ...[environment.signal].filter((one) => one !== undefined)])
106
+ : environment.signal;
59
107
  const end = () => stop.abort();
60
108
  given?.addEventListener("abort", end, { once: true });
61
109
  const signals = given ? [] : ["SIGINT", "SIGTERM"];
@@ -96,6 +144,7 @@ export const listenUntilStopped = async (command, context, messenger, onEvent, {
96
144
  if (pipe && !given)
97
145
  process.stdout.off("error", closed);
98
146
  given?.removeEventListener("abort", end);
147
+ environment.endUntilStopped?.();
99
148
  }
100
149
  };
101
150
  const describe = (event, render) => {
@@ -11,7 +11,7 @@ import { failureContext } from "../errors/reasons.js";
11
11
  import { envName, readOnlyLockOf, readOnlyRefusal } from "../ports/app.js";
12
12
  import { recorded, wasSettled } from "../runs/recording.js";
13
13
  import { processRun, useAsProcessRun } from "../runs/scope.js";
14
- import { keyForCommand } from "../sends/permissions.js";
14
+ import { AUTHORITY_KEY, keyForCommand } from "../sends/permissions.js";
15
15
  import { logParsedArgv, logVersion } from "./argv-log.js";
16
16
  import { commandPathOf } from "./command-contract.js";
17
17
  import { outputFor, provide } from "./context.js";
@@ -254,6 +254,10 @@ const runOnce = async (argv, definition, options) => {
254
254
  if (lock && changesAuthorityNow(action) && !flags.dryRun)
255
255
  throw new CliError("permission_error", `this process is read-only (${lock}) — \`${command} ${path.join(" ")}\` changes what a later run may do; ` +
256
256
  `run it without ${lock}`);
257
+ if (options.agent && options.agent.authority !== true && changesAuthorityNow(action))
258
+ throw new CliError("permission_error", `\`${command} ${path.join(" ")}\` changes what a later run may do, and this profile keeps that from agents ` +
259
+ `(${AUTHORITY_KEY}) — the owner turns it on in a terminal: ` +
260
+ `\`${command} <profile> config set${path[0] === "bot" ? " --bot" : ""} permissions.${AUTHORITY_KEY} allow\``, { permission: AUTHORITY_KEY, sent: false });
257
261
  const { lifetime, cooperative: stopsAtItsPace } = metaOf(action);
258
262
  const persistent = lifetime === "persistent";
259
263
  listens = persistent;
@@ -1,13 +1,14 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { annotate } from "@wirecat/cli-base/commands";
3
- import { Command, Option } from "commander";
3
+ import { Command } from "commander";
4
4
  import { findRun, listRuns, readEvents, runsDirFor } from "../../runs/run.js";
5
5
  import { environmentOf, outputFor } from "../context.js";
6
6
  import { LOCAL_READ, NO_EFFECTS } from "../effects.js";
7
7
  import { itemOf, listOf, renderAnswer, snakeKeys } from "../envelope.js";
8
+ import { typedQuery, withQuery } from "../list-query.js";
8
9
  import { positiveCount } from "../paging.js";
9
10
  import { lineOf } from "../steps.js";
10
- import { runsFound, searchRuns } from "./search.js";
11
+ import { RUNS_QUERY, runListFilter } from "./query.js";
11
12
  /** Reading the log is not itself worth recording, so **nothing here starts a run**. */
12
13
  export const runsCommand = (app) => {
13
14
  const command = new Command("runs").description("recorded runs — what this tool did, and when");
@@ -18,24 +19,35 @@ export const runsCommand = (app) => {
18
19
  throw new CliError("not_found", `no recorded run "${id}" — \`${lineOf(from, "runs list")}\` shows them`);
19
20
  return found;
20
21
  };
21
- annotate(command.command("list"), {
22
- effects: LOCAL_READ,
22
+ withQuery(annotate(command.command("list"), {
23
23
  examples: [`${app.command} runs list --limit 10`],
24
- })
24
+ effects: LOCAL_READ,
25
+ }), RUNS_QUERY)
25
26
  .description("recorded runs, newest first")
26
27
  .option("--limit <n>", "how many to show", positiveCount("--limit"), 20)
27
28
  .option("--page <n>", "which page, starting at 1", positiveCount("--page"), 1)
28
- .action(function () {
29
+ .action(function (words) {
30
+ const filter = runListFilter(words);
29
31
  const { limit, page } = this.opts();
30
32
  const output = outputFor(this);
31
33
  const { renderer } = output;
32
- const runs = listRuns(dir(this), { atLeast: page * limit + 1 });
34
+ const runs = listRuns(dir(this), { atLeast: page * limit + 1, filter });
33
35
  const shown = runs.slice((page - 1) * limit, page * limit);
34
- renderAnswer(this, output, listOf("runs list", { items: shown.map(snakeKeys), limit, page, has_more: runs.length > page * limit }));
36
+ renderAnswer(this, output, listOf("runs list", {
37
+ items: shown.map(snakeKeys),
38
+ limit,
39
+ page,
40
+ has_more: runs.length > page * limit,
41
+ args: words.length ? { query: typedQuery(words) } : {},
42
+ }));
35
43
  if (runs.length === 0)
36
44
  renderer.note("nothing recorded — a run is kept with `--record`, or when it fails");
37
45
  });
38
- annotate(command.command("show"), { effects: LOCAL_READ, examples: [`${app.command} runs show run-101`] })
46
+ annotate(command.command("show"), {
47
+ examples: [`${app.command} runs show run-101`],
48
+ effects: LOCAL_READ,
49
+ output: { entity: "run", view: "details" },
50
+ })
39
51
  .argument("<run-id>", `an id from \`${app.command} runs list\``)
40
52
  .description("one run: what it was, how it ended, and every event it logged")
41
53
  .option("--problems", "only failed responses, warnings and crashes")
@@ -56,36 +68,11 @@ export const runsCommand = (app) => {
56
68
  if (shown.length < events.length)
57
69
  renderer.note(`${events.length - shown.length} other events — \`${lineOf(this, `runs show ${id}`)}\` shows them all`);
58
70
  });
59
- annotate(command.command("search"), {
60
- effects: LOCAL_READ,
61
- examples: [`${app.command} runs search timeout --limit 10`],
71
+ annotate(command.command("path"), {
72
+ examples: [`${app.command} runs path run-101`],
73
+ effects: NO_EFFECTS,
74
+ output: { entity: "run_location" },
62
75
  })
63
- .description("search recorded diagnostic metadata and events; no message contents")
64
- .argument("[query]", "case-insensitive literal diagnostic text")
65
- .addOption(new Option("--status <status>", "run outcome").choices(["success", "failed", "partial", "running"]))
66
- .option("--error-code <code>", "one stable error code")
67
- .option("--operation <name>", "one operation, such as messages.download")
68
- .option("--profile <name>", "one recorded profile")
69
- .option("--since-time <time>", "runs starting on or after an ISO time or 2h / 1d ago")
70
- .option("--limit <n>", "matches per page, at most 100", positiveCount("--limit"), 20)
71
- .option("--page <n>", "result page", positiveCount("--page"), 1)
72
- .action(function (query) {
73
- const options = this.opts();
74
- const found = searchRuns(dir(this), {
75
- ...options,
76
- since: options.sinceTime,
77
- ...(query === undefined ? {} : { query }),
78
- });
79
- renderAnswer(this, outputFor(this), runsFound(found, {
80
- query,
81
- status: options.status,
82
- error_code: options.errorCode,
83
- operation: options.operation,
84
- profile: options.profile,
85
- since_time: options.sinceTime,
86
- }));
87
- });
88
- annotate(command.command("path"), { effects: NO_EFFECTS, examples: [`${app.command} runs path run-101`] })
89
76
  .argument("<run-id>", `an id from \`${app.command} runs list\``)
90
77
  .description("the directory holding one run")
91
78
  .action(function (id) {
@@ -0,0 +1,5 @@
1
+ import { type QueryRegistry } from "@wirecat/cli-base/query";
2
+ import type { RunListFilter } from "../../runs/run.js";
3
+ import { type QueryWords } from "../list-query.js";
4
+ export declare const RUNS_QUERY: QueryRegistry;
5
+ export declare const runListFilter: (words: QueryWords) => RunListFilter;
@@ -0,0 +1,32 @@
1
+ import { queryFieldsOf } from "@wirecat/cli-base/query";
2
+ import { recordedRun } from "../../entities/index.js";
3
+ import { queryTerms } from "../list-query.js";
4
+ export const RUNS_QUERY = {
5
+ entity: "run",
6
+ backend: "native",
7
+ fields: queryFieldsOf(recordedRun, {
8
+ native: ["run_id", "command", "profile", "status", "error_code"],
9
+ examples: {
10
+ command: 'command:"messages send"',
11
+ profile: "profile:work",
12
+ status: "status:failed",
13
+ error_code: "error_code:timeout",
14
+ },
15
+ }).map((field) => field.native
16
+ ? {
17
+ ...field,
18
+ operators: field.operators.filter((operator) => operator === "term" || operator === "phrase"),
19
+ }
20
+ : field),
21
+ };
22
+ export const runListFilter = (words) => {
23
+ const terms = queryTerms(words, RUNS_QUERY, "runs list");
24
+ const status = recordedRun.entries.status.options.find((value) => value === terms.get("status"));
25
+ return {
26
+ ...(terms.has("run_id") ? { runId: terms.get("run_id") } : {}),
27
+ ...(terms.has("command") ? { command: terms.get("command") } : {}),
28
+ ...(terms.has("profile") ? { profile: terms.get("profile") } : {}),
29
+ ...(status === undefined ? {} : { status }),
30
+ ...(terms.has("error_code") ? { errorCode: terms.get("error_code") } : {}),
31
+ };
32
+ };
@@ -0,0 +1,4 @@
1
+ import { Command } from "commander";
2
+ import type { AppIdentity } from "../../ports/app.js";
3
+ /** Diagnostic files are independent of linked accounts and never start a recorded run themselves. */
4
+ export declare const runsSearchCommand: (app: AppIdentity) => Command;
@@ -0,0 +1,27 @@
1
+ import { annotate } from "@wirecat/cli-base/commands";
2
+ import { Command } from "commander";
3
+ import { runsDirFor } from "../../runs/run.js";
4
+ import { environmentOf, outputFor } from "../context.js";
5
+ import { LOCAL_READ } from "../effects.js";
6
+ import { renderAnswer } from "../envelope.js";
7
+ import { typedQuery, withQuery } from "../list-query.js";
8
+ import { positiveCount } from "../paging.js";
9
+ import { runsFound, searchRuns } from "./search.js";
10
+ import { RUN_SEARCH_QUERY, runSearchFilter } from "./search-query.js";
11
+ /** Diagnostic files are independent of linked accounts and never start a recorded run themselves. */
12
+ export const runsSearchCommand = (app) => annotate(withQuery(new Command("runs")
13
+ .summary("search recorded diagnostic metadata and events")
14
+ .description("search recorded diagnostic metadata and events; literal words and field:value terms joined by AND; no message contents")
15
+ .option("--since-time <time>", "runs starting on or after an ISO time or 2h / 1d ago")
16
+ .option("--limit <n>", "matches per page, at most 100", positiveCount("--limit"), 20)
17
+ .option("--page <n>", "result page", positiveCount("--page"), 1), RUN_SEARCH_QUERY), {
18
+ effects: LOCAL_READ,
19
+ output: { entity: "run", view: "hit" },
20
+ examples: [`${app.command} search runs timeout status:failed --limit 10`],
21
+ }).action(function (words) {
22
+ const filter = runSearchFilter(words);
23
+ const options = this.opts();
24
+ const dir = runsDirFor(app, environmentOf(this).env ?? process.env);
25
+ const found = searchRuns(dir, { ...filter, ...options, since: options.sinceTime });
26
+ renderAnswer(this, outputFor(this), runsFound(found, { query: typedQuery(words), since_time: options.sinceTime }));
27
+ });
@@ -0,0 +1,4 @@
1
+ import { type QueryWords } from "../list-query.js";
2
+ import type { RunSearch } from "./search.js";
3
+ export declare const RUN_SEARCH_QUERY: import("@wirecat/cli-base/query").QueryRegistry;
4
+ export declare const runSearchFilter: (words: QueryWords) => Pick<RunSearch, "terms" | "status" | "errorCode" | "operation" | "profile">;