@skrr-ai/cli 0.1.36 → 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 (266) hide show
  1. package/README.md +17 -4
  2. package/bin/dev-fallback.js +167 -4
  3. package/dist/base-command.js +46 -2
  4. package/dist/commands/agents/actions/create.d.ts +5 -0
  5. package/dist/commands/agents/actions/create.js +42 -5
  6. package/dist/commands/agents/actions/delete.d.ts +1 -0
  7. package/dist/commands/agents/actions/delete.js +7 -0
  8. package/dist/commands/agents/actions/update.d.ts +5 -0
  9. package/dist/commands/agents/actions/update.js +27 -1
  10. package/dist/commands/agents/api-actions/add.d.ts +41 -0
  11. package/dist/commands/agents/api-actions/add.js +205 -0
  12. package/dist/commands/agents/api-actions/index.d.ts +39 -0
  13. package/dist/commands/agents/api-actions/index.js +103 -0
  14. package/dist/commands/agents/api-actions/remove.d.ts +14 -0
  15. package/dist/commands/agents/api-actions/remove.js +36 -0
  16. package/dist/commands/agents/avatar/default/apply.d.ts +25 -0
  17. package/dist/commands/agents/avatar/default/apply.js +56 -0
  18. package/dist/commands/agents/avatar/default/index.d.ts +29 -0
  19. package/dist/commands/agents/avatar/default/index.js +61 -0
  20. package/dist/commands/agents/avatar/default/set.d.ts +24 -0
  21. package/dist/commands/agents/avatar/default/set.js +120 -0
  22. package/dist/commands/agents/avatar/library/delete.d.ts +20 -0
  23. package/dist/commands/agents/avatar/library/delete.js +47 -0
  24. package/dist/commands/agents/avatar/library/index.d.ts +16 -0
  25. package/dist/commands/agents/avatar/library/index.js +56 -0
  26. package/dist/commands/agents/avatar/presets.d.ts +19 -0
  27. package/dist/commands/agents/avatar/presets.js +56 -0
  28. package/dist/commands/agents/avatar/upload.js +7 -1
  29. package/dist/commands/agents/categories/create.d.ts +14 -0
  30. package/dist/commands/agents/categories/create.js +46 -0
  31. package/dist/commands/agents/categories/delete.d.ts +13 -0
  32. package/dist/commands/agents/categories/delete.js +41 -0
  33. package/dist/commands/agents/categories/index.d.ts +36 -0
  34. package/dist/commands/agents/categories/index.js +135 -0
  35. package/dist/commands/agents/categories/update.d.ts +22 -0
  36. package/dist/commands/agents/categories/update.js +59 -0
  37. package/dist/commands/agents/chat.d.ts +21 -0
  38. package/dist/commands/agents/chat.js +94 -7
  39. package/dist/commands/agents/create.d.ts +10 -0
  40. package/dist/commands/agents/create.js +64 -3
  41. package/dist/commands/agents/ensure-default.d.ts +27 -0
  42. package/dist/commands/agents/ensure-default.js +65 -0
  43. package/dist/commands/agents/list.d.ts +15 -0
  44. package/dist/commands/agents/list.js +31 -1
  45. package/dist/commands/agents/mute.d.ts +16 -0
  46. package/dist/commands/agents/mute.js +38 -0
  47. package/dist/commands/agents/pin.d.ts +16 -0
  48. package/dist/commands/agents/pin.js +38 -0
  49. package/dist/commands/agents/scene-background/upload.d.ts +30 -0
  50. package/dist/commands/agents/scene-background/upload.js +97 -0
  51. package/dist/commands/agents/show.js +13 -0
  52. package/dist/commands/agents/tools/auth.d.ts +19 -0
  53. package/dist/commands/agents/tools/auth.js +41 -0
  54. package/dist/commands/agents/tools/calls.d.ts +36 -0
  55. package/dist/commands/agents/tools/calls.js +117 -0
  56. package/dist/commands/agents/tools/index.d.ts +20 -0
  57. package/dist/commands/agents/tools/index.js +77 -0
  58. package/dist/commands/agents/unmute.d.ts +16 -0
  59. package/dist/commands/agents/unmute.js +38 -0
  60. package/dist/commands/agents/unpin.d.ts +16 -0
  61. package/dist/commands/agents/unpin.js +38 -0
  62. package/dist/commands/agents/update.d.ts +12 -0
  63. package/dist/commands/agents/update.js +74 -3
  64. package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
  65. package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
  66. package/dist/commands/convos/messages.d.ts +13 -0
  67. package/dist/commands/convos/messages.js +40 -16
  68. package/dist/commands/daemon/index.js +8 -1
  69. package/dist/commands/daemon/logs.d.ts +26 -0
  70. package/dist/commands/daemon/logs.js +56 -0
  71. package/dist/commands/daemon/uninstall.js +1 -1
  72. package/dist/commands/followups/act.d.ts +22 -0
  73. package/dist/commands/followups/act.js +103 -0
  74. package/dist/commands/followups/cancel.d.ts +20 -0
  75. package/dist/commands/followups/cancel.js +50 -0
  76. package/dist/commands/followups/checkin.d.ts +27 -0
  77. package/dist/commands/followups/checkin.js +107 -0
  78. package/dist/commands/followups/list.d.ts +24 -0
  79. package/dist/commands/followups/list.js +90 -0
  80. package/dist/commands/followups/observability.d.ts +23 -0
  81. package/dist/commands/followups/observability.js +114 -0
  82. package/dist/commands/followups/remind.d.ts +26 -0
  83. package/dist/commands/followups/remind.js +106 -0
  84. package/dist/commands/followups/reschedule.d.ts +15 -0
  85. package/dist/commands/followups/reschedule.js +48 -0
  86. package/dist/commands/followups/resolve.d.ts +23 -0
  87. package/dist/commands/followups/resolve.js +54 -0
  88. package/dist/commands/followups/show.d.ts +12 -0
  89. package/dist/commands/followups/show.js +46 -0
  90. package/dist/commands/followups/watch.d.ts +51 -0
  91. package/dist/commands/followups/watch.js +211 -0
  92. package/dist/commands/goals/labels/add.js +19 -2
  93. package/dist/commands/goals/labels/remove.js +22 -2
  94. package/dist/commands/harnesses/forget.js +28 -0
  95. package/dist/commands/harnesses/install.d.ts +41 -0
  96. package/dist/commands/harnesses/install.js +165 -0
  97. package/dist/commands/harnesses/installers.d.ts +45 -0
  98. package/dist/commands/harnesses/installers.js +155 -0
  99. package/dist/commands/harnesses/list.d.ts +82 -1
  100. package/dist/commands/harnesses/list.js +104 -12
  101. package/dist/commands/harnesses/models.js +10 -1
  102. package/dist/commands/harnesses/show.d.ts +15 -0
  103. package/dist/commands/harnesses/show.js +44 -3
  104. package/dist/commands/harnesses/update.js +1 -1
  105. package/dist/commands/harnesses/usage.d.ts +25 -0
  106. package/dist/commands/harnesses/usage.js +24 -2
  107. package/dist/commands/instructions/status.js +13 -1
  108. package/dist/commands/labels/list.d.ts +24 -1
  109. package/dist/commands/labels/list.js +62 -19
  110. package/dist/commands/machines/dedicated/catalog.js +4 -15
  111. package/dist/commands/machines/dedicated/create.d.ts +8 -0
  112. package/dist/commands/machines/dedicated/create.js +58 -1
  113. package/dist/commands/machines/dedicated/destroy.js +18 -4
  114. package/dist/commands/machines/dedicated/list.js +9 -6
  115. package/dist/commands/machines/hosted/list.js +12 -0
  116. package/dist/commands/machines/list.js +11 -0
  117. package/dist/commands/machines/show.d.ts +1 -0
  118. package/dist/commands/machines/show.js +40 -11
  119. package/dist/commands/memory/governance/index.d.ts +21 -0
  120. package/dist/commands/memory/governance/index.js +64 -0
  121. package/dist/commands/memory/governance/set.d.ts +24 -0
  122. package/dist/commands/memory/governance/set.js +95 -0
  123. package/dist/commands/memory/provenance.d.ts +18 -0
  124. package/dist/commands/memory/provenance.js +66 -0
  125. package/dist/commands/profiles/list.d.ts +23 -0
  126. package/dist/commands/profiles/list.js +96 -0
  127. package/dist/commands/search/skills.d.ts +11 -1
  128. package/dist/commands/search/skills.js +26 -8
  129. package/dist/commands/skills/details.d.ts +2 -0
  130. package/dist/commands/skills/details.js +24 -1
  131. package/dist/commands/skills/import-as-actions.d.ts +32 -0
  132. package/dist/commands/skills/import-as-actions.js +175 -0
  133. package/dist/commands/skills/install.d.ts +2 -0
  134. package/dist/commands/skills/install.js +24 -1
  135. package/dist/commands/skills/invocable.js +6 -2
  136. package/dist/commands/skills/local.d.ts +40 -0
  137. package/dist/commands/skills/local.js +160 -0
  138. package/dist/commands/skills/search.d.ts +11 -1
  139. package/dist/commands/skills/search.js +29 -8
  140. package/dist/commands/spaces/actions/add.js +23 -5
  141. package/dist/commands/spaces/actions/available.js +3 -0
  142. package/dist/commands/spaces/actions/list.d.ts +5 -0
  143. package/dist/commands/spaces/actions/list.js +49 -3
  144. package/dist/commands/spaces/actions/run.d.ts +28 -0
  145. package/dist/commands/spaces/actions/run.js +146 -0
  146. package/dist/commands/spaces/actions/update.js +27 -5
  147. package/dist/commands/spaces/create.d.ts +3 -0
  148. package/dist/commands/spaces/create.js +32 -2
  149. package/dist/commands/spaces/label-summaries.d.ts +27 -0
  150. package/dist/commands/spaces/label-summaries.js +64 -0
  151. package/dist/commands/spaces/labels/add.js +19 -2
  152. package/dist/commands/spaces/labels/remove.js +22 -2
  153. package/dist/commands/spaces/show.js +2 -0
  154. package/dist/commands/spaces/task-rollups.d.ts +5 -0
  155. package/dist/commands/spaces/task-rollups.js +15 -4
  156. package/dist/commands/spaces/update.d.ts +5 -0
  157. package/dist/commands/spaces/update.js +41 -4
  158. package/dist/commands/spaces/workflows/lifecycle-diagnostics.d.ts +11 -0
  159. package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +105 -0
  160. package/dist/commands/tasks/actions/attach.js +32 -7
  161. package/dist/commands/tasks/complete.d.ts +1 -0
  162. package/dist/commands/tasks/complete.js +21 -1
  163. package/dist/commands/tasks/create.d.ts +3 -0
  164. package/dist/commands/tasks/create.js +34 -2
  165. package/dist/commands/tasks/events/append.js +29 -6
  166. package/dist/commands/tasks/expectations/assess.d.ts +12 -0
  167. package/dist/commands/tasks/expectations/assess.js +123 -14
  168. package/dist/commands/tasks/expectations.js +95 -17
  169. package/dist/commands/tasks/follow-up.js +2 -2
  170. package/dist/commands/tasks/labels/attach.js +20 -3
  171. package/dist/commands/tasks/labels/detach.js +21 -3
  172. package/dist/commands/tasks/lifecycle/attach.d.ts +14 -0
  173. package/dist/commands/tasks/lifecycle/attach.js +47 -0
  174. package/dist/commands/tasks/lifecycle/definitions.d.ts +11 -0
  175. package/dist/commands/tasks/lifecycle/definitions.js +59 -0
  176. package/dist/commands/tasks/lifecycle/detach.d.ts +19 -0
  177. package/dist/commands/tasks/lifecycle/detach.js +72 -0
  178. package/dist/commands/tasks/lifecycle/history.d.ts +13 -0
  179. package/dist/commands/tasks/lifecycle/history.js +38 -0
  180. package/dist/commands/tasks/lifecycle/migrate.d.ts +14 -0
  181. package/dist/commands/tasks/lifecycle/migrate.js +68 -0
  182. package/dist/commands/tasks/lifecycle/reconcile.d.ts +11 -0
  183. package/dist/commands/tasks/lifecycle/reconcile.js +27 -0
  184. package/dist/commands/tasks/lifecycle/reopen.d.ts +15 -0
  185. package/dist/commands/tasks/lifecycle/reopen.js +71 -0
  186. package/dist/commands/tasks/lifecycle/show.d.ts +11 -0
  187. package/dist/commands/tasks/lifecycle/show.js +27 -0
  188. package/dist/commands/tasks/lifecycle/start.d.ts +12 -0
  189. package/dist/commands/tasks/lifecycle/start.js +30 -0
  190. package/dist/commands/tasks/lifecycle/transition.d.ts +15 -0
  191. package/dist/commands/tasks/lifecycle/transition.js +109 -0
  192. package/dist/commands/tasks/list.js +16 -9
  193. package/dist/commands/tasks/move.d.ts +1 -0
  194. package/dist/commands/tasks/move.js +29 -0
  195. package/dist/commands/tasks/result/submit.js +11 -1
  196. package/dist/commands/tasks/review-queue.d.ts +44 -0
  197. package/dist/commands/tasks/review-queue.js +314 -0
  198. package/dist/commands/tasks/show.d.ts +11 -0
  199. package/dist/commands/tasks/show.js +139 -4
  200. package/dist/commands/tasks/trust.js +7 -2
  201. package/dist/commands/tasks/update.d.ts +1 -0
  202. package/dist/commands/tasks/update.js +83 -0
  203. package/dist/commands/tasks/updates/add.js +1 -1
  204. package/dist/commands/token/list.d.ts +22 -0
  205. package/dist/commands/token/list.js +68 -0
  206. package/dist/commands/token/revoke.d.ts +23 -0
  207. package/dist/commands/token/revoke.js +53 -0
  208. package/dist/lib/agent-actions.d.ts +92 -11
  209. package/dist/lib/agent-actions.js +171 -25
  210. package/dist/lib/agent-config.d.ts +11 -0
  211. package/dist/lib/agent-config.js +85 -3
  212. package/dist/lib/agent-pin-mute.d.ts +29 -0
  213. package/dist/lib/agent-pin-mute.js +30 -0
  214. package/dist/lib/agent-visual-refs.d.ts +33 -0
  215. package/dist/lib/agent-visual-refs.js +267 -0
  216. package/dist/lib/agentic-stream.d.ts +6 -0
  217. package/dist/lib/agentic-stream.js +34 -3
  218. package/dist/lib/assignee-resolver.d.ts +1 -1
  219. package/dist/lib/assignee-resolver.js +8 -0
  220. package/dist/lib/bulk-task-targets.d.ts +12 -0
  221. package/dist/lib/bulk-task-targets.js +34 -1
  222. package/dist/lib/cli-installers.d.ts +130 -0
  223. package/dist/lib/cli-installers.js +145 -0
  224. package/dist/lib/command-miss.js +55 -7
  225. package/dist/lib/dedicated-lease-command.d.ts +13 -0
  226. package/dist/lib/dedicated-lease-command.js +22 -1
  227. package/dist/lib/dedicated-machines.d.ts +95 -1
  228. package/dist/lib/dedicated-machines.js +236 -2
  229. package/dist/lib/failed-lookup.d.ts +3 -0
  230. package/dist/lib/failed-lookup.js +40 -0
  231. package/dist/lib/first-party-harness-agent.js +16 -2
  232. package/dist/lib/first-party-harness-doctor.d.ts +15 -0
  233. package/dist/lib/first-party-harness-doctor.js +126 -27
  234. package/dist/lib/followups.d.ts +115 -0
  235. package/dist/lib/followups.js +127 -0
  236. package/dist/lib/harnesses.d.ts +8 -0
  237. package/dist/lib/instruction-input.d.ts +19 -1
  238. package/dist/lib/instruction-input.js +52 -1
  239. package/dist/lib/label-ref.d.ts +95 -0
  240. package/dist/lib/label-ref.js +163 -0
  241. package/dist/lib/label-scope.d.ts +17 -2
  242. package/dist/lib/label-scope.js +27 -7
  243. package/dist/lib/local-skills.d.ts +98 -0
  244. package/dist/lib/local-skills.js +195 -0
  245. package/dist/lib/machines.d.ts +10 -10
  246. package/dist/lib/machines.js +23 -1
  247. package/dist/lib/search-query.d.ts +19 -0
  248. package/dist/lib/search-query.js +35 -0
  249. package/dist/lib/space-custom-actions.d.ts +72 -0
  250. package/dist/lib/space-custom-actions.js +117 -0
  251. package/dist/lib/space-resolver.d.ts +1 -21
  252. package/dist/lib/space-resolver.js +19 -10
  253. package/dist/lib/task-closure.d.ts +33 -0
  254. package/dist/lib/task-closure.js +104 -0
  255. package/dist/lib/task-lifecycle-output.d.ts +14 -0
  256. package/dist/lib/task-lifecycle-output.js +209 -0
  257. package/dist/lib/task-ref-resolver.d.ts +1 -17
  258. package/dist/lib/task-ref-resolver.js +18 -8
  259. package/dist/lib/tasks.d.ts +3 -0
  260. package/dist/lib/tasks.js +40 -3
  261. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +4 -0
  262. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +4 -0
  263. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  264. package/dist/node_modules/@skrr-ai/data-provider/index.js +5559 -4897
  265. package/oclif.manifest.json +33890 -28690
  266. package/package.json +24 -6
@@ -38,7 +38,16 @@ class HarnessesModels extends base_command_1.BaseCommand {
38
38
  const report = payload;
39
39
  const models = report?.models ?? [];
40
40
  if (models.length === 0) {
41
- this.log('This harness advertises no models.');
41
+ // "This harness advertises no models." is true and answers none of the
42
+ // three questions a reader has: is this a product gap, my machine's
43
+ // fault, or a broken command? Saying which provider was resolved and
44
+ // where the list comes from picks one — and, in the bug this replaced,
45
+ // would have shown `provider` as null on a Claude harness and named the
46
+ // defect on sight.
47
+ const resolved = report?.provider;
48
+ this.log(resolved
49
+ ? `No models are listed for ${resolved}. skrr keeps a curated list per provider and ${resolved} has no entries in it, so pick a model on the agent instead — \`skrr agents update <agent> --model <id>\` accepts an id this list does not name.`
50
+ : 'This harness resolved to no provider, so no model list could be built. That is a defect, not a configuration problem — report it with the harness id.');
42
51
  return;
43
52
  }
44
53
  if (report?.provider || report?.source) {
@@ -10,3 +10,18 @@ export default class HarnessesShow extends BaseCommand {
10
10
  };
11
11
  run(): Promise<void>;
12
12
  }
13
+ /**
14
+ * Say what the caller DID pass, not only what they did not.
15
+ *
16
+ * Every id in this product is a bare string, and three of them are printed side
17
+ * by side: `machines show` takes a machine or daemon id, `harnesses show` takes
18
+ * a Harness id. `machines show <daemon-id>` succeeds, so a user who learns that
19
+ * reasonably tries the same id here — and got back only a restatement of the
20
+ * rule they had just broken, plus the Mongo field name `_id`, which names an
21
+ * implementation detail rather than anything they can see.
22
+ *
23
+ * The shapes below are the ones our own output prints, so recognising them
24
+ * costs nothing and turns a refusal into a redirect. Anything unrecognised
25
+ * still falls through to the general answer rather than being guessed at.
26
+ */
27
+ export declare function describeWrongHarnessRef(ref: string): string;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeWrongHarnessRef = describeWrongHarnessRef;
3
4
  const core_1 = require("@oclif/core");
4
5
  const base_command_1 = require("../../base-command");
5
6
  const harness_provider_input_1 = require("../../lib/harness-provider-input");
@@ -35,9 +36,9 @@ class HarnessesShow extends base_command_1.BaseCommand {
35
36
  // "HTTP 400 Bad Request — GET /api/harnesses/<id>". Any 400 on this
36
37
  // lookup is the id being wrong, so branch on the status.
37
38
  if (err instanceof api_fetch_1.ApiFetchError && err.status === 400) {
38
- this.error(`Not a harness id: ${args.harness}\n` +
39
- 'A harness id is the 24-character Harness _id. Run `skrr harnesses list --all` ' +
40
- 'to see the ids on this account.', { exit: 1 });
39
+ this.error(`Not a harness id: ${args.harness}\n${describeWrongHarnessRef(args.harness)}`, {
40
+ exit: 1,
41
+ });
41
42
  return;
42
43
  }
43
44
  this.handleApiError(err);
@@ -61,6 +62,14 @@ class HarnessesShow extends base_command_1.BaseCommand {
61
62
  this.log(` provider ${harness.provider}`);
62
63
  // `harnessLocation` is the difference between "your laptop" and "a cloud row",
63
64
  // and it decides which verbs apply: ping and claudeEngine are local-only.
65
+ // When YOUR alias is covering the shared name, say so and show both. The
66
+ // header prints the effective name, which is the alias — without this the
67
+ // value `harnesses update --name` just wrote appears on no surface at all,
68
+ // and you cannot tell you are reading your own label rather than the one
69
+ // everyone else sees.
70
+ if (harness.aliased && harness.sharedName && harness.sharedName !== harness.name) {
71
+ this.log(` shared name ${harness.sharedName} (your alias is showing instead)`);
72
+ }
64
73
  this.log(` mode ${harness.harnessLocation ?? 'unknown'}`);
65
74
  this.log(` status ${harness.status ?? 'unknown'}`);
66
75
  // Only for the claude provider. The daemon stamps `claudeEngine` on every
@@ -77,3 +86,35 @@ class HarnessesShow extends base_command_1.BaseCommand {
77
86
  }
78
87
  }
79
88
  exports.default = HarnessesShow;
89
+ /**
90
+ * Say what the caller DID pass, not only what they did not.
91
+ *
92
+ * Every id in this product is a bare string, and three of them are printed side
93
+ * by side: `machines show` takes a machine or daemon id, `harnesses show` takes
94
+ * a Harness id. `machines show <daemon-id>` succeeds, so a user who learns that
95
+ * reasonably tries the same id here — and got back only a restatement of the
96
+ * rule they had just broken, plus the Mongo field name `_id`, which names an
97
+ * implementation detail rather than anything they can see.
98
+ *
99
+ * The shapes below are the ones our own output prints, so recognising them
100
+ * costs nothing and turns a refusal into a redirect. Anything unrecognised
101
+ * still falls through to the general answer rather than being guessed at.
102
+ */
103
+ function describeWrongHarnessRef(ref) {
104
+ const seeIds = 'Run `skrr harnesses list --all` to see the harness ids on this account.';
105
+ if (/^daemon_/.test(ref)) {
106
+ return `That is a daemon id. \`skrr machines show ${ref}\` takes those. ${seeIds}`;
107
+ }
108
+ if (/^machine:/.test(ref)) {
109
+ return `That is a machine id. \`skrr machines show ${ref}\` takes those. ${seeIds}`;
110
+ }
111
+ if (/^(lease:|dedicated-runtime:|hosted-machine:)/.test(ref)) {
112
+ return ('That is a machine id for a rented runtime. `skrr machines show` takes it as printed; ' +
113
+ `\`skrr machines dedicated show\` takes the part after the colon. ${seeIds}`);
114
+ }
115
+ if (/^[0-9a-f]{24}$/i.test(ref)) {
116
+ // Right shape, wrong row — so the id is not the thing to explain.
117
+ return `No harness on this account has that id. ${seeIds}`;
118
+ }
119
+ return `A harness id is 24 hexadecimal characters. ${seeIds}`;
120
+ }
@@ -29,7 +29,7 @@ class HarnessesUpdate extends base_command_1.BaseCommand {
29
29
  }),
30
30
  description: core_1.Flags.string({ description: 'New description' }),
31
31
  'claude-engine': core_1.Flags.string({
32
- description: 'How Claude Code runs on this machine: `sdk` in-process (needed for AskUserQuestion), `cli` as a child process. Local harnesses only.',
32
+ description: 'How Claude Code runs on this machine: `sdk` in-process (needed for AskUserQuestion), `cli` as a child process. Local CLAUDE harnesses only — the server refuses it on any other provider, because nothing would ever read it.',
33
33
  options: ['cli', 'sdk'],
34
34
  }),
35
35
  };
@@ -1,6 +1,12 @@
1
1
  import { BaseCommand } from '../../base-command';
2
2
  export type UsageRow = {
3
3
  /** Fleet rollup only — one row per harness. */
4
+ harnessId?: string;
5
+ /**
6
+ * The pre-rename spelling of `harnessId`. The server stopped sending it at
7
+ * the harness rename; reading it alone left the HARNESS column printing `-`
8
+ * on every row of the rollup. Kept as a fallback for an older deployment.
9
+ */
4
10
  runtimeId?: string;
5
11
  name?: string;
6
12
  /** Single-harness report only — one row per hour. */
@@ -12,6 +18,25 @@ export type UsageRow = {
12
18
  outputTokens?: number;
13
19
  cachedInputTokens?: number;
14
20
  };
21
+ /**
22
+ * The rows to render, whichever spelling the server used.
23
+ *
24
+ * `byHarness` is what it sends today. This reader asked only for `byRuntime` —
25
+ * the pre-rename spelling — so the fleet rollup was ALWAYS empty and the
26
+ * command always printed "No usage recorded.", while `--json` on the same call
27
+ * showed $24.93 across 46 sessions. A rollup cannot be empty while its own
28
+ * members are not, which is what made it findable.
29
+ *
30
+ * The old spellings stay as fallbacks, after the current one, so an older
31
+ * deployment still renders. They are not the primary, which is the whole bug.
32
+ */
33
+ export declare function selectUsageRows(payload: {
34
+ byHarness?: UsageRow[];
35
+ byRuntime?: UsageRow[];
36
+ usage?: UsageRow[];
37
+ } | undefined): UsageRow[];
38
+ /** The HARNESS cell, under either spelling of the id. */
39
+ export declare function usageRowHarnessCell(row: UsageRow): string;
15
40
  export interface UsageTable {
16
41
  rows: Record<string, string>[];
17
42
  columns: {
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectUsageRows = selectUsageRows;
4
+ exports.usageRowHarnessCell = usageRowHarnessCell;
3
5
  exports.buildUsageTable = buildUsageTable;
4
6
  const core_1 = require("@oclif/core");
5
7
  const base_command_1 = require("../../base-command");
@@ -22,6 +24,26 @@ function count(value) {
22
24
  const n = Number(value);
23
25
  return Number.isFinite(n) ? n.toLocaleString('en-US') : '-';
24
26
  }
27
+ /**
28
+ * The rows to render, whichever spelling the server used.
29
+ *
30
+ * `byHarness` is what it sends today. This reader asked only for `byRuntime` —
31
+ * the pre-rename spelling — so the fleet rollup was ALWAYS empty and the
32
+ * command always printed "No usage recorded.", while `--json` on the same call
33
+ * showed $24.93 across 46 sessions. A rollup cannot be empty while its own
34
+ * members are not, which is what made it findable.
35
+ *
36
+ * The old spellings stay as fallbacks, after the current one, so an older
37
+ * deployment still renders. They are not the primary, which is the whole bug.
38
+ */
39
+ function selectUsageRows(payload) {
40
+ return payload?.byHarness ?? payload?.byRuntime ?? payload?.usage ?? [];
41
+ }
42
+ /** The HARNESS cell, under either spelling of the id. */
43
+ function usageRowHarnessCell(row) {
44
+ const id = row.harnessId ?? row.runtimeId;
45
+ return id ? String(id).slice(0, 12) : '-';
46
+ }
25
47
  const SHARED_COLUMNS = [
26
48
  { key: 'provider', header: 'PROVIDER' },
27
49
  { key: 'sessions', header: 'SESSIONS' },
@@ -66,7 +88,7 @@ function buildUsageTable(rows, perHarness) {
66
88
  ...SHARED_COLUMNS,
67
89
  ],
68
90
  rows: rows.map((r) => ({
69
- harness: r.runtimeId ? String(r.runtimeId).slice(0, 12) : '-',
91
+ harness: usageRowHarnessCell(r),
70
92
  name: (0, format_1.formatCell)(r.name),
71
93
  ...shared(r),
72
94
  })),
@@ -105,7 +127,7 @@ class HarnessesUsage extends base_command_1.BaseCommand {
105
127
  // `{usage, totals}`. Neither is a superset, so read both rather than
106
128
  // assuming one and rendering a table of dashes for the other.
107
129
  const payload = report;
108
- const rows = payload?.byRuntime ?? payload?.usage ?? [];
130
+ const rows = selectUsageRows(payload);
109
131
  const totals = payload?.grandTotals ?? payload?.totals;
110
132
  if (rows.length === 0) {
111
133
  this.log(args.harness ? 'No usage recorded for this harness.' : 'No usage recorded.');
@@ -29,6 +29,18 @@ class InstructionsStatus extends base_command_1.BaseCommand {
29
29
  // only `bundleId`, so join to the bundle list — including archived/deleted,
30
30
  // since a target can outlive its bundle. A failed lookup is non-fatal: the
31
31
  // column falls back to the full bundle id.
32
+ // The live daemons, so a stalled row can tell "this machine now starts its
33
+ // daemon a different way" from "the daemon is gone" — and only the second
34
+ // earns a destructive remedy. Non-fatal: an empty list degrades to the old
35
+ // wording, which is a guess, not a wrong claim about a daemon we checked.
36
+ let liveDaemonIds = [];
37
+ try {
38
+ const daemons = await data_provider_1.dataService.getDaemons();
39
+ liveDaemonIds = (daemons.active || []).map((d) => d.daemonId);
40
+ }
41
+ catch {
42
+ // Leave it empty.
43
+ }
32
44
  let bundleNames = new Map();
33
45
  try {
34
46
  const bundleResponse = await data_provider_1.dataService.listManagedInstructionBundles({
@@ -62,7 +74,7 @@ class InstructionsStatus extends base_command_1.BaseCommand {
62
74
  (target.scope === 'global' ? 'global' : target.projectPath || 'project'),
63
75
  desired: target.desiredState,
64
76
  applied: target.appliedVersionNumber ? `v${target.appliedVersionNumber}` : '-',
65
- status: (0, instruction_input_1.describeTargetSync)(target),
77
+ status: (0, instruction_input_1.describeTargetSync)(target, Date.now(), liveDaemonIds),
66
78
  })), [
67
79
  { key: 'id', header: 'TARGET ID' },
68
80
  // Wide enough for a prompt name (matches `instructions list`'s NAME
@@ -1,11 +1,34 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * `labels list` — every label you can actually USE, by default.
4
+ *
5
+ * This used to default to `--scope user` and show one scope at a time, which
6
+ * is a faithful rendering of `GET /api/labels` and a poor answer to the
7
+ * question people ask. `scope` is an OWNERSHIP boundary, not a filter anyone
8
+ * browses by: a workspace-scoped label is attachable by every member, so
9
+ * leaving it out of the default view means the vocabulary you are told about
10
+ * is smaller than the one you have.
11
+ *
12
+ * It had a measurable cost. Three workspace-scoped labels existed and were
13
+ * invisible to the bare command, so nobody knew they were there — which is
14
+ * part of why the label system shipped complete and went unused, and why a
15
+ * second `dogfood` nearly got created beside one that already existed.
16
+ *
17
+ * `--scope` still narrows to exactly one scope, so every explicit invocation
18
+ * behaves as it always did. Only the bare command changed, and it changed to
19
+ * a superset.
20
+ *
21
+ * The merge itself is `lib/label-ref.collectLabelVocabulary` — the same
22
+ * function `--label <name>` resolves through. A separate copy here is how the
23
+ * name you can type and the name you can see start to disagree.
24
+ */
2
25
  export default class LabelsList extends BaseCommand {
3
26
  static description: string;
4
27
  static examples: string[];
5
28
  static flags: {
29
+ scope: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
6
30
  'applicable-to': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
31
  'include-archived': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
- scope: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
9
32
  'scope-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
33
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
34
  };
@@ -5,17 +5,48 @@ const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const format_1 = require("../../lib/format");
7
7
  const label_scope_1 = require("../../lib/label-scope");
8
+ const label_ref_1 = require("../../lib/label-ref");
9
+ /**
10
+ * `labels list` — every label you can actually USE, by default.
11
+ *
12
+ * This used to default to `--scope user` and show one scope at a time, which
13
+ * is a faithful rendering of `GET /api/labels` and a poor answer to the
14
+ * question people ask. `scope` is an OWNERSHIP boundary, not a filter anyone
15
+ * browses by: a workspace-scoped label is attachable by every member, so
16
+ * leaving it out of the default view means the vocabulary you are told about
17
+ * is smaller than the one you have.
18
+ *
19
+ * It had a measurable cost. Three workspace-scoped labels existed and were
20
+ * invisible to the bare command, so nobody knew they were there — which is
21
+ * part of why the label system shipped complete and went unused, and why a
22
+ * second `dogfood` nearly got created beside one that already existed.
23
+ *
24
+ * `--scope` still narrows to exactly one scope, so every explicit invocation
25
+ * behaves as it always did. Only the bare command changed, and it changed to
26
+ * a superset.
27
+ *
28
+ * The merge itself is `lib/label-ref.collectLabelVocabulary` — the same
29
+ * function `--label <name>` resolves through. A separate copy here is how the
30
+ * name you can type and the name you can see start to disagree.
31
+ */
8
32
  class LabelsList extends base_command_1.BaseCommand {
9
- static description = 'List labels in a scope';
33
+ static description = 'List labels — every scope you can use, unless --scope narrows it';
10
34
  static examples = [
11
35
  '<%= config.bin %> labels list',
12
36
  '<%= config.bin %> labels list --applicable-to space',
37
+ '<%= config.bin %> labels list --scope user',
13
38
  '<%= config.bin %> labels list --scope workspace --scope-id <workspace-id>',
14
39
  '<%= config.bin %> labels list --include-archived --json',
15
40
  ];
16
41
  static flags = {
17
42
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
18
43
  ...label_scope_1.labelScopeFlags,
44
+ // No default, unlike the shared flag: its absence is what selects the
45
+ // merged view, so "not passed" and "passed user" must stay distinguishable.
46
+ scope: core_1.Flags.string({
47
+ description: 'Narrow to ONE scope. Omit to list every scope you can use.',
48
+ options: ['user', 'workspace'],
49
+ }),
19
50
  'applicable-to': core_1.Flags.string({
20
51
  description: 'Only labels applicable to this entity type',
21
52
  options: [...label_scope_1.LABEL_APPLICABLE],
@@ -25,38 +56,49 @@ class LabelsList extends base_command_1.BaseCommand {
25
56
  async run() {
26
57
  this.requireAuth();
27
58
  const { flags } = await this.parse(LabelsList);
28
- let scope;
29
- try {
30
- scope = (0, label_scope_1.resolveLabelScope)(flags);
31
- }
32
- catch (err) {
33
- this.error(err.message, { exit: 1 });
34
- }
35
- let response;
59
+ const applicableTo = flags['applicable-to'];
60
+ let items = [];
36
61
  try {
37
- response = await data_provider_1.dataService.getLabels({
38
- ...scope,
39
- ...(flags['applicable-to']
40
- ? { applicableTo: flags['applicable-to'] }
41
- : {}),
42
- ...(flags['include-archived'] ? { includeArchived: true } : {}),
43
- });
62
+ if (flags.scope) {
63
+ const scope = (0, label_scope_1.resolveLabelScope)(flags);
64
+ const response = (await data_provider_1.dataService.getLabels({
65
+ ...scope,
66
+ ...(applicableTo ? { applicableTo } : {}),
67
+ ...(flags['include-archived'] ? { includeArchived: true } : {}),
68
+ }));
69
+ items = response?.data ?? [];
70
+ }
71
+ else {
72
+ items = await (0, label_ref_1.collectLabelVocabulary)({
73
+ ...(applicableTo ? { applicableTo } : {}),
74
+ ...(process.env.OVERSKY_WORKSPACE_ID
75
+ ? { workspaceId: process.env.OVERSKY_WORKSPACE_ID }
76
+ : {}),
77
+ ...(flags['include-archived'] ? { includeArchived: true } : {}),
78
+ });
79
+ }
44
80
  }
45
81
  catch (err) {
82
+ if (err instanceof Error && !('status' in err))
83
+ this.error(err.message, { exit: 1 });
46
84
  this.handleApiError(err);
47
85
  }
48
86
  if (flags.json) {
49
- this.log(JSON.stringify(response, null, 2));
87
+ this.log(JSON.stringify({ data: items }, null, 2));
50
88
  return;
51
89
  }
52
- const items = response?.data ?? [];
53
90
  if (items.length === 0) {
54
- this.log('No labels.');
91
+ this.log(flags.scope
92
+ ? 'No labels in this scope.'
93
+ : 'No labels you can use. Create one with `labels create --name <name> --applicable-to <entity>`.');
55
94
  return;
56
95
  }
57
96
  const rows = items.map((l) => ({
58
97
  id: String(l.id ?? '-'),
59
98
  name: String(l.name ?? '-'),
99
+ // Where it lives decides who else sees it, and it is the one column the
100
+ // old single-scope view never had to show.
101
+ scope: String(l.scope ?? '-'),
60
102
  color: String(l.color ?? '-'),
61
103
  appliesTo: Array.isArray(l.applicableTo) ? l.applicableTo.join(',') : '-',
62
104
  used: String(l.usageCount ?? 0),
@@ -65,6 +107,7 @@ class LabelsList extends base_command_1.BaseCommand {
65
107
  (0, format_1.renderTable)(rows, [
66
108
  { key: 'id', header: 'ID' },
67
109
  { key: 'name', header: 'NAME', maxWidth: 32 },
110
+ { key: 'scope', header: 'SCOPE' },
68
111
  { key: 'color', header: 'COLOR' },
69
112
  { key: 'appliesTo', header: 'APPLIES TO' },
70
113
  { key: 'used', header: 'USED' },
@@ -41,16 +41,8 @@ class DedicatedMachinesCatalog extends dedicated_lease_command_1.DedicatedLeaseC
41
41
  this.log('\nSizes (--size-preset)');
42
42
  if (catalog.sizes.length === 0)
43
43
  this.log(' none offered right now');
44
- for (const size of catalog.sizes) {
45
- const spec = [
46
- size.vcpus ? `${size.vcpus} vCPU` : '',
47
- size.memoryGiB ? `${size.memoryGiB} GiB RAM` : '',
48
- size.accelerator || '',
49
- ]
50
- .filter(Boolean)
51
- .join(' · ');
52
- this.log(` ${String(size.id).padEnd(14)}${spec}`);
53
- }
44
+ for (const line of (0, dedicated_machines_1.dedicatedCatalogSizeLines)(catalog))
45
+ this.log(line);
54
46
  if (catalog.storage) {
55
47
  const { minGb, defaultGb, maxGb } = catalog.storage;
56
48
  this.log(`\nWorkspace (--storage-gb)\n ${minGb}–${maxGb} GiB, default ${defaultGb}; grow later, never shrink`);
@@ -64,11 +56,8 @@ class DedicatedMachinesCatalog extends dedicated_lease_command_1.DedicatedLeaseC
64
56
  if (catalog.regionClasses.length) {
65
57
  this.log(`\nRegion (--region-class)\n ${catalog.regionClasses.join(', ')}`);
66
58
  }
67
- this.log('\nPrices');
68
- if (catalog.priceLines.length === 0)
69
- this.log(' not published by this server');
70
- for (const line of catalog.priceLines) {
71
- this.log(` ${line.display || `${(0, dedicated_machines_1.formatUsdCents)(line.amountCents)} ${line.label || ''}`}`);
59
+ for (const line of (0, dedicated_machines_1.dedicatedCatalogPriceLineOutput)(catalog.priceLines, catalog.priceBookVersion)) {
60
+ this.log(line);
72
61
  }
73
62
  }
74
63
  }
@@ -17,6 +17,14 @@ export default class DedicatedMachinesCreate extends DedicatedLeaseCommand {
17
17
  workspace: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
18
18
  'request-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
19
19
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
+ yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
+ 'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
22
  };
21
23
  run(): Promise<void>;
24
+ /**
25
+ * The catalog for the summary, or null. Not through `callDedicated`, which
26
+ * exits on an API error: an unreachable catalog must cost the summary its
27
+ * detail, not stop a create the user is about to confirm anyway.
28
+ */
29
+ private readCatalogForSummary;
22
30
  }
@@ -4,12 +4,14 @@ const core_1 = require("@oclif/core");
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const dedicated_lease_command_1 = require("../../../lib/dedicated-lease-command");
6
6
  const dedicated_machines_1 = require("../../../lib/dedicated-machines");
7
+ const harnesses_1 = require("../../../lib/harnesses");
7
8
  class DedicatedMachinesCreate extends dedicated_lease_command_1.DedicatedLeaseCommand {
8
9
  static description = 'Request a Dedicated Runtime lease. See `machines dedicated catalog` for sizes, storage bounds and prices.';
9
10
  static examples = [
10
11
  '<%= config.bin %> machines dedicated create --name dev-box --size-preset standard',
11
12
  '<%= config.bin %> machines dedicated create --name dev-box --size-preset standard --spending-limit-cents 20000',
12
- '<%= config.bin %> machines dedicated create --name dev-box --size-preset standard --spending-limit-cents 20000 --repository acme/app --wait --json',
13
+ '<%= config.bin %> machines dedicated create --name dev-box --size-preset standard --dry-run',
14
+ '<%= config.bin %> machines dedicated create --name dev-box --size-preset standard --spending-limit-cents 20000 --repository acme/app --yes --wait --json',
13
15
  ];
14
16
  static flags = {
15
17
  name: core_1.Flags.string({ description: 'Display name for the dedicated machine', required: true }),
@@ -55,6 +57,13 @@ class DedicatedMachinesCreate extends dedicated_lease_command_1.DedicatedLeaseCo
55
57
  description: 'Idempotency key. Reuse it to safely retry the same create request.',
56
58
  }),
57
59
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
60
+ yes: core_1.Flags.boolean({
61
+ char: 'y',
62
+ description: 'Skip the confirmation. A create starts billing, so it asks first; required when not at a terminal.',
63
+ }),
64
+ 'dry-run': core_1.Flags.boolean({
65
+ description: 'Show the size, price, storage, retention and cap this create would use, and create nothing',
66
+ }),
58
67
  ...dedicated_lease_command_1.dedicatedWaitFlags,
59
68
  };
60
69
  async run() {
@@ -76,6 +85,40 @@ class DedicatedMachinesCreate extends dedicated_lease_command_1.DedicatedLeaseCo
76
85
  repositoryRef: flags.repository,
77
86
  workspaceId: flags.workspace,
78
87
  });
88
+ // Say what is about to be bought before it is, in the catalog's own numbers.
89
+ // `destroy` has always asked first; the command that starts a meter did not
90
+ // (OSK-9592). A catalog read that fails only loses the summary's detail,
91
+ // never the confirmation itself.
92
+ if (flags['dry-run'] || !flags.yes) {
93
+ const catalog = await this.readCatalogForSummary();
94
+ const summary = (0, dedicated_machines_1.dedicatedCreateSummary)(catalog, {
95
+ name: flags.name,
96
+ sizePreset: flags['size-preset'],
97
+ storageGb: flags['storage-gb'],
98
+ retentionClass: flags['retention-class'],
99
+ spendingLimitCents: flags['spending-limit-cents'],
100
+ });
101
+ if (flags['dry-run']) {
102
+ if (flags.json) {
103
+ this.log(JSON.stringify({ dryRun: true, request: body, summary }, null, 2));
104
+ }
105
+ else {
106
+ for (const line of summary)
107
+ this.log(line);
108
+ this.log('Dry run: nothing was created.');
109
+ }
110
+ return;
111
+ }
112
+ if (!process.stdin.isTTY) {
113
+ this.error('Creating a Dedicated Runtime starts billing. Pass --yes to confirm when not at a terminal, or --dry-run to see what it would create.', { exit: 2 });
114
+ }
115
+ for (const line of summary)
116
+ this.log(line);
117
+ if (!(await this.confirm('Create it? [y/N] '))) {
118
+ this.log('Aborted. Nothing was created.');
119
+ this.exit(1);
120
+ }
121
+ }
79
122
  const response = await this.callDedicated(() => (0, dedicated_machines_1.createDedicatedRuntime)(body));
80
123
  const lease = response?.lease;
81
124
  const id = (0, dedicated_machines_1.dedicatedLeaseId)(lease);
@@ -97,6 +140,20 @@ class DedicatedMachinesCreate extends dedicated_lease_command_1.DedicatedLeaseCo
97
140
  this.log(`Poll: ${this.config.bin} machines dedicated show ${id} (or pass --wait)`);
98
141
  }
99
142
  }
143
+ /**
144
+ * The catalog for the summary, or null. Not through `callDedicated`, which
145
+ * exits on an API error: an unreachable catalog must cost the summary its
146
+ * detail, not stop a create the user is about to confirm anyway.
147
+ */
148
+ async readCatalogForSummary() {
149
+ try {
150
+ return (0, dedicated_machines_1.dedicatedCatalogFromProducts)(await (0, harnesses_1.listHarnessProducts)());
151
+ }
152
+ catch (error) {
153
+ process.stderr.write(`Could not read the catalog for the summary (${error?.message || 'unknown error'}); showing what was passed.\n`);
154
+ return null;
155
+ }
156
+ }
100
157
  }
101
158
  exports.default = DedicatedMachinesCreate;
102
159
  function newRequestId() {
@@ -45,12 +45,17 @@ class DedicatedMachinesDestroy extends dedicated_lease_command_1.DedicatedLeaseC
45
45
  // so it does not ask. This mirrors the server's rule only to decide whether
46
46
  // to PROMPT; the server still decides what happens.
47
47
  let willPurge = Boolean(flags['purge-storage']);
48
+ // Read even under --yes when the workspace may be kept: whether a destroy
49
+ // keeps billing storage is worth saying afterwards, and only the lease as it
50
+ // stood before the destroy can say it.
51
+ const prior = willPurge
52
+ ? undefined
53
+ : (await this.callDedicated(() => (0, dedicated_machines_1.getDedicatedRuntime)(leaseId, this.scopeFrom(flags))))
54
+ ?.lease;
48
55
  if (!willPurge && !flags.yes) {
49
- const current = await this.callDedicated(() => (0, dedicated_machines_1.getDedicatedRuntime)(leaseId, this.scopeFrom(flags)));
50
- const lease = current?.lease;
51
56
  willPurge =
52
- lease?.state === 'archived' ||
53
- (!flags['retain-storage'] && (lease?.storage?.retentionClass || 'none') === 'none');
57
+ prior?.state === 'archived' ||
58
+ (!flags['retain-storage'] && (prior?.storage?.retentionClass || 'none') === 'none');
54
59
  }
55
60
  if (willPurge && !flags.yes) {
56
61
  if (!process.stdin.isTTY) {
@@ -69,6 +74,15 @@ class DedicatedMachinesDestroy extends dedicated_lease_command_1.DedicatedLeaseC
69
74
  retentionIntent = 'retain';
70
75
  const response = await this.callDedicated(() => (0, dedicated_machines_1.destroyDedicatedRuntime)(leaseId, { ...this.scopeFrom(flags), retentionIntent }));
71
76
  await this.reportLeaseAction(response, flags, { kind: 'destroyed' }, 'Destroying');
77
+ if (!flags.json) {
78
+ const note = (0, dedicated_machines_1.dedicatedDestroyRetentionNote)(prior, {
79
+ purge: Boolean(flags['purge-storage']),
80
+ retain: Boolean(flags['retain-storage']),
81
+ bin: this.config.bin,
82
+ });
83
+ if (note)
84
+ this.log(note);
85
+ }
72
86
  }
73
87
  }
74
88
  exports.default = DedicatedMachinesDestroy;
@@ -40,9 +40,14 @@ class DedicatedMachinesList extends dedicated_lease_command_1.DedicatedLeaseComm
40
40
  }
41
41
  return;
42
42
  }
43
+ const scopeSpendCents = (0, dedicated_machines_1.dedicatedScopeMonthSpendCents)(leases);
44
+ if (scopeSpendCents !== null) {
45
+ this.log(`Dedicated Runtime spend this month, across every machine listed: ${(0, dedicated_machines_1.formatUsdCents)(scopeSpendCents)}`);
46
+ this.log('');
47
+ }
43
48
  (0, format_1.renderTable)(leases.map((lease) => ({
44
49
  id: (0, format_1.formatCell)((0, dedicated_machines_1.dedicatedLeaseId)(lease)),
45
- status: (0, format_1.formatCell)((0, dedicated_machines_1.dedicatedLeaseState)(lease)),
50
+ status: (0, format_1.formatCell)((0, dedicated_machines_1.dedicatedLeaseStatusCell)(lease)),
46
51
  name: (0, format_1.formatCell)((0, dedicated_machines_1.dedicatedLeaseDisplayName)(lease)),
47
52
  size: (0, format_1.formatCell)([
48
53
  lease.resources?.sizePreset,
@@ -50,17 +55,15 @@ class DedicatedMachinesList extends dedicated_lease_command_1.DedicatedLeaseComm
50
55
  ]
51
56
  .filter(Boolean)
52
57
  .join(' ') || '-'),
53
- spend: (0, format_1.formatCell)(lease.billing
54
- ? `${(0, dedicated_machines_1.formatUsdCents)(lease.billing.currentMonthSpendCents)}/${(0, dedicated_machines_1.formatUsdCents)(lease.billing.monthlySpendLimitCents)}${lease.billing.spendingLimitExceeded ? ' OVER' : ''}`
55
- : '-'),
58
+ cap: (0, format_1.formatCell)((0, dedicated_machines_1.dedicatedLeaseCapCell)(lease)),
56
59
  retention: (0, format_1.formatCell)(lease.storage?.retentionClass || '-'),
57
60
  backup: (0, format_1.formatCell)(lease.storage?.lastBackupAt || '-'),
58
61
  })), [
59
62
  { key: 'id', header: 'ID' },
60
- { key: 'status', header: 'STATUS' },
63
+ { key: 'status', header: 'STATUS', maxWidth: 28 },
61
64
  { key: 'name', header: 'NAME', maxWidth: 28 },
62
65
  { key: 'size', header: 'SIZE', maxWidth: 18 },
63
- { key: 'spend', header: 'MONTH/CAP', maxWidth: 24 },
66
+ { key: 'cap', header: 'MONTHLY CAP', maxWidth: 24 },
64
67
  { key: 'retention', header: 'RETENTION', maxWidth: 14 },
65
68
  { key: 'backup', header: 'LAST BACKUP', maxWidth: 24 },
66
69
  ], (line) => this.log(line));
@@ -61,6 +61,18 @@ class HostedMachinesList extends base_command_1.BaseCommand {
61
61
  { key: 'repos', header: 'REPOS', maxWidth: 36 },
62
62
  { key: 'preview', header: 'PREVIEW', maxWidth: 48 },
63
63
  ], (line) => this.log(line));
64
+ // How to find the same box in the other list. `machines list` identifies a
65
+ // hosted machine by `lease:<leaseId>` — a different random string from the
66
+ // `sbs_` id in the ID column above — so side by side the two commands read
67
+ // as two unrelated machines. Saying the mapping once, where both values are
68
+ // already on screen, is cheaper than a column nobody can interpret.
69
+ const withLease = sessions.filter((session) => session.machineLeaseId);
70
+ if (withLease.length > 0) {
71
+ this.log('');
72
+ this.log(`In \`${this.config.bin} machines list\` these appear under their LEASE id as ` +
73
+ `\`lease:<lease>\` — e.g. \`lease:${String(withLease[0].machineLeaseId)}\`. ` +
74
+ `\`machines show\` takes either.`);
75
+ }
64
76
  }
65
77
  /**
66
78
  * OSK-2045 — this command lists Hosted Machine *sessions* and nothing else,