@skrr-ai/cli 0.1.37 → 0.1.38

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 (174) hide show
  1. package/bin/dev-fallback.js +167 -4
  2. package/dist/base-command.js +46 -2
  3. package/dist/commands/agents/actions/create.js +18 -7
  4. package/dist/commands/agents/actions/delete.d.ts +1 -0
  5. package/dist/commands/agents/actions/delete.js +7 -0
  6. package/dist/commands/agents/actions/update.js +5 -3
  7. package/dist/commands/agents/api-actions/add.js +5 -2
  8. package/dist/commands/agents/api-actions/index.d.ts +11 -0
  9. package/dist/commands/agents/api-actions/index.js +27 -1
  10. package/dist/commands/agents/avatar/default/index.d.ts +11 -0
  11. package/dist/commands/agents/avatar/default/index.js +17 -0
  12. package/dist/commands/agents/avatar/default/set.d.ts +2 -1
  13. package/dist/commands/agents/avatar/default/set.js +32 -7
  14. package/dist/commands/agents/avatar/upload.js +7 -1
  15. package/dist/commands/agents/categories/delete.js +8 -0
  16. package/dist/commands/agents/categories/index.d.ts +21 -5
  17. package/dist/commands/agents/categories/index.js +68 -15
  18. package/dist/commands/agents/chat.d.ts +21 -0
  19. package/dist/commands/agents/chat.js +94 -7
  20. package/dist/commands/agents/create.js +11 -4
  21. package/dist/commands/agents/list.d.ts +15 -0
  22. package/dist/commands/agents/list.js +31 -1
  23. package/dist/commands/agents/scene-background/upload.d.ts +30 -0
  24. package/dist/commands/agents/scene-background/upload.js +97 -0
  25. package/dist/commands/agents/show.js +13 -0
  26. package/dist/commands/agents/tools/calls.d.ts +23 -5
  27. package/dist/commands/agents/tools/calls.js +81 -18
  28. package/dist/commands/agents/update.js +15 -5
  29. package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
  30. package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
  31. package/dist/commands/convos/messages.d.ts +13 -0
  32. package/dist/commands/convos/messages.js +40 -16
  33. package/dist/commands/daemon/index.js +8 -1
  34. package/dist/commands/daemon/logs.d.ts +26 -0
  35. package/dist/commands/daemon/logs.js +56 -0
  36. package/dist/commands/daemon/uninstall.js +1 -1
  37. package/dist/commands/followups/act.js +3 -2
  38. package/dist/commands/followups/cancel.d.ts +6 -3
  39. package/dist/commands/followups/cancel.js +16 -6
  40. package/dist/commands/followups/observability.d.ts +23 -0
  41. package/dist/commands/followups/observability.js +114 -0
  42. package/dist/commands/followups/reschedule.js +1 -1
  43. package/dist/commands/followups/resolve.js +3 -3
  44. package/dist/commands/followups/show.js +2 -1
  45. package/dist/commands/followups/watch.js +9 -5
  46. package/dist/commands/harnesses/forget.js +28 -0
  47. package/dist/commands/harnesses/install.d.ts +41 -0
  48. package/dist/commands/harnesses/install.js +165 -0
  49. package/dist/commands/harnesses/installers.d.ts +45 -0
  50. package/dist/commands/harnesses/installers.js +155 -0
  51. package/dist/commands/harnesses/list.d.ts +82 -1
  52. package/dist/commands/harnesses/list.js +104 -12
  53. package/dist/commands/harnesses/models.js +10 -1
  54. package/dist/commands/harnesses/show.d.ts +15 -0
  55. package/dist/commands/harnesses/show.js +44 -3
  56. package/dist/commands/harnesses/update.js +1 -1
  57. package/dist/commands/harnesses/usage.d.ts +25 -0
  58. package/dist/commands/harnesses/usage.js +24 -2
  59. package/dist/commands/instructions/status.js +13 -1
  60. package/dist/commands/machines/dedicated/catalog.js +4 -15
  61. package/dist/commands/machines/dedicated/create.d.ts +8 -0
  62. package/dist/commands/machines/dedicated/create.js +58 -1
  63. package/dist/commands/machines/dedicated/destroy.js +18 -4
  64. package/dist/commands/machines/dedicated/list.js +9 -6
  65. package/dist/commands/machines/hosted/list.js +12 -0
  66. package/dist/commands/machines/list.js +11 -0
  67. package/dist/commands/machines/show.d.ts +1 -0
  68. package/dist/commands/machines/show.js +40 -11
  69. package/dist/commands/profiles/list.d.ts +23 -0
  70. package/dist/commands/profiles/list.js +96 -0
  71. package/dist/commands/search/skills.d.ts +11 -1
  72. package/dist/commands/search/skills.js +26 -8
  73. package/dist/commands/skills/details.d.ts +2 -0
  74. package/dist/commands/skills/details.js +24 -1
  75. package/dist/commands/skills/import-as-actions.d.ts +32 -0
  76. package/dist/commands/skills/import-as-actions.js +175 -0
  77. package/dist/commands/skills/install.d.ts +2 -0
  78. package/dist/commands/skills/install.js +24 -1
  79. package/dist/commands/skills/invocable.js +6 -2
  80. package/dist/commands/skills/local.d.ts +40 -0
  81. package/dist/commands/skills/local.js +160 -0
  82. package/dist/commands/skills/search.d.ts +11 -1
  83. package/dist/commands/skills/search.js +29 -8
  84. package/dist/commands/spaces/actions/add.js +23 -5
  85. package/dist/commands/spaces/actions/available.js +3 -0
  86. package/dist/commands/spaces/actions/list.d.ts +5 -0
  87. package/dist/commands/spaces/actions/list.js +49 -3
  88. package/dist/commands/spaces/actions/run.d.ts +28 -0
  89. package/dist/commands/spaces/actions/run.js +146 -0
  90. package/dist/commands/spaces/actions/update.js +27 -5
  91. package/dist/commands/spaces/label-summaries.d.ts +27 -0
  92. package/dist/commands/spaces/label-summaries.js +64 -0
  93. package/dist/commands/spaces/task-rollups.d.ts +5 -0
  94. package/dist/commands/spaces/task-rollups.js +15 -4
  95. package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +63 -3
  96. package/dist/commands/tasks/actions/attach.js +32 -7
  97. package/dist/commands/tasks/complete.js +13 -0
  98. package/dist/commands/tasks/create.js +1 -1
  99. package/dist/commands/tasks/events/append.js +29 -6
  100. package/dist/commands/tasks/expectations/assess.d.ts +1 -0
  101. package/dist/commands/tasks/expectations/assess.js +43 -5
  102. package/dist/commands/tasks/expectations.js +77 -16
  103. package/dist/commands/tasks/follow-up.js +2 -2
  104. package/dist/commands/tasks/lifecycle/definitions.js +21 -2
  105. package/dist/commands/tasks/lifecycle/detach.d.ts +7 -0
  106. package/dist/commands/tasks/lifecycle/detach.js +43 -3
  107. package/dist/commands/tasks/lifecycle/migrate.d.ts +2 -0
  108. package/dist/commands/tasks/lifecycle/migrate.js +42 -3
  109. package/dist/commands/tasks/lifecycle/reopen.d.ts +2 -0
  110. package/dist/commands/tasks/lifecycle/reopen.js +40 -3
  111. package/dist/commands/tasks/lifecycle/transition.js +33 -2
  112. package/dist/commands/tasks/list.js +16 -9
  113. package/dist/commands/tasks/move.js +11 -13
  114. package/dist/commands/tasks/review-queue.d.ts +15 -0
  115. package/dist/commands/tasks/review-queue.js +180 -31
  116. package/dist/commands/tasks/show.d.ts +11 -0
  117. package/dist/commands/tasks/show.js +105 -12
  118. package/dist/commands/tasks/trust.js +7 -2
  119. package/dist/commands/tasks/update.js +46 -10
  120. package/dist/commands/tasks/updates/add.js +1 -1
  121. package/dist/commands/token/list.d.ts +22 -0
  122. package/dist/commands/token/list.js +68 -0
  123. package/dist/commands/token/revoke.d.ts +23 -0
  124. package/dist/commands/token/revoke.js +53 -0
  125. package/dist/lib/agent-actions.d.ts +57 -2
  126. package/dist/lib/agent-actions.js +113 -21
  127. package/dist/lib/agent-pin-mute.d.ts +10 -5
  128. package/dist/lib/agent-pin-mute.js +17 -4
  129. package/dist/lib/agent-visual-refs.d.ts +27 -0
  130. package/dist/lib/agent-visual-refs.js +127 -2
  131. package/dist/lib/agentic-stream.d.ts +6 -0
  132. package/dist/lib/agentic-stream.js +34 -3
  133. package/dist/lib/assignee-resolver.d.ts +1 -1
  134. package/dist/lib/assignee-resolver.js +8 -0
  135. package/dist/lib/bulk-task-targets.d.ts +12 -0
  136. package/dist/lib/bulk-task-targets.js +34 -1
  137. package/dist/lib/cli-installers.d.ts +130 -0
  138. package/dist/lib/cli-installers.js +145 -0
  139. package/dist/lib/command-miss.js +55 -7
  140. package/dist/lib/dedicated-lease-command.d.ts +13 -0
  141. package/dist/lib/dedicated-lease-command.js +22 -1
  142. package/dist/lib/dedicated-machines.d.ts +95 -1
  143. package/dist/lib/dedicated-machines.js +236 -2
  144. package/dist/lib/failed-lookup.d.ts +3 -0
  145. package/dist/lib/failed-lookup.js +40 -0
  146. package/dist/lib/first-party-harness-agent.js +9 -2
  147. package/dist/lib/first-party-harness-doctor.d.ts +15 -0
  148. package/dist/lib/first-party-harness-doctor.js +126 -27
  149. package/dist/lib/followups.d.ts +14 -6
  150. package/dist/lib/followups.js +31 -6
  151. package/dist/lib/harnesses.d.ts +8 -0
  152. package/dist/lib/instruction-input.d.ts +19 -1
  153. package/dist/lib/instruction-input.js +52 -1
  154. package/dist/lib/label-scope.d.ts +15 -0
  155. package/dist/lib/label-scope.js +25 -5
  156. package/dist/lib/local-skills.d.ts +98 -0
  157. package/dist/lib/local-skills.js +195 -0
  158. package/dist/lib/machines.d.ts +10 -10
  159. package/dist/lib/machines.js +23 -1
  160. package/dist/lib/search-query.d.ts +19 -0
  161. package/dist/lib/search-query.js +35 -0
  162. package/dist/lib/space-custom-actions.d.ts +72 -0
  163. package/dist/lib/space-custom-actions.js +117 -0
  164. package/dist/lib/space-resolver.d.ts +1 -21
  165. package/dist/lib/space-resolver.js +19 -10
  166. package/dist/lib/task-closure.d.ts +3 -2
  167. package/dist/lib/task-closure.js +18 -20
  168. package/dist/lib/task-lifecycle-output.js +117 -9
  169. package/dist/lib/task-ref-resolver.d.ts +1 -17
  170. package/dist/lib/task-ref-resolver.js +18 -8
  171. package/dist/lib/tasks.js +9 -3
  172. package/dist/node_modules/@skrr-ai/data-provider/index.js +5076 -4947
  173. package/oclif.manifest.json +26635 -25649
  174. package/package.json +9 -3
@@ -1,14 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.taskPointerLines = taskPointerLines;
3
4
  exports.taskFailureLines = taskFailureLines;
4
5
  const core_1 = require("@oclif/core");
5
6
  const data_provider_1 = require("@skrr-ai/data-provider");
6
7
  const api_fetch_1 = require("../../lib/api-fetch");
7
8
  const session_task_endpoints_1 = require("../../lib/session-task-endpoints");
8
9
  const base_command_1 = require("../../base-command");
10
+ const task_closure_1 = require("../../lib/task-closure");
9
11
  const task_resolver_1 = require("../../lib/task-resolver");
10
12
  const web_url_1 = require("../../lib/web-url");
11
13
  const html_text_1 = require("../../lib/html-text");
14
+ /**
15
+ * Where to look next — as COMMANDS, not as facts about the task.
16
+ *
17
+ * These used to print as `What the agent produced: skrr tasks output <ref>`,
18
+ * in the same `Label: value` grammar as every line above them, so the line read
19
+ * as a statement of what the agent had produced. It also printed on tasks no
20
+ * agent had ever run, asserting output that did not exist (OSK-9669). They are
21
+ * now a visibly separate list, and only once a run, a conversation or a Result
22
+ * exists for them to show.
23
+ */
24
+ function taskPointerLines(t, ref, bin = 'skrr') {
25
+ const hasSomethingToShow = Boolean(t.conversationId || t.lastFinalizedRunId || t.checkoutRunId || t.currentResultId);
26
+ if (!hasSomethingToShow)
27
+ return [];
28
+ const pointers = [
29
+ [`${bin} tasks output ${ref}`, 'what the agent produced'],
30
+ ];
31
+ if (t.conversationId) {
32
+ pointers.push([`${bin} tasks transcript ${ref}`, 'the full agent transcript']);
33
+ }
34
+ const width = Math.max(...pointers.map(([command]) => command.length));
35
+ return [
36
+ '',
37
+ 'See also:',
38
+ ...pointers.map(([command, what]) => ` ${command.padEnd(width)} ${what}`),
39
+ ];
40
+ }
12
41
  /**
13
42
  * The failure rows of `tasks show`, when there are any. (S3 dogfood finding)
14
43
  *
@@ -179,13 +208,80 @@ class TasksShow extends base_command_1.BaseCommand {
179
208
  * it stood on at close. Show it so a bare "done" never reads as "clean."
180
209
  */
181
210
  const resolution = t.resolution;
182
- if (resolution?.closure && resolution.closure.closable === false) {
183
- const parts = [];
184
- const unmet = resolution.closure.expectations?.unmet?.length ?? 0;
185
- if (unmet > 0)
186
- parts.push(`${unmet} Expectation${unmet === 1 ? '' : 's'} still unmet`);
187
- parts.push(...(resolution.closure.blockers || []).filter((c) => c !== 'TASK_EXPECTATIONS_NOT_MET'));
188
- this.log(`Resolution: ${resolution.kind ?? '-'} — closed over ${parts.join('; ') || 'outstanding items'}`);
211
+ /*
212
+ * Same renderer as the `tasks move` receipt, so the two cannot disagree
213
+ * about a Task they are both describing.
214
+ *
215
+ * This copy showed an owner's ordinary close as an override:
216
+ * `TASK_RESULT_REVIEW_REQUIRED` lands in `blockers` whenever the policy is
217
+ * unsatisfied — including when the owner, who is not bound by the Review
218
+ * they declared, closes it themselves — and this rendered it identically to
219
+ * a genuine forced close. So the only read surface for "was this gate
220
+ * overridden?" could not answer the question.
221
+ */
222
+ if (resolution?.closure) {
223
+ const sentences = (0, task_closure_1.closureSnapshotLines)(resolution.closure);
224
+ if (sentences.length > 0) {
225
+ this.log(`Resolution: ${resolution.kind ?? '-'} — ${sentences.join(' ')}`);
226
+ }
227
+ }
228
+ /*
229
+ * A Result orphaned by a spec edit is not the same as no Result.
230
+ *
231
+ * Editing a `description` or an Expectation advances `specVersion`, which
232
+ * unsets `currentResultId` by design — the old answer replied to a question
233
+ * that has changed. But every CLI surface then reported the Task as though
234
+ * nothing had ever been submitted, and the prior Result is still there. The
235
+ * web already says this correctly ("No Result answers the current Task spec
236
+ * vN", plus the previous one); the CLI said nothing at all.
237
+ *
238
+ * The extra request is paid only in the ambiguous case — when there is no
239
+ * current Result — and `tasks show` is a detail view, so one call to
240
+ * distinguish "nobody answered" from "the answer is out of date" is worth
241
+ * it. A failed lookup degrades to silence rather than to a claim.
242
+ */
243
+ const taskSpecVersion = Number(t.specVersion) || 1;
244
+ if (t.currentResultId) {
245
+ this.log(`Result: ${String(t.currentResultId)} (answers spec v${taskSpecVersion})`);
246
+ /*
247
+ * The Review on that Result, with its REASON.
248
+ *
249
+ * Closing over a `changes_requested` Review is allowed and recorded, and
250
+ * the closure line says "a reviewer requested changes" — the verdict. What
251
+ * they asked for lived only on the Review row, which no text surface
252
+ * printed, so the objection survived as a fact and lost its content. One
253
+ * read, on a detail view, only when there is a Result to have been
254
+ * reviewed; a failed read says nothing rather than implying no Review.
255
+ */
256
+ try {
257
+ const history = await data_provider_1.dataService.listTaskResults(taskIdentity.id);
258
+ const review = (history?.reviews || []).find((row) => row.resultId === String(t.currentResultId));
259
+ if (review) {
260
+ const who = (0, task_closure_1.describeAssessor)(review.reviewedBy?.kind);
261
+ const when = review.reviewedAt ? ` on ${String(review.reviewedAt).slice(0, 10)}` : '';
262
+ const why = review.rationale ? `: ${review.rationale}` : '';
263
+ this.log(`Review: ${review.decision} by ${who}${when}${why}`);
264
+ }
265
+ }
266
+ catch {
267
+ // Silence, not "no Review": this read failing is not evidence of absence.
268
+ }
269
+ }
270
+ else {
271
+ let priorLine = 'none submitted';
272
+ try {
273
+ const history = await data_provider_1.dataService.listTaskResults(taskIdentity.id);
274
+ const prior = history?.results?.[0];
275
+ if (prior) {
276
+ priorLine =
277
+ `none answers spec v${taskSpecVersion} — previous v${prior.version} ` +
278
+ `answered spec v${prior.specVersion ?? '?'}`;
279
+ }
280
+ }
281
+ catch {
282
+ priorLine = 'none answers the current spec (earlier Results could not be read)';
283
+ }
284
+ this.log(`Result: ${priorLine}`);
189
285
  }
190
286
  this.log(`Executor: ${t.executionMode ?? 'legacy / inferred'}`);
191
287
  this.log(`Priority: ${t.priority ?? '-'}`);
@@ -333,11 +429,8 @@ class TasksShow extends base_command_1.BaseCommand {
333
429
  // Silent: a Task this caller can VIEW but whose acceptance they cannot
334
430
  // read is a permissions shape, not something to shout about mid-page.
335
431
  }
336
- this.log('');
337
- this.log(`What the agent produced: skrr tasks output ${ref}`);
338
- if (t.conversationId) {
339
- this.log(`Full agent transcript: skrr tasks transcript ${ref}`);
340
- }
432
+ for (const line of taskPointerLines(t, ref, this.config.bin))
433
+ this.log(line);
341
434
  }
342
435
  }
343
436
  exports.default = TasksShow;
@@ -41,8 +41,13 @@ class TasksTrust extends base_command_1.BaseCommand {
41
41
  level: core_1.Flags.string({
42
42
  options: [...data_provider_1.DELIVERABLE_AUTHORITIES],
43
43
  required: true,
44
- description: 'How well evidenced the answer must be: self_reported (the doer says so), ' +
45
- 'human_confirmed (someone else looked), observation_backed (the platform saw it).',
44
+ description:
45
+ // `observation_backed` is a RETIRED rung name for this domain — the flag
46
+ // rejects it, so the only two values the help explained were one real one
47
+ // and one the command refuses. The four the enum actually accepts:
48
+ 'How well evidenced the answer must be: self_reported (the doer says so), ' +
49
+ 'human_confirmed (a person confirmed it), system_observed (the platform saw it), ' +
50
+ 'external_receipt (a provider receipt proves it).',
46
51
  }),
47
52
  reason: core_1.Flags.string({
48
53
  required: true,
@@ -251,26 +251,62 @@ class TasksUpdate extends base_command_1.BaseCommand {
251
251
  * rewrote the list and revoked the assessments with no warning at all.
252
252
  * Same write, same price: the disclosure belongs on the effective change.
253
253
  */
254
- if (expectationsPatch !== undefined || body.expectations !== undefined) {
255
- let settledCount = 0;
254
+ /*
255
+ * The DESCRIPTION is a spec field too.
256
+ *
257
+ * `TaskSpecFence.TASK_SPEC_FIELDS` is `['description', 'expectations']`, and
258
+ * both advance `specVersion` — which revokes every assessment, because they
259
+ * are matched by exact version. The disclosure was wired to the Expectations
260
+ * payload alone, so fixing a typo in the prose voided a reviewer's sign-off
261
+ * with no warning and no receipt. Same write, same price, same disclosure.
262
+ */
263
+ const changesSpec = expectationsPatch !== undefined ||
264
+ body.expectations !== undefined ||
265
+ flags.description !== undefined ||
266
+ body.description !== undefined;
267
+ if (changesSpec) {
268
+ let settled = [];
269
+ let unreadable = false;
256
270
  try {
257
271
  const acceptance = await data_provider_1.dataService.getTaskAcceptance(taskIdentity.id);
258
- settledCount = acceptance.expectations.filter((e) => e.assessmentId).length;
272
+ settled = acceptance.expectations.filter((e) => e.assessmentId);
259
273
  }
260
274
  catch {
261
275
  // An unreadable acceptance is not a reason to refuse the edit. Warn
262
276
  // without a number rather than inventing one or blocking the write.
263
- settledCount = -1;
277
+ unreadable = true;
264
278
  }
265
- if (settledCount !== 0 && !flags.yes) {
266
- const noun = settledCount === 1 ? 'assessment' : 'assessments';
267
- const count = settledCount < 0 ? 'Any existing' : `${settledCount} existing`;
268
- this.log(`Changing Expectations advances the Task specification, so ${count} ${noun} ` +
279
+ if ((unreadable || settled.length > 0) && !flags.yes) {
280
+ const noun = settled.length === 1 ? 'assessment' : 'assessments';
281
+ const count = unreadable ? 'Any existing' : `${settled.length} existing`;
282
+ const field = expectationsPatch !== undefined || body.expectations !== undefined
283
+ ? 'Changing Expectations'
284
+ : 'Changing the description';
285
+ this.log(`${field} advances the Task specification, so ${count} ${noun} ` +
269
286
  'will stop counting and must be recorded again.');
287
+ /*
288
+ * NAME them. The count alone made the warning unactionable — and it is
289
+ * task-WIDE, so Expectations you are not touching lose their judgements
290
+ * too, which a bare number actively hides.
291
+ */
292
+ for (const row of settled) {
293
+ this.log(` ${row.id} ${row.sentence || row.text || ''}`);
294
+ }
295
+ if (settled.length > 0) {
296
+ this.log('Revocation covers every Expectation on this Task, including ones this edit ' +
297
+ 'does not touch.');
298
+ }
270
299
  const ok = await this.confirm('Continue? [y/N] ');
271
300
  if (!ok) {
272
- this.log('No change made.');
273
- return;
301
+ /*
302
+ * Non-zero, because the edit did NOT happen.
303
+ *
304
+ * This printed "No change made." and exited 0, so `skrr tasks update
305
+ * … && echo applied` printed `applied`. The guard exists to protect
306
+ * the non-interactive path, and the non-interactive path is precisely
307
+ * the one that cannot see a message — it can only read the exit code.
308
+ */
309
+ this.error('No change made — re-run with `--yes` to accept the revocation.', { exit: 2 });
274
310
  }
275
311
  }
276
312
  }
@@ -42,7 +42,7 @@ class TasksUpdatesAdd extends base_command_1.BaseCommand {
42
42
  'fields are required even when empty. ' +
43
43
  'progress {completedSinceLast:[],nextActions:[],currentFocus?} · ' +
44
44
  'finding {finding,evidenceRefs:[],impact?,severity?} · ' +
45
- 'decision {decision,rationale,alternativesConsidered?} · ' +
45
+ 'decision {decision,rationale,alternativesConsidered?:[]} · ' +
46
46
  'blocker {cause,requiredAction,nextOwner} · ' +
47
47
  'verification {claim,result:passed|failed|inconclusive,evidenceRefs:[]}. ' +
48
48
  'nextOwner is a principal object like {"kind":"human","userId":"..."}, not a name. ' +
@@ -0,0 +1,22 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * `skrr token list`
4
+ *
5
+ * The other half of `token mint`. Minting binds a long-lived refresh session to
6
+ * a machine and records the `--label` with it — which reads as a promise that
7
+ * the credential can be found again — and there was no way to see it: `token`
8
+ * had only `mint`, and `list-tokens` shows CI tokens, a different credential
9
+ * type, so `revoke-token` had nothing to aim at. The only remedy was
10
+ * `logout --all-devices`, which signs every machine out.
11
+ *
12
+ * `GET /api/auth/sessions/refresh` has served exactly this list all along; it
13
+ * was simply not reachable from the CLI.
14
+ */
15
+ export default class TokenList extends BaseCommand {
16
+ static description: string;
17
+ static examples: string[];
18
+ static flags: {
19
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
+ };
21
+ run(): Promise<void>;
22
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const base_command_1 = require("../../base-command");
5
+ const api_fetch_1 = require("../../lib/api-fetch");
6
+ const format_1 = require("../../lib/format");
7
+ /**
8
+ * `skrr token list`
9
+ *
10
+ * The other half of `token mint`. Minting binds a long-lived refresh session to
11
+ * a machine and records the `--label` with it — which reads as a promise that
12
+ * the credential can be found again — and there was no way to see it: `token`
13
+ * had only `mint`, and `list-tokens` shows CI tokens, a different credential
14
+ * type, so `revoke-token` had nothing to aim at. The only remedy was
15
+ * `logout --all-devices`, which signs every machine out.
16
+ *
17
+ * `GET /api/auth/sessions/refresh` has served exactly this list all along; it
18
+ * was simply not reachable from the CLI.
19
+ */
20
+ class TokenList extends base_command_1.BaseCommand {
21
+ static description = 'List the active sessions a minted token belongs to — the other half of `token mint`';
22
+ static examples = [
23
+ '<%= config.bin %> token list',
24
+ '<%= config.bin %> token list --json',
25
+ ];
26
+ static flags = { json: core_1.Flags.boolean({ description: 'Output JSON' }) };
27
+ async run() {
28
+ this.requireAuth();
29
+ const { flags } = await this.parse(TokenList);
30
+ let data = null;
31
+ try {
32
+ data = await (0, api_fetch_1.apiFetch)('/api/auth/sessions/refresh', {
33
+ method: 'GET',
34
+ credential: this.resolvedCredential,
35
+ });
36
+ }
37
+ catch (err) {
38
+ this.handleApiError(err);
39
+ }
40
+ const sessions = data?.sessions || [];
41
+ if (flags.json)
42
+ return this.log(JSON.stringify({ sessions, count: sessions.length }, null, 2));
43
+ if (!sessions.length)
44
+ return this.log('No active sessions.');
45
+ (0, format_1.renderTable)(sessions.map((session) => ({
46
+ id: session.id,
47
+ kind: session.kind,
48
+ // The operator's own name for the credential wins over a derived
49
+ // device string — it is what they would search by.
50
+ device: session.label || session.device || '-',
51
+ ip: session.ip || '-',
52
+ lastActive: session.lastActiveAt || session.createdAt || '-',
53
+ // The session you are typing in. Revoking it signs THIS machine out,
54
+ // which is the one outcome nobody means when they revoke a credential.
55
+ current: session.current ? 'yes' : '',
56
+ })), [
57
+ { key: 'id', header: 'ID' },
58
+ { key: 'kind', header: 'KIND' },
59
+ { key: 'device', header: 'DEVICE', maxWidth: 34 },
60
+ { key: 'ip', header: 'IP' },
61
+ { key: 'lastActive', header: 'LAST ACTIVE' },
62
+ { key: 'current', header: 'CURRENT' },
63
+ ], (line) => this.log(line));
64
+ this.log('');
65
+ this.log('Retire one with: skrr token revoke <id>');
66
+ }
67
+ }
68
+ exports.default = TokenList;
@@ -0,0 +1,23 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * `skrr token revoke <id>`
4
+ *
5
+ * Retire ONE session — the credential `token mint` handed to one machine —
6
+ * without signing every other machine out. `logout --all-devices` was the only
7
+ * way to do this, which is a blunt instrument for "that one CI box is
8
+ * decommissioned".
9
+ *
10
+ * Owner-gated server-side: a row that is not the caller's returns
11
+ * `{ revoked: false }` rather than an error, so ids cannot be enumerated.
12
+ */
13
+ export default class TokenRevoke extends BaseCommand {
14
+ static description: string;
15
+ static examples: string[];
16
+ static args: {
17
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
18
+ };
19
+ static flags: {
20
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
+ };
22
+ run(): Promise<void>;
23
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const base_command_1 = require("../../base-command");
5
+ const api_fetch_1 = require("../../lib/api-fetch");
6
+ /**
7
+ * `skrr token revoke <id>`
8
+ *
9
+ * Retire ONE session — the credential `token mint` handed to one machine —
10
+ * without signing every other machine out. `logout --all-devices` was the only
11
+ * way to do this, which is a blunt instrument for "that one CI box is
12
+ * decommissioned".
13
+ *
14
+ * Owner-gated server-side: a row that is not the caller's returns
15
+ * `{ revoked: false }` rather than an error, so ids cannot be enumerated.
16
+ */
17
+ class TokenRevoke extends base_command_1.BaseCommand {
18
+ static description = 'Revoke one session by id — see `skrr token list` for the ids';
19
+ static examples = ['<%= config.bin %> token revoke 665f1c2e9a4b3d0011aa77bc'];
20
+ static args = {
21
+ id: core_1.Args.string({
22
+ required: true,
23
+ ignoreStdin: true,
24
+ description: 'Session id from `skrr token list`',
25
+ }),
26
+ };
27
+ static flags = { json: core_1.Flags.boolean({ description: 'Output JSON' }) };
28
+ async run() {
29
+ this.requireAuth();
30
+ const { args, flags } = await this.parse(TokenRevoke);
31
+ let data = null;
32
+ try {
33
+ data = await (0, api_fetch_1.apiFetch)(`/api/auth/sessions/refresh/${encodeURIComponent(args.id)}/revoke`, {
34
+ method: 'POST',
35
+ credential: this.resolvedCredential,
36
+ body: {},
37
+ });
38
+ }
39
+ catch (err) {
40
+ this.handleApiError(err);
41
+ }
42
+ if (flags.json)
43
+ return this.log(JSON.stringify(data, null, 2));
44
+ if (data?.revoked)
45
+ return this.log(`Revoked session ${args.id}. Other machines stay signed in.`);
46
+ // The server does not distinguish "already gone" from "not yours" on
47
+ // purpose, so neither does this — saying "not found" would leak the
48
+ // difference and let somebody probe for ids.
49
+ this.log(`Nothing to revoke for ${args.id} — it is already revoked, or it is not one of your sessions.`);
50
+ this.log('List what is active with: skrr token list');
51
+ }
52
+ }
53
+ exports.default = TokenRevoke;
@@ -1,11 +1,50 @@
1
1
  import type { Agent, AgentAction, AgentActionOutcome, AgentActionScope, BuiltinActionConfig, BuiltinActionType, BuiltinAgentAction, EmbeddedActionRef, PromptAgentAction } from '@skrr-ai/data-provider';
2
- export declare const BUILTIN_ACTION_TYPES: readonly ["slack_send_message", "slack_tool", "group_chat_message", "space_create_task", "space_post_update", "task_create", "task_mark_done", "task_assign", "goal_create", "goal_mark_done", "smart_schedule_plan", "smart_schedule_delta_watch"];
2
+ export { BUILTIN_ACTION_TYPES } from '@skrr-ai/data-provider';
3
3
  export declare const ACTION_TARGET_TYPES: readonly ["group-chats", "spaces", "tasks", "goals", "slack-channels", "slack-users"];
4
4
  export type ActionTargetType = (typeof ACTION_TARGET_TYPES)[number];
5
5
  export declare function parseJsonObjectFlag(value: string | undefined, flagName: string): Record<string, unknown> | undefined;
6
6
  export declare function fetchAgentForActionEdit(agentId: string): Promise<Agent>;
7
7
  export declare function getAgentActions(agent: Agent): AgentAction[];
8
8
  export declare function findAgentAction(agent: Agent, actionId: string): AgentAction | undefined;
9
+ /**
10
+ * The row as the SERVER stored it, not as we sent it.
11
+ *
12
+ * `updateAgentActions` returns the updated Agent, and both editors used to
13
+ * discard it and print the object they had just built — so the output was the
14
+ * input, restated. That is not a cosmetic difference: the server normalizes
15
+ * `agentActions` through an allowlist before persisting, so a field it drops is
16
+ * echoed back by the CLI as though it had been saved. A `label` scope and
17
+ * `outcome: durable` were both silently discarded that way and the CLI reported
18
+ * success with the values the caller had typed (OSK-9628).
19
+ *
20
+ * Falling back to the local object keeps the command working if the write
21
+ * succeeds but the response is unexpectedly shaped; it cannot make the output
22
+ * wrong in a way it was not already.
23
+ */
24
+ export declare function storedActionOrLocal(agent: Agent | null | undefined, local: AgentAction): AgentAction;
25
+ export type AppendedAgentAction = {
26
+ /** The action as the server stored it. */
27
+ action: AgentAction;
28
+ /** False when this id was already in the library — a replayed request. */
29
+ created: boolean;
30
+ total: number;
31
+ };
32
+ /**
33
+ * Append ONE action atomically.
34
+ *
35
+ * `agents actions create` used to read the agent, append in memory and write
36
+ * the whole library back through `updateAgentActions`. Two concurrent creates
37
+ * therefore each read the same library and the later write erased the earlier
38
+ * action — six parallel creates all printed success and left one (OSK-9673).
39
+ * This endpoint appends with a single conditional `$push` on the server, so
40
+ * concurrent creates all land, the cap is enforced at the instant of the write,
41
+ * and a retried request whose id already landed returns `created: false`
42
+ * instead of adding a second copy. The client-generated id is what makes that
43
+ * retry safe, so it is sent rather than left for the server to assign.
44
+ *
45
+ * Update and delete still use `updateAgentActions` and still read-modify-write.
46
+ */
47
+ export declare function appendAgentAction(agentId: string, action: AgentAction): Promise<AppendedAgentAction>;
9
48
  export declare function updateAgentActions(agentId: string, actions: AgentAction[]): Promise<Agent>;
10
49
  /**
11
50
  * Fields every action shape carries regardless of kind.
@@ -52,7 +91,23 @@ export type AgentActionScopeFlags = {
52
91
  * `--scope space --space s1` twice is the kind of ceremony people skip and then
53
92
  * wonder why the action is everywhere. An explicit `--scope general` clears it.
54
93
  */
55
- export declare function resolveAgentActionScopeFlags(flags: AgentActionScopeFlags): AgentActionScope | undefined;
94
+ /**
95
+ * `--label` accepts an id OR a name, like every other `--label` in the CLI.
96
+ *
97
+ * It used to take the value verbatim as a `labelId`. A name was therefore
98
+ * stored as though it were an id, matched no space's `labelIds`, and produced
99
+ * an action offered NOWHERE — silently, with the create reporting success. That
100
+ * is exactly the "an action that disappears reads as deletion" hazard the label
101
+ * tier was designed around, arriving through the CLI instead of through a card
102
+ * that forgot to pass its labels. An unknown name was accepted too, so a typo
103
+ * was indistinguishable from a working scope.
104
+ *
105
+ * `resolveLabelRefs` is the same resolver `spaces create --label` uses: it
106
+ * short-circuits when every value already looks like an id (so a scripted
107
+ * caller pays no request), refuses an unknown name with the near misses named,
108
+ * and refuses an ambiguous one rather than guessing.
109
+ */
110
+ export declare function resolveAgentActionScopeFlags(flags: AgentActionScopeFlags): Promise<AgentActionScope | undefined>;
56
111
  /** Human-readable one-liner for a scope, used by list/show renderers. */
57
112
  export declare function describeAgentActionScope(scope?: AgentActionScope | null): string;
58
113
  export declare function nextActionOrder(actions: AgentAction[]): number;