@skrr-ai/cli 0.1.22 → 0.1.24

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 (281) hide show
  1. package/README.md +83 -73
  2. package/bin/dev-fallback.js +221 -0
  3. package/bin/run.js +96 -0
  4. package/dist/base-command.d.ts +15 -0
  5. package/dist/base-command.js +145 -11
  6. package/dist/commands/agents/attach-harness.d.ts +37 -0
  7. package/dist/commands/agents/attach-harness.js +102 -3
  8. package/dist/commands/agents/chat.d.ts +69 -1
  9. package/dist/commands/agents/chat.js +202 -22
  10. package/dist/commands/agents/create.d.ts +38 -0
  11. package/dist/commands/agents/create.js +95 -2
  12. package/dist/commands/agents/schedule-trace.js +11 -0
  13. package/dist/commands/agents/show.js +29 -1
  14. package/dist/commands/api-keys/create.d.ts +24 -0
  15. package/dist/commands/api-keys/create.js +80 -0
  16. package/dist/commands/api-keys/list.d.ts +11 -0
  17. package/dist/commands/api-keys/list.js +42 -0
  18. package/dist/commands/api-keys/revoke.d.ts +14 -0
  19. package/dist/commands/api-keys/revoke.js +33 -0
  20. package/dist/commands/api-keys/rotate.d.ts +19 -0
  21. package/dist/commands/api-keys/rotate.js +42 -0
  22. package/dist/commands/balance/index.d.ts +18 -0
  23. package/dist/commands/balance/index.js +29 -0
  24. package/dist/commands/balance/show.d.ts +25 -0
  25. package/dist/commands/balance/show.js +60 -0
  26. package/dist/commands/balance/statement.d.ts +19 -0
  27. package/dist/commands/balance/statement.js +52 -0
  28. package/dist/commands/browser/doctor.d.ts +11 -0
  29. package/dist/commands/browser/doctor.js +11 -0
  30. package/dist/commands/code/jobs/approve.d.ts +25 -0
  31. package/dist/commands/code/jobs/approve.js +52 -0
  32. package/dist/commands/code/jobs/cancel.d.ts +12 -0
  33. package/dist/commands/code/jobs/cancel.js +39 -0
  34. package/dist/commands/code/jobs/index.d.ts +19 -0
  35. package/dist/commands/code/jobs/index.js +38 -0
  36. package/dist/commands/code/jobs/list.d.ts +12 -0
  37. package/dist/commands/code/jobs/list.js +87 -0
  38. package/dist/commands/code/jobs/run.d.ts +32 -0
  39. package/dist/commands/code/jobs/run.js +151 -0
  40. package/dist/commands/code/jobs/show.d.ts +12 -0
  41. package/dist/commands/code/jobs/show.js +86 -0
  42. package/dist/commands/commitments/preflight.js +18 -4
  43. package/dist/commands/commitments/receipt-metrics.d.ts +39 -0
  44. package/dist/commands/commitments/receipt-metrics.js +78 -0
  45. package/dist/commands/convos/search.js +5 -5
  46. package/dist/commands/harnesses/leases/list.d.ts +3 -0
  47. package/dist/commands/harnesses/leases/list.js +59 -12
  48. package/dist/commands/harnesses/leases/show.js +36 -0
  49. package/dist/commands/harnesses/list.d.ts +18 -0
  50. package/dist/commands/harnesses/list.js +30 -0
  51. package/dist/commands/harnesses/products.js +49 -3
  52. package/dist/commands/harnesses/quota.d.ts +16 -0
  53. package/dist/commands/harnesses/quota.js +26 -1
  54. package/dist/commands/harnesses/usage.js +17 -0
  55. package/dist/commands/inbox/index.d.ts +2 -1
  56. package/dist/commands/inbox/index.js +27 -4
  57. package/dist/commands/login.js +4 -1
  58. package/dist/commands/machines/dedicated/create.d.ts +18 -0
  59. package/dist/commands/machines/dedicated/create.js +90 -0
  60. package/dist/commands/machines/dedicated/destroy.d.ts +14 -0
  61. package/dist/commands/machines/dedicated/destroy.js +56 -0
  62. package/dist/commands/machines/dedicated/grow.d.ts +13 -0
  63. package/dist/commands/machines/dedicated/grow.js +46 -0
  64. package/dist/commands/machines/dedicated/health-check.d.ts +12 -0
  65. package/dist/commands/machines/dedicated/health-check.js +42 -0
  66. package/dist/commands/machines/dedicated/incident-close.d.ts +12 -0
  67. package/dist/commands/machines/dedicated/incident-close.js +42 -0
  68. package/dist/commands/machines/dedicated/index.d.ts +6 -0
  69. package/dist/commands/machines/dedicated/index.js +39 -0
  70. package/dist/commands/machines/dedicated/list.d.ts +11 -0
  71. package/dist/commands/machines/dedicated/list.js +58 -0
  72. package/dist/commands/machines/dedicated/restart.d.ts +12 -0
  73. package/dist/commands/machines/dedicated/restart.js +42 -0
  74. package/dist/commands/machines/dedicated/show.d.ts +12 -0
  75. package/dist/commands/machines/dedicated/show.js +58 -0
  76. package/dist/commands/machines/dedicated/snapshot.d.ts +12 -0
  77. package/dist/commands/machines/dedicated/snapshot.js +42 -0
  78. package/dist/commands/machines/dedicated/start.d.ts +12 -0
  79. package/dist/commands/machines/dedicated/start.js +42 -0
  80. package/dist/commands/machines/dedicated/stop.d.ts +12 -0
  81. package/dist/commands/machines/dedicated/stop.js +42 -0
  82. package/dist/commands/machines/hosted/index.js +4 -1
  83. package/dist/commands/machines/list.d.ts +1 -0
  84. package/dist/commands/machines/list.js +33 -5
  85. package/dist/commands/memory/approvals/issue.d.ts +18 -0
  86. package/dist/commands/memory/approvals/issue.js +63 -0
  87. package/dist/commands/memory/commit.js +5 -2
  88. package/dist/commands/memory/context.d.ts +3 -2
  89. package/dist/commands/memory/context.js +9 -11
  90. package/dist/commands/memory/delete.d.ts +1 -1
  91. package/dist/commands/memory/list.d.ts +1 -1
  92. package/dist/commands/memory/list.js +1 -1
  93. package/dist/commands/memory/mounts/resolve.d.ts +10 -0
  94. package/dist/commands/memory/mounts/resolve.js +27 -0
  95. package/dist/commands/memory/proposals/commit.d.ts +18 -0
  96. package/dist/commands/memory/proposals/commit.js +32 -0
  97. package/dist/commands/memory/proposals/create.d.ts +18 -0
  98. package/dist/commands/memory/proposals/create.js +56 -0
  99. package/dist/commands/memory/proposals/list.d.ts +13 -0
  100. package/dist/commands/memory/proposals/list.js +31 -0
  101. package/dist/commands/memory/proposals/promote.d.ts +22 -0
  102. package/dist/commands/memory/proposals/promote.js +33 -0
  103. package/dist/commands/memory/proposals/reject.d.ts +16 -0
  104. package/dist/commands/memory/proposals/reject.js +28 -0
  105. package/dist/commands/memory/rebuild-index.d.ts +20 -0
  106. package/dist/commands/memory/rebuild-index.js +25 -0
  107. package/dist/commands/memory/save.d.ts +1 -1
  108. package/dist/commands/memory/save.js +1 -1
  109. package/dist/commands/memory/search.d.ts +6 -3
  110. package/dist/commands/memory/search.js +27 -3
  111. package/dist/commands/memory/show.d.ts +1 -1
  112. package/dist/commands/memory/stores/archive.d.ts +14 -0
  113. package/dist/commands/memory/stores/archive.js +31 -0
  114. package/dist/commands/memory/stores/create.d.ts +12 -0
  115. package/dist/commands/memory/stores/create.js +30 -0
  116. package/dist/commands/memory/stores/delete.d.ts +15 -0
  117. package/dist/commands/memory/stores/delete.js +38 -0
  118. package/dist/commands/memory/stores/list.d.ts +10 -0
  119. package/dist/commands/memory/stores/list.js +29 -0
  120. package/dist/commands/memory/stores/show.d.ts +12 -0
  121. package/dist/commands/memory/stores/show.js +14 -0
  122. package/dist/commands/memory/stores/update.d.ts +15 -0
  123. package/dist/commands/memory/stores/update.js +39 -0
  124. package/dist/commands/memory/sync/delete.d.ts +13 -0
  125. package/dist/commands/memory/sync/delete.js +35 -0
  126. package/dist/commands/memory/sync/pull.d.ts +19 -0
  127. package/dist/commands/memory/sync/pull.js +52 -0
  128. package/dist/commands/memory/sync/push.d.ts +18 -0
  129. package/dist/commands/memory/sync/push.js +115 -0
  130. package/dist/commands/memory/sync/resolve.d.ts +23 -0
  131. package/dist/commands/memory/sync/resolve.js +27 -0
  132. package/dist/commands/memory/sync/status.d.ts +9 -0
  133. package/dist/commands/memory/sync/status.js +22 -0
  134. package/dist/commands/memory/topics/archive.d.ts +20 -0
  135. package/dist/commands/memory/topics/archive.js +29 -0
  136. package/dist/commands/memory/topics/create.d.ts +23 -0
  137. package/dist/commands/memory/topics/create.js +46 -0
  138. package/dist/commands/memory/topics/delete.d.ts +22 -0
  139. package/dist/commands/memory/topics/delete.js +30 -0
  140. package/dist/commands/memory/topics/forget.d.ts +22 -0
  141. package/dist/commands/memory/topics/forget.js +29 -0
  142. package/dist/commands/memory/topics/list.d.ts +13 -0
  143. package/dist/commands/memory/topics/list.js +22 -0
  144. package/dist/commands/memory/topics/move.d.ts +22 -0
  145. package/dist/commands/memory/topics/move.js +26 -0
  146. package/dist/commands/memory/topics/show.d.ts +17 -0
  147. package/dist/commands/memory/topics/show.js +21 -0
  148. package/dist/commands/memory/topics/update.d.ts +23 -0
  149. package/dist/commands/memory/topics/update.js +27 -0
  150. package/dist/commands/memory/tree.d.ts +5 -2
  151. package/dist/commands/memory/tree.js +20 -2
  152. package/dist/commands/memory/update.d.ts +1 -1
  153. package/dist/commands/memory/versions/list.d.ts +16 -0
  154. package/dist/commands/memory/versions/list.js +22 -0
  155. package/dist/commands/memory/versions/redact.d.ts +21 -0
  156. package/dist/commands/memory/versions/redact.js +22 -0
  157. package/dist/commands/memory/versions/restore.d.ts +20 -0
  158. package/dist/commands/memory/versions/restore.js +16 -0
  159. package/dist/commands/memory/versions/show.d.ts +15 -0
  160. package/dist/commands/memory/versions/show.js +17 -0
  161. package/dist/commands/messages/list.js +25 -8
  162. package/dist/commands/messages/show.js +10 -0
  163. package/dist/commands/spaces/update.js +16 -12
  164. package/dist/commands/spaces/work-sync/reconcile.d.ts +38 -0
  165. package/dist/commands/spaces/work-sync/reconcile.js +90 -0
  166. package/dist/commands/spaces/work-sync/telemetry.d.ts +29 -0
  167. package/dist/commands/spaces/work-sync/telemetry.js +96 -0
  168. package/dist/commands/store/install.js +5 -1
  169. package/dist/commands/store/releases.js +1 -1
  170. package/dist/commands/store/update.js +5 -1
  171. package/dist/commands/subscriptions/cancel.js +5 -1
  172. package/dist/commands/subscriptions/health.js +5 -1
  173. package/dist/commands/subscriptions/status.js +5 -1
  174. package/dist/commands/subscriptions/subscribe.js +5 -1
  175. package/dist/commands/tasks/activity.d.ts +2 -3
  176. package/dist/commands/tasks/activity.js +20 -6
  177. package/dist/commands/tasks/complete.d.ts +22 -150
  178. package/dist/commands/tasks/complete.js +174 -599
  179. package/dist/commands/tasks/create.d.ts +6 -0
  180. package/dist/commands/tasks/create.js +114 -4
  181. package/dist/commands/tasks/deliverable/add.d.ts +8 -0
  182. package/dist/commands/tasks/deliverable/add.js +62 -11
  183. package/dist/commands/tasks/deliverable/confirm.d.ts +5 -5
  184. package/dist/commands/tasks/deliverable/confirm.js +9 -18
  185. package/dist/commands/tasks/deliverable/list.d.ts +12 -0
  186. package/dist/commands/tasks/deliverable/list.js +58 -0
  187. package/dist/commands/tasks/events/append.d.ts +2 -0
  188. package/dist/commands/tasks/events/append.js +41 -6
  189. package/dist/commands/tasks/events/list.d.ts +1 -0
  190. package/dist/commands/tasks/events/list.js +1 -0
  191. package/dist/commands/tasks/events/tail.d.ts +1 -0
  192. package/dist/commands/tasks/events/tail.js +1 -0
  193. package/dist/commands/tasks/expectations/assess.d.ts +21 -0
  194. package/dist/commands/tasks/expectations/assess.js +103 -0
  195. package/dist/commands/tasks/expectations.js +121 -25
  196. package/dist/commands/tasks/judge.d.ts +1 -0
  197. package/dist/commands/tasks/judge.js +2 -1
  198. package/dist/commands/tasks/output.d.ts +1 -11
  199. package/dist/commands/tasks/output.js +80 -141
  200. package/dist/commands/tasks/report.d.ts +3 -3
  201. package/dist/commands/tasks/report.js +66 -41
  202. package/dist/commands/tasks/resource/add.d.ts +23 -0
  203. package/dist/commands/tasks/resource/add.js +76 -0
  204. package/dist/commands/tasks/resource/list.d.ts +13 -0
  205. package/dist/commands/tasks/resource/list.js +41 -0
  206. package/dist/commands/tasks/result/show.d.ts +13 -0
  207. package/dist/commands/tasks/result/show.js +64 -0
  208. package/dist/commands/tasks/result/submit.d.ts +26 -0
  209. package/dist/commands/tasks/result/submit.js +134 -0
  210. package/dist/commands/tasks/resume/save.d.ts +16 -0
  211. package/dist/commands/tasks/resume/save.js +69 -0
  212. package/dist/commands/tasks/resume/show.d.ts +13 -0
  213. package/dist/commands/tasks/resume/show.js +35 -0
  214. package/dist/commands/tasks/review.d.ts +17 -0
  215. package/dist/commands/tasks/review.js +54 -0
  216. package/dist/commands/tasks/runs.js +5 -0
  217. package/dist/commands/tasks/self-schedule.js +7 -4
  218. package/dist/commands/tasks/show.js +19 -0
  219. package/dist/commands/tasks/timeline.d.ts +2 -2
  220. package/dist/commands/tasks/timeline.js +19 -24
  221. package/dist/commands/tasks/updates/add.d.ts +22 -0
  222. package/dist/commands/tasks/updates/add.js +122 -0
  223. package/dist/commands/tasks/updates/list.d.ts +15 -0
  224. package/dist/commands/tasks/updates/list.js +57 -0
  225. package/dist/commands/tasks/waive.d.ts +1 -0
  226. package/dist/commands/tasks/waive.js +2 -1
  227. package/dist/lib/agentic-stream.d.ts +257 -0
  228. package/dist/lib/agentic-stream.js +490 -33
  229. package/dist/lib/balance.d.ts +8 -0
  230. package/dist/lib/commitment-product.d.ts +10 -1
  231. package/dist/lib/commitment-product.js +66 -2
  232. package/dist/lib/commitments.d.ts +2 -1
  233. package/dist/lib/commitments.js +1 -0
  234. package/dist/lib/conversation-search.d.ts +3 -0
  235. package/dist/lib/conversation-search.js +75 -0
  236. package/dist/lib/dedicated-machines.d.ts +66 -0
  237. package/dist/lib/dedicated-machines.js +161 -0
  238. package/dist/lib/execution-target.d.ts +21 -0
  239. package/dist/lib/execution-target.js +22 -6
  240. package/dist/lib/harnesses.d.ts +87 -6
  241. package/dist/lib/harnesses.js +32 -1
  242. package/dist/lib/hosted-machines.js +9 -0
  243. package/dist/lib/inbox.d.ts +2 -1
  244. package/dist/lib/machines.d.ts +17 -0
  245. package/dist/lib/machines.js +29 -0
  246. package/dist/lib/managed-agent-keys.d.ts +104 -0
  247. package/dist/lib/managed-agent-keys.js +50 -0
  248. package/dist/lib/memory-scope.d.ts +4 -3
  249. package/dist/lib/memory-scope.js +9 -9
  250. package/dist/lib/memory-sync.d.ts +114 -0
  251. package/dist/lib/memory-sync.js +730 -0
  252. package/dist/lib/message-provenance.d.ts +42 -0
  253. package/dist/lib/message-provenance.js +89 -0
  254. package/dist/lib/scoped-memory-command.d.ts +68 -0
  255. package/dist/lib/scoped-memory-command.js +183 -0
  256. package/dist/lib/session-task-endpoints.d.ts +1 -1
  257. package/dist/lib/session-task-endpoints.js +7 -0
  258. package/dist/lib/sky-code-broker.d.ts +47 -0
  259. package/dist/lib/sky-code-broker.js +50 -0
  260. package/dist/lib/sky-code-doctor.js +10 -1
  261. package/dist/lib/task-artifact-flags.d.ts +2 -0
  262. package/dist/lib/task-artifact-flags.js +7 -0
  263. package/dist/lib/task-execution.d.ts +4 -5
  264. package/dist/lib/task-execution.js +4 -5
  265. package/dist/lib/task-extras.d.ts +1 -5
  266. package/dist/lib/task-extras.js +1 -5
  267. package/dist/lib/task-legacy.d.ts +24 -0
  268. package/dist/lib/task-legacy.js +48 -0
  269. package/dist/lib/task-transcript.d.ts +5 -7
  270. package/dist/lib/task-transcript.js +8 -16
  271. package/dist/lib/tasks.d.ts +39 -21
  272. package/dist/lib/tasks.js +64 -21
  273. package/dist/lib/triggers.d.ts +39 -1
  274. package/dist/lib/triggers.js +70 -2
  275. package/dist/lib/usage-discovery.d.ts +17 -1
  276. package/dist/lib/usage-discovery.js +17 -1
  277. package/dist/lib/work-sync.d.ts +70 -0
  278. package/dist/lib/work-sync.js +29 -0
  279. package/dist/node_modules/@skrr-ai/data-provider/index.js +3760 -3621
  280. package/oclif.manifest.json +14653 -7813
  281. package/package.json +26 -5
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class CodeJobsCancel extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ job: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const base_command_1 = require("../../../base-command");
6
+ class CodeJobsCancel extends base_command_1.BaseCommand {
7
+ static description = 'Stop a Hosted Job Machine job, and its billing';
8
+ static examples = [
9
+ '<%= config.bin %> code jobs cancel <job-id>',
10
+ '<%= config.bin %> code jobs cancel <job-id> --json',
11
+ ];
12
+ static args = {
13
+ job: core_1.Args.string({ description: 'Job id', required: true, ignoreStdin: true }),
14
+ };
15
+ static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
16
+ async run() {
17
+ this.requireAuth();
18
+ const { args, flags } = await this.parse(CodeJobsCancel);
19
+ let task;
20
+ try {
21
+ const res = await data_provider_1.dataService.cancelCloudCodingTask(args.job);
22
+ task = res?.task;
23
+ }
24
+ catch (err) {
25
+ this.handleApiError(err);
26
+ }
27
+ if (flags.json) {
28
+ this.log(JSON.stringify(task, null, 2));
29
+ return;
30
+ }
31
+ // Report the status the SERVER came back with, never the one we asked for.
32
+ // Cancel races a job that is already finishing, and printing "cancelled"
33
+ // over a `completed` row would be the CLI inventing an outcome.
34
+ this.log(`Job ${task?.id ?? args.job} is now ${task?.status ?? 'unknown'}.`);
35
+ if (task?.prUrl)
36
+ this.log(` ${String(task.prUrl)}`);
37
+ }
38
+ }
39
+ exports.default = CodeJobsCancel;
@@ -0,0 +1,19 @@
1
+ import { Command } from '@oclif/core';
2
+ /**
3
+ * `skrr code jobs` — the lifecycle of work running on a Hosted Job Machine.
4
+ *
5
+ * A job is the object, not the machine. A Hosted Job Machine is one-shot
6
+ * compute that exists for the duration of one job and is torn down after it, so
7
+ * there is nothing to create, update or resume; `skrr code handover` starts one
8
+ * and these verbs observe and stop it.
9
+ *
10
+ * They exist because `POST /api/cloud-coding/tasks` had a CLI binding and the
11
+ * rest of the surface did not. An operator could dispatch a job onto managed
12
+ * cloud compute and then had no way to see whether it was running, no way to
13
+ * read why it failed, and — the one that costs money — no way to cancel it.
14
+ */
15
+ export default class CodeJobs extends Command {
16
+ static description: string;
17
+ static examples: string[];
18
+ run(): Promise<void>;
19
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ /**
5
+ * `skrr code jobs` — the lifecycle of work running on a Hosted Job Machine.
6
+ *
7
+ * A job is the object, not the machine. A Hosted Job Machine is one-shot
8
+ * compute that exists for the duration of one job and is torn down after it, so
9
+ * there is nothing to create, update or resume; `skrr code handover` starts one
10
+ * and these verbs observe and stop it.
11
+ *
12
+ * They exist because `POST /api/cloud-coding/tasks` had a CLI binding and the
13
+ * rest of the surface did not. An operator could dispatch a job onto managed
14
+ * cloud compute and then had no way to see whether it was running, no way to
15
+ * read why it failed, and — the one that costs money — no way to cancel it.
16
+ */
17
+ class CodeJobs extends core_1.Command {
18
+ static description = 'Jobs running on a Hosted Job Machine';
19
+ static examples = [
20
+ '<%= config.bin %> code jobs run --to "Analyst" --prompt "..." --wait',
21
+ '<%= config.bin %> code jobs list',
22
+ '<%= config.bin %> code jobs show <job-id>',
23
+ '<%= config.bin %> code jobs cancel <job-id>',
24
+ '<%= config.bin %> code jobs approve <job-id>',
25
+ ];
26
+ async run() {
27
+ this.log('Hosted Job Machine job commands:');
28
+ this.log(' skrr code jobs run run a prompt on a fresh machine, no repo');
29
+ this.log(' skrr code jobs list list your jobs, newest first');
30
+ this.log(' skrr code jobs show show one job and its timeline');
31
+ this.log(' skrr code jobs cancel stop a job and its billing');
32
+ this.log(' skrr code jobs approve land the pull request a repo job opened');
33
+ this.log('');
34
+ this.log('`run` needs no repository. For a job that clones a repo and opens a');
35
+ this.log('pull request, use `skrr code handover`.');
36
+ }
37
+ }
38
+ exports.default = CodeJobs;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class CodeJobsList extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ active: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ limit: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const base_command_1 = require("../../../base-command");
6
+ const format_1 = require("../../../lib/format");
7
+ /** Terminal states — a job here is no longer holding a machine or billing. */
8
+ const TERMINAL = new Set(['completed', 'failed', 'cancelled', 'timed_out']);
9
+ /** Cents are the server's unit for money everywhere else; jobs report dollars. */
10
+ function costCell(task) {
11
+ const raw = task.costUsd;
12
+ if (typeof raw !== 'number' || !Number.isFinite(raw))
13
+ return '-';
14
+ return `$${raw.toFixed(2)}`;
15
+ }
16
+ class CodeJobsList extends base_command_1.BaseCommand {
17
+ static description = 'List your Hosted Job Machine jobs, newest first';
18
+ static examples = [
19
+ '<%= config.bin %> code jobs list',
20
+ '<%= config.bin %> code jobs list --active',
21
+ '<%= config.bin %> code jobs list --status running --limit 5',
22
+ '<%= config.bin %> code jobs list --json',
23
+ ];
24
+ static flags = {
25
+ status: core_1.Flags.string({
26
+ description: 'Filter by one exact status (e.g. running, failed, completed)',
27
+ }),
28
+ // The question an operator actually opens this with is "what is costing me
29
+ // money right now", and the server has no such filter -- it takes one exact
30
+ // status. Filtering client-side is honest here because the page is already
31
+ // bounded by --limit.
32
+ active: core_1.Flags.boolean({
33
+ description: 'Show only jobs that are still running (and still billing)',
34
+ default: false,
35
+ exclusive: ['status'],
36
+ }),
37
+ limit: core_1.Flags.integer({ description: 'How many to return (1-100, default 20)' }),
38
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
39
+ };
40
+ async run() {
41
+ this.requireAuth();
42
+ const { flags } = await this.parse(CodeJobsList);
43
+ let tasks = [];
44
+ try {
45
+ const res = await data_provider_1.dataService.listCloudCodingTasks({
46
+ ...(flags.status ? { status: flags.status } : {}),
47
+ ...(flags.limit ? { limit: flags.limit } : {}),
48
+ });
49
+ tasks = res?.data ?? [];
50
+ }
51
+ catch (err) {
52
+ this.handleApiError(err);
53
+ }
54
+ if (flags.active) {
55
+ tasks = tasks.filter((t) => !TERMINAL.has(String(t.status)));
56
+ }
57
+ if (flags.json) {
58
+ this.log(JSON.stringify(tasks, null, 2));
59
+ return;
60
+ }
61
+ if (tasks.length === 0) {
62
+ // Say what empty MEANS. With --active it is the reassuring answer, not an
63
+ // empty query: nothing is running, so nothing is being billed.
64
+ this.log(flags.active
65
+ ? 'No jobs running — nothing is holding a Hosted Job Machine, and nothing is accruing.'
66
+ : 'No jobs. Start one with `skrr code jobs run`, or `skrr code handover`\n' +
67
+ 'for a job that clones a repository and opens a pull request.');
68
+ return;
69
+ }
70
+ (0, format_1.renderTable)(tasks.map((t) => ({
71
+ id: (0, format_1.formatCell)(t.id),
72
+ status: (0, format_1.formatCell)(t.status),
73
+ repo: (0, format_1.formatCell)(t.repoFullName),
74
+ cost: costCell(t),
75
+ created: (0, format_1.localStamp)(t.createdAt),
76
+ pr: (0, format_1.formatCell)(t.prUrl),
77
+ })), [
78
+ { key: 'id', header: 'JOB' },
79
+ { key: 'status', header: 'STATUS' },
80
+ { key: 'repo', header: 'REPO', maxWidth: 28 },
81
+ { key: 'cost', header: 'COST' },
82
+ { key: 'created', header: 'CREATED' },
83
+ { key: 'pr', header: 'PR', maxWidth: 40 },
84
+ ], (line) => this.log(line));
85
+ }
86
+ }
87
+ exports.default = CodeJobsList;
@@ -0,0 +1,32 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ /**
3
+ * `skrr code jobs run` — one-shot managed compute with no repository.
4
+ *
5
+ * OSK-7806. The sibling `skrr code handover` clones a GitHub repo, works it and
6
+ * opens a pull request. This runs the same Hosted Job Machine with no repo at
7
+ * all: a prompt in, a recorded result out, teardown. It is the shape for work
8
+ * that is not a code change — process some data, call an API, check something
9
+ * on a machine that is not yours and does not persist.
10
+ *
11
+ * Egress defaults to `isolated`: the control plane and nothing else. That is
12
+ * deliberate and worth knowing before you reach for `--egress`. The repository
13
+ * requirement used to be an abuse gate — burning managed compute meant first
14
+ * installing the GitHub App on a repo you own — and a job that needs no repo
15
+ * has no such gate, so its default reach must be the narrowest one that works.
16
+ */
17
+ export default class CodeJobsRun extends BaseCommand {
18
+ static description: string;
19
+ static examples: string[];
20
+ static flags: {
21
+ to: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
22
+ prompt: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
23
+ 'prompt-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
+ egress: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
25
+ wait: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
26
+ interval: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
27
+ timeout: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
28
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
29
+ };
30
+ private sleep;
31
+ run(): Promise<void>;
32
+ }
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const node_fs_1 = require("node:fs");
5
+ const node_crypto_1 = require("node:crypto");
6
+ const data_provider_1 = require("@skrr-ai/data-provider");
7
+ const base_command_1 = require("../../../base-command");
8
+ const code_handover_1 = require("../../../lib/code-handover");
9
+ const TERMINAL = new Set(['completed', 'failed', 'cancelled', 'timed_out']);
10
+ /**
11
+ * `skrr code jobs run` — one-shot managed compute with no repository.
12
+ *
13
+ * OSK-7806. The sibling `skrr code handover` clones a GitHub repo, works it and
14
+ * opens a pull request. This runs the same Hosted Job Machine with no repo at
15
+ * all: a prompt in, a recorded result out, teardown. It is the shape for work
16
+ * that is not a code change — process some data, call an API, check something
17
+ * on a machine that is not yours and does not persist.
18
+ *
19
+ * Egress defaults to `isolated`: the control plane and nothing else. That is
20
+ * deliberate and worth knowing before you reach for `--egress`. The repository
21
+ * requirement used to be an abuse gate — burning managed compute meant first
22
+ * installing the GitHub App on a repo you own — and a job that needs no repo
23
+ * has no such gate, so its default reach must be the narrowest one that works.
24
+ */
25
+ class CodeJobsRun extends base_command_1.BaseCommand {
26
+ static description = 'Run a prompt on a one-shot Hosted Job Machine, with no repository';
27
+ static examples = [
28
+ '<%= config.bin %> code jobs run --to "Analyst" --prompt "summarise the attached figures" --wait',
29
+ '<%= config.bin %> code jobs run --to agent_abc --prompt-file ./brief.md',
30
+ '<%= config.bin %> code jobs run --to agent_abc --prompt "fetch and chart" --egress allowlist --json',
31
+ ];
32
+ static flags = {
33
+ to: core_1.Flags.string({ description: 'Agent that runs the job — id or name', required: true }),
34
+ prompt: core_1.Flags.string({ description: 'What the agent should do', exclusive: ['prompt-file'] }),
35
+ 'prompt-file': core_1.Flags.string({
36
+ description: 'Read the prompt from a file (for prompts too long to type)',
37
+ exclusive: ['prompt'],
38
+ }),
39
+ egress: core_1.Flags.string({
40
+ description: 'Network reach. Default `isolated` — the control plane only, nothing else.',
41
+ options: ['isolated', 'allowlist', 'open'],
42
+ }),
43
+ wait: core_1.Flags.boolean({ description: 'Wait for the job to finish and print its result' }),
44
+ interval: core_1.Flags.integer({ description: 'Poll interval in seconds while waiting', default: 5 }),
45
+ timeout: core_1.Flags.integer({ description: 'Give up waiting after this many minutes', default: 30 }),
46
+ json: core_1.Flags.boolean({ description: 'Output the task as JSON' }),
47
+ };
48
+ async sleep(ms) {
49
+ return new Promise((resolve) => setTimeout(resolve, ms));
50
+ }
51
+ async run() {
52
+ this.requireAuth();
53
+ const { flags } = await this.parse(CodeJobsRun);
54
+ let prompt = flags.prompt ?? '';
55
+ if (flags['prompt-file']) {
56
+ try {
57
+ prompt = (0, node_fs_1.readFileSync)(flags['prompt-file'], 'utf8');
58
+ }
59
+ catch (err) {
60
+ this.error(`Could not read --prompt-file: ${err.message}`);
61
+ }
62
+ }
63
+ if (!prompt.trim()) {
64
+ this.error('A prompt is required — pass --prompt or --prompt-file.');
65
+ }
66
+ let agents = [];
67
+ try {
68
+ const resp = await data_provider_1.dataService.listAgents({ requiredPermission: data_provider_1.PermissionBits.VIEW });
69
+ agents = (resp?.data ?? []).filter(Boolean);
70
+ }
71
+ catch (err) {
72
+ this.handleApiError(err);
73
+ }
74
+ const matched = (0, code_handover_1.matchAgent)(flags.to, agents);
75
+ if (matched.kind === 'none') {
76
+ this.error(`No agent matches "${flags.to}". Run \`skrr agents list\` to see the agents you can use.`);
77
+ }
78
+ if (matched.kind === 'ambiguous') {
79
+ this.error(`"${flags.to}" matches ${matched.candidates.length} agents. Pass the agent id instead:\n` +
80
+ matched.candidates.map((a) => ` ${a.id} ${a.name ?? ''}`).join('\n'));
81
+ }
82
+ const target = matched.agent;
83
+ let task;
84
+ try {
85
+ const res = await data_provider_1.dataService.createCloudCodingTask({
86
+ agentId: target.id,
87
+ prompt,
88
+ mode: 'bare',
89
+ machineProduct: 'hosted-job-machine',
90
+ ...(flags.egress ? { egressPolicy: flags.egress } : {}),
91
+ clientRequestId: (0, node_crypto_1.randomUUID)(),
92
+ });
93
+ task = res?.task;
94
+ }
95
+ catch (err) {
96
+ this.handleApiError(err);
97
+ }
98
+ const jobId = String(task?.id ?? '');
99
+ if (!flags.wait) {
100
+ if (flags.json) {
101
+ this.log(JSON.stringify(task, null, 2));
102
+ return;
103
+ }
104
+ this.log(`Job ${jobId} ${task?.status ?? 'queued'} on ${target.name ?? target.id}`);
105
+ this.log(` ${this.config.bin} code jobs show ${jobId}`);
106
+ // It bills from here. Say the stop verb at the moment the spending starts,
107
+ // not only in the docs for a command they may never open.
108
+ this.log(` ${this.config.bin} code jobs cancel ${jobId} (it bills while it runs)`);
109
+ return;
110
+ }
111
+ const deadline = Date.now() + flags.timeout * 60_000;
112
+ let current = task;
113
+ while (!TERMINAL.has(String(current?.status)) && Date.now() < deadline) {
114
+ await this.sleep(Math.max(1, flags.interval) * 1000);
115
+ try {
116
+ current = (await data_provider_1.dataService.getCloudCodingTask(jobId))?.task;
117
+ }
118
+ catch (err) {
119
+ this.handleApiError(err);
120
+ }
121
+ }
122
+ if (flags.json) {
123
+ this.log(JSON.stringify(current, null, 2));
124
+ return;
125
+ }
126
+ if (!TERMINAL.has(String(current?.status))) {
127
+ // A timed-out WAIT is not a timed-out job. The job is still running and
128
+ // still billing, so say which of the two happened and how to stop it.
129
+ this.log(`Still ${current?.status ?? 'running'} after ${flags.timeout}m — the job continues.`);
130
+ this.log(` ${this.config.bin} code jobs show ${jobId}`);
131
+ this.log(` ${this.config.bin} code jobs cancel ${jobId}`);
132
+ return;
133
+ }
134
+ this.log(`Job ${jobId} ${current?.status}`);
135
+ const result = current?.result;
136
+ if (result?.text) {
137
+ this.log('');
138
+ this.log(result.text);
139
+ if (result.truncated) {
140
+ this.log('');
141
+ this.log(`[truncated — ${result.bytes} bytes produced; full record in --json]`);
142
+ }
143
+ }
144
+ const error = current?.error;
145
+ if (error?.code || error?.message) {
146
+ this.log('');
147
+ this.log(`Error: ${[error.code, error.message].filter(Boolean).join(': ')}`);
148
+ }
149
+ }
150
+ }
151
+ exports.default = CodeJobsRun;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class CodeJobsShow extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ job: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const base_command_1 = require("../../../base-command");
6
+ const format_1 = require("../../../lib/format");
7
+ const TERMINAL = new Set(['completed', 'failed', 'cancelled', 'timed_out']);
8
+ class CodeJobsShow extends base_command_1.BaseCommand {
9
+ static description = 'Show one Hosted Job Machine job and its timeline';
10
+ static examples = [
11
+ '<%= config.bin %> code jobs show <job-id>',
12
+ '<%= config.bin %> code jobs show <job-id> --json',
13
+ ];
14
+ static args = {
15
+ job: core_1.Args.string({ description: 'Job id', required: true, ignoreStdin: true }),
16
+ };
17
+ static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
18
+ async run() {
19
+ this.requireAuth();
20
+ const { args, flags } = await this.parse(CodeJobsShow);
21
+ let task;
22
+ try {
23
+ const res = await data_provider_1.dataService.getCloudCodingTask(args.job);
24
+ task = res?.task;
25
+ }
26
+ catch (err) {
27
+ this.handleApiError(err);
28
+ }
29
+ if (flags.json) {
30
+ this.log(JSON.stringify(task, null, 2));
31
+ return;
32
+ }
33
+ if (!task) {
34
+ this.log('Job not found.');
35
+ return;
36
+ }
37
+ const line = (label, value) => {
38
+ if (value === undefined || value === null || value === '')
39
+ return;
40
+ this.log(`${label.padEnd(14)}${String(value)}`);
41
+ };
42
+ line('ID:', task.id);
43
+ line('Status:', task.status);
44
+ line('Agent:', task.agentId);
45
+ if (task.repoFullName) {
46
+ line('Repo:', `${task.repoFullName}@${task.baseBranch ?? 'main'}`);
47
+ }
48
+ line('Work branch:', task.workBranch);
49
+ line('Machine:', task.machineProductLabel ?? task.machineProduct);
50
+ line('Lease:', task.machineLeaseId);
51
+ line('Model:', task.model);
52
+ if (typeof task.costUsd === 'number')
53
+ line('Cost:', `$${task.costUsd.toFixed(2)}`);
54
+ line('PR:', task.prUrl);
55
+ line('Preview:', task.previewUrl);
56
+ line('Created:', (0, format_1.localStamp)(task.createdAt));
57
+ const error = task.error;
58
+ if (error?.code || error?.message) {
59
+ line('Error:', [error.code, error.message].filter(Boolean).join(': '));
60
+ }
61
+ // The timeline is the closest thing to a log this surface has: the task
62
+ // persists its state transitions but not the agent's transcript, so this is
63
+ // where "what happened, and where did it stop" is answerable at all.
64
+ const timeline = Array.isArray(task.timeline) ? task.timeline : [];
65
+ if (timeline.length > 0) {
66
+ this.log('');
67
+ this.log('Timeline');
68
+ for (const entry of timeline) {
69
+ const at = entry.at ? (0, format_1.localStamp)(entry.at) : '-';
70
+ const took = typeof entry.durationMs === 'number' ? ` (${Math.round(entry.durationMs / 1000)}s)` : '';
71
+ const note = entry.note ? ` ${entry.note}` : '';
72
+ this.log(` ${at} ${String(entry.state ?? '-').padEnd(14)}${took}${note}`);
73
+ }
74
+ }
75
+ // A non-terminal job is still holding a machine. Say so, and say the verb —
76
+ // this is the whole reason the cancel binding exists.
77
+ if (!TERMINAL.has(String(task.status))) {
78
+ this.log('');
79
+ this.log(`Still running — it holds a machine and is billing. Stop it with:`);
80
+ this.log(` ${this.config.bin} code jobs cancel ${task.id}`);
81
+ }
82
+ this.log('');
83
+ this.log('Full record: --json');
84
+ }
85
+ }
86
+ exports.default = CodeJobsShow;
@@ -24,12 +24,26 @@ const prompt_1 = require("../../lib/prompt");
24
24
  */
25
25
  function preflightGateLines(gate, verbose = false) {
26
26
  const mark = gate.verdict === 'pass' ? 'ok' : gate.verdict === 'block' ? 'BLOCK' : 'warn';
27
+ // A BLOCKING gate always states its reason. `ok` gates got a helpful `note`
28
+ // and `warn` gates got a reason token, and the BLOCK gates — the only ones
29
+ // that stop the run — got the label and nothing: two of them side by side,
30
+ // one meaning "still a draft" (completely benign) and one meaning "this
31
+ // runtime cannot enforce the contract", rendered identically. The reasons
32
+ // were in the `--json` payload the whole time; the plain renderer just
33
+ // dropped `reason` for exactly the verdict that needs it (OSK-7700).
34
+ //
35
+ // A gate that HAS a note keeps the reason behind `--verbose`, because the
36
+ // note is the same fact in words a reader can act on and the token beside it
37
+ // is then just noise: "Will the platform wake it? — status_paused" above
38
+ // "Resume it first." says one thing twice, in two vocabularies.
39
+ const note = gate.detail?.note;
40
+ const hasNote = typeof note === 'string' && Boolean(note.trim());
41
+ const showReason = verbose || (gate.verdict === 'block' && !hasNote);
27
42
  const lines = [
28
- ` ${mark.padEnd(5)} ${gate.label}${verbose && gate.reason ? ` — ${gate.reason}` : ''}`,
43
+ ` ${mark.padEnd(5)} ${gate.label}${showReason && gate.reason ? ` — ${gate.reason}` : ''}`,
29
44
  ];
30
- const note = gate.detail?.note;
31
- if (typeof note === 'string' && note.trim()) {
32
- lines.push(` ${note.trim()}`);
45
+ if (hasNote) {
46
+ lines.push(` ${String(note).trim()}`);
33
47
  }
34
48
  // `detail.hint` is the actionable plain-words sentence — why the gate is
35
49
  // capped and the exact command to lift it. It lived only in --json, so the
@@ -0,0 +1,39 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * The quarter to report when the caller names none.
4
+ *
5
+ * Computed in **UTC**, deliberately. The server buckets a quarter on UTC
6
+ * boundaries, so a local-time computation would disagree with it for a few
7
+ * hours either side of a quarter change — `skrr` would ask for Q3 and the
8
+ * server would answer about the Q4 it thinks we are in. Same clock, no seam.
9
+ *
10
+ * The previous calendar quarter is always closed, which is the cohort this
11
+ * endpoint reports on (`scope: 'closed_quarter'`); the current one is not, so
12
+ * it is never a safe default.
13
+ */
14
+ export declare function previousClosedQuarter(now?: Date): {
15
+ year: number;
16
+ quarter: number;
17
+ };
18
+ /**
19
+ * `skrr commitments receipt-metrics <id>` — the closed-quarter rollup behind
20
+ * `commitments receipt`.
21
+ *
22
+ * `receipt` explains ONE wake. This aggregates a whole closed quarter, which is
23
+ * the window you need to answer "is this commitment actually delivering" rather
24
+ * than "what happened last night".
25
+ */
26
+ export default class CommitmentsReceiptMetrics extends BaseCommand {
27
+ static description: string;
28
+ static examples: string[];
29
+ static args: {
30
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
31
+ };
32
+ static flags: {
33
+ year: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
34
+ quarter: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
35
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
36
+ verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
37
+ };
38
+ run(): Promise<void>;
39
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.previousClosedQuarter = previousClosedQuarter;
4
+ const core_1 = require("@oclif/core");
5
+ const base_command_1 = require("../../base-command");
6
+ const commitments_1 = require("../../lib/commitments");
7
+ const commitment_product_1 = require("../../lib/commitment-product");
8
+ /**
9
+ * The quarter to report when the caller names none.
10
+ *
11
+ * Computed in **UTC**, deliberately. The server buckets a quarter on UTC
12
+ * boundaries, so a local-time computation would disagree with it for a few
13
+ * hours either side of a quarter change — `skrr` would ask for Q3 and the
14
+ * server would answer about the Q4 it thinks we are in. Same clock, no seam.
15
+ *
16
+ * The previous calendar quarter is always closed, which is the cohort this
17
+ * endpoint reports on (`scope: 'closed_quarter'`); the current one is not, so
18
+ * it is never a safe default.
19
+ */
20
+ function previousClosedQuarter(now = new Date()) {
21
+ const quarter = Math.floor(now.getUTCMonth() / 3) + 1;
22
+ if (quarter === 1)
23
+ return { year: now.getUTCFullYear() - 1, quarter: 4 };
24
+ return { year: now.getUTCFullYear(), quarter: quarter - 1 };
25
+ }
26
+ /**
27
+ * `skrr commitments receipt-metrics <id>` — the closed-quarter rollup behind
28
+ * `commitments receipt`.
29
+ *
30
+ * `receipt` explains ONE wake. This aggregates a whole closed quarter, which is
31
+ * the window you need to answer "is this commitment actually delivering" rather
32
+ * than "what happened last night".
33
+ */
34
+ class CommitmentsReceiptMetrics extends base_command_1.BaseCommand {
35
+ static description = "Show a closed quarter's execution receipt metrics for a commitment";
36
+ static examples = [
37
+ '<%= config.bin %> commitments receipt-metrics obj_abc123',
38
+ '<%= config.bin %> commitments receipt-metrics obj_abc123 --year 2026 --quarter 2',
39
+ '<%= config.bin %> commitments receipt-metrics obj_abc123 --verbose',
40
+ '<%= config.bin %> commitments receipt-metrics obj_abc123 --json',
41
+ ];
42
+ static args = {
43
+ id: core_1.Args.string({ description: 'Commitment ID', required: true, ignoreStdin: true }),
44
+ };
45
+ static flags = {
46
+ year: core_1.Flags.integer({ description: 'Calendar year (defaults to the last closed quarter)' }),
47
+ quarter: core_1.Flags.integer({
48
+ description: 'Quarter 1-4 (defaults to the last closed quarter)',
49
+ min: 1,
50
+ max: 4,
51
+ }),
52
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
53
+ verbose: core_1.Flags.boolean({ description: 'Include the measurement basis behind the rates' }),
54
+ };
55
+ async run() {
56
+ this.requireAuth();
57
+ const { args, flags } = await this.parse(CommitmentsReceiptMetrics);
58
+ // Both or neither. Filling in half of a caller's intent would silently
59
+ // report on a quarter they did not ask for — and the numbers would look
60
+ // perfectly plausible, which is the kind of wrong that gets acted on.
61
+ if ((flags.year === undefined) !== (flags.quarter === undefined)) {
62
+ this.error('Pass --year and --quarter together, or neither to use the last closed quarter.');
63
+ }
64
+ const fallback = previousClosedQuarter();
65
+ const year = flags.year ?? fallback.year;
66
+ const quarter = flags.quarter ?? fallback.quarter;
67
+ try {
68
+ const response = await commitments_1.commitmentApi.receiptQuarterlyMetrics(args.id, year, quarter);
69
+ if (flags.json)
70
+ return this.log(JSON.stringify(response, null, 2));
71
+ (0, commitment_product_1.renderExecutionReceiptQuarterlyMetrics)(response, (line) => this.log(line), flags.verbose);
72
+ }
73
+ catch (error) {
74
+ this.handleApiError(error);
75
+ }
76
+ }
77
+ }
78
+ exports.default = CommitmentsReceiptMetrics;