@skrr-ai/cli 0.1.94 → 0.1.96

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 (298) hide show
  1. package/README.md +175 -165
  2. package/bin/run.js +32 -0
  3. package/bin/update-npm-runner.cjs +33 -0
  4. package/bin/update-session.cjs +194 -0
  5. package/bin/update-worker.cjs +208 -0
  6. package/dist/base-command.js +0 -15
  7. package/dist/commands/agent/message/send.d.ts +2 -0
  8. package/dist/commands/agent/message/send.js +75 -5
  9. package/dist/commands/agents/chat.js +1 -1
  10. package/dist/commands/agents/create.d.ts +0 -3
  11. package/dist/commands/agents/create.js +0 -26
  12. package/dist/commands/agents/egress.d.ts +17 -0
  13. package/dist/commands/agents/egress.js +84 -0
  14. package/dist/commands/agents/mailbox/messages.d.ts +14 -0
  15. package/dist/commands/agents/mailbox/messages.js +52 -0
  16. package/dist/commands/agents/mailbox/show.d.ts +13 -0
  17. package/dist/commands/agents/mailbox/show.js +52 -0
  18. package/dist/commands/agents/operating-plan.js +8 -0
  19. package/dist/commands/agents/roles/capture.d.ts +16 -0
  20. package/dist/commands/agents/roles/capture.js +53 -0
  21. package/dist/commands/agents/roles/install.d.ts +1 -0
  22. package/dist/commands/agents/roles/install.js +8 -1
  23. package/dist/commands/agents/roles/preview.d.ts +2 -0
  24. package/dist/commands/agents/roles/preview.js +27 -3
  25. package/dist/commands/agents/update.d.ts +0 -3
  26. package/dist/commands/agents/update.js +1 -31
  27. package/dist/commands/agents/usage-cap.d.ts +4 -1
  28. package/dist/commands/agents/usage-cap.js +49 -10
  29. package/dist/commands/code/doctor.d.ts +1 -0
  30. package/dist/commands/code/doctor.js +6 -0
  31. package/dist/commands/code/index.js +4 -0
  32. package/dist/commands/code/install.d.ts +5 -62
  33. package/dist/commands/code/install.js +86 -279
  34. package/dist/commands/code/jobs/show.js +12 -0
  35. package/dist/commands/code/rollback.d.ts +8 -0
  36. package/dist/commands/code/rollback.js +20 -0
  37. package/dist/commands/code/update.d.ts +8 -37
  38. package/dist/commands/code/update.js +134 -213
  39. package/dist/commands/commitments/action-proposals/decide.d.ts +2 -0
  40. package/dist/commands/commitments/action-proposals/decide.js +12 -0
  41. package/dist/commands/commitments/action-proposals.js +7 -0
  42. package/dist/commands/commitments/create.d.ts +1 -0
  43. package/dist/commands/commitments/create.js +17 -1
  44. package/dist/commands/commitments/delivery.d.ts +1 -0
  45. package/dist/commands/commitments/delivery.js +11 -1
  46. package/dist/commands/commitments/doctor.js +4 -0
  47. package/dist/commands/commitments/effective-policy.js +26 -0
  48. package/dist/commands/commitments/endpoints/create.js +5 -2
  49. package/dist/commands/commitments/observations.d.ts +10 -0
  50. package/dist/commands/commitments/observations.js +88 -0
  51. package/dist/commands/commitments/pause.d.ts +1 -1
  52. package/dist/commands/commitments/pause.js +4 -3
  53. package/dist/commands/commitments/preflight.js +2 -1
  54. package/dist/commands/commitments/reports.d.ts +2 -0
  55. package/dist/commands/commitments/reports.js +22 -4
  56. package/dist/commands/commitments/update.d.ts +1 -0
  57. package/dist/commands/commitments/update.js +35 -5
  58. package/dist/commands/daemon/install.js +4 -0
  59. package/dist/commands/generate-recovery-code.d.ts +13 -1
  60. package/dist/commands/generate-recovery-code.js +146 -13
  61. package/dist/commands/grants/create.d.ts +1 -0
  62. package/dist/commands/grants/create.js +8 -0
  63. package/dist/commands/grants/rotate.d.ts +5 -0
  64. package/dist/commands/grants/rotate.js +13 -1
  65. package/dist/commands/grants/update.d.ts +12 -0
  66. package/dist/commands/grants/update.js +27 -0
  67. package/dist/commands/instructions/guide.d.ts +20 -0
  68. package/dist/commands/instructions/guide.js +84 -0
  69. package/dist/commands/instructions/install.js +57 -33
  70. package/dist/commands/instructions/setup.d.ts +14 -0
  71. package/dist/commands/instructions/setup.js +72 -0
  72. package/dist/commands/integrations/execute.js +1 -1
  73. package/dist/commands/login.d.ts +5 -6
  74. package/dist/commands/login.js +27 -11
  75. package/dist/commands/logout.js +7 -7
  76. package/dist/commands/machines/hosted/start.js +7 -1
  77. package/dist/commands/machines/list.js +4 -4
  78. package/dist/commands/measures/confirm.d.ts +1 -7
  79. package/dist/commands/measures/confirm.js +15 -14
  80. package/dist/commands/measures/list.d.ts +1 -7
  81. package/dist/commands/measures/list.js +1 -7
  82. package/dist/commands/measures/record.js +4 -4
  83. package/dist/commands/measures/show.d.ts +11 -0
  84. package/dist/commands/measures/show.js +26 -0
  85. package/dist/commands/northstars/governance/configure.d.ts +1 -0
  86. package/dist/commands/northstars/governance/configure.js +8 -1
  87. package/dist/commands/proposals/discard.d.ts +5 -0
  88. package/dist/commands/proposals/discard.js +8 -0
  89. package/dist/commands/proposals/keep.d.ts +5 -0
  90. package/dist/commands/proposals/keep.js +8 -0
  91. package/dist/commands/proposals/list.d.ts +15 -0
  92. package/dist/commands/proposals/list.js +74 -0
  93. package/dist/commands/proposals/show.d.ts +12 -0
  94. package/dist/commands/proposals/show.js +27 -0
  95. package/dist/commands/proposals/snooze.d.ts +5 -0
  96. package/dist/commands/proposals/snooze.js +8 -0
  97. package/dist/commands/security/overview.d.ts +11 -0
  98. package/dist/commands/security/overview.js +158 -0
  99. package/dist/commands/signals/monitors/create.d.ts +9 -0
  100. package/dist/commands/signals/monitors/create.js +29 -0
  101. package/dist/commands/signals/monitors/list.d.ts +8 -0
  102. package/dist/commands/signals/monitors/list.js +16 -0
  103. package/dist/commands/signals/monitors/pause.d.ts +11 -0
  104. package/dist/commands/signals/monitors/pause.js +17 -0
  105. package/dist/commands/signals/monitors/resume.d.ts +11 -0
  106. package/dist/commands/signals/monitors/resume.js +17 -0
  107. package/dist/commands/signals/monitors/revise.d.ts +12 -0
  108. package/dist/commands/signals/monitors/revise.js +25 -0
  109. package/dist/commands/signals/monitors/show.d.ts +11 -0
  110. package/dist/commands/signals/monitors/show.js +17 -0
  111. package/dist/commands/signals/monitors/test.d.ts +12 -0
  112. package/dist/commands/signals/monitors/test.js +25 -0
  113. package/dist/commands/signals/monitors/transitions.d.ts +11 -0
  114. package/dist/commands/signals/monitors/transitions.js +17 -0
  115. package/dist/commands/signals/monitors/validate.d.ts +9 -0
  116. package/dist/commands/signals/monitors/validate.js +24 -0
  117. package/dist/commands/signals/schemas/list.d.ts +8 -0
  118. package/dist/commands/signals/schemas/list.js +16 -0
  119. package/dist/commands/signals/schemas/show.d.ts +11 -0
  120. package/dist/commands/signals/schemas/show.js +17 -0
  121. package/dist/commands/signals/sources/list.d.ts +8 -0
  122. package/dist/commands/signals/sources/list.js +16 -0
  123. package/dist/commands/signals/sources/receipts.d.ts +11 -0
  124. package/dist/commands/signals/sources/receipts.js +17 -0
  125. package/dist/commands/signals/sources/show.d.ts +11 -0
  126. package/dist/commands/signals/sources/show.js +17 -0
  127. package/dist/commands/spaces/claim.d.ts +12 -0
  128. package/dist/commands/spaces/claim.js +42 -0
  129. package/dist/commands/spaces/ownership.d.ts +12 -0
  130. package/dist/commands/spaces/ownership.js +46 -0
  131. package/dist/commands/spaces/usage.d.ts +12 -0
  132. package/dist/commands/spaces/usage.js +28 -0
  133. package/dist/commands/spaces/workflows/run.js +3 -0
  134. package/dist/commands/spaces/workflows/runs.js +2 -1
  135. package/dist/commands/spaces/workflows/update.d.ts +6 -1
  136. package/dist/commands/spaces/workflows/update.js +34 -2
  137. package/dist/commands/tasks/cancel-cascade.js +1 -1
  138. package/dist/commands/tasks/cascade-status.d.ts +2 -0
  139. package/dist/commands/tasks/cascade-status.js +27 -1
  140. package/dist/commands/tasks/create-tree.d.ts +5 -1
  141. package/dist/commands/tasks/create-tree.js +35 -7
  142. package/dist/commands/tasks/create.d.ts +4 -0
  143. package/dist/commands/tasks/create.js +16 -0
  144. package/dist/commands/tasks/deliverable/add.js +12 -3
  145. package/dist/commands/tasks/economics-compare.d.ts +15 -0
  146. package/dist/commands/tasks/economics-compare.js +60 -0
  147. package/dist/commands/tasks/expectations/evaluate.d.ts +13 -0
  148. package/dist/commands/tasks/expectations/evaluate.js +43 -0
  149. package/dist/commands/tasks/interrupt.js +2 -2
  150. package/dist/commands/tasks/list.d.ts +1 -0
  151. package/dist/commands/tasks/list.js +44 -1
  152. package/dist/commands/tasks/pause-schedule.js +1 -1
  153. package/dist/commands/tasks/show.d.ts +5 -0
  154. package/dist/commands/tasks/show.js +17 -0
  155. package/dist/commands/tasks/update.d.ts +4 -0
  156. package/dist/commands/tasks/update.js +9 -0
  157. package/dist/commands/tasks/workflow-findings.js +2 -2
  158. package/dist/commands/token/list.d.ts +3 -5
  159. package/dist/commands/token/list.js +99 -57
  160. package/dist/commands/triggers/create.d.ts +2 -0
  161. package/dist/commands/triggers/create.js +15 -0
  162. package/dist/commands/update.d.ts +14 -0
  163. package/dist/commands/update.js +61 -0
  164. package/dist/commands/workspaces/grants.d.ts +12 -0
  165. package/dist/commands/workspaces/grants.js +31 -0
  166. package/dist/commands/workspaces/stewardship.d.ts +13 -0
  167. package/dist/commands/workspaces/stewardship.js +34 -0
  168. package/dist/generated/managed-instruction-guides.json +9 -0
  169. package/dist/hooks/init.d.ts +5 -0
  170. package/dist/hooks/init.js +27 -0
  171. package/dist/lib/agent-config.d.ts +0 -12
  172. package/dist/lib/agent-config.js +1 -27
  173. package/dist/lib/agent-mailbox.d.ts +35 -0
  174. package/dist/lib/agent-mailbox.js +55 -0
  175. package/dist/lib/commitment-endpoints.d.ts +2 -1
  176. package/dist/lib/commitment-endpoints.js +2 -5
  177. package/dist/lib/commitment-product.d.ts +3 -1
  178. package/dist/lib/commitment-product.js +10 -0
  179. package/dist/lib/commitment-terms.js +1 -1
  180. package/dist/lib/commitments.d.ts +17 -15
  181. package/dist/lib/commitments.js +173 -137
  182. package/dist/lib/compasses.d.ts +14 -0
  183. package/dist/lib/compasses.js +25 -11
  184. package/dist/lib/daemon-installer.d.ts +2 -0
  185. package/dist/lib/daemon-installer.js +100 -24
  186. package/dist/lib/first-party-harness-doctor.js +31 -1
  187. package/dist/lib/first-party-harness-remote-install.d.ts +71 -0
  188. package/dist/lib/first-party-harness-remote-install.js +310 -0
  189. package/dist/lib/first-party-harness-remote-update.d.ts +46 -0
  190. package/dist/lib/first-party-harness-remote-update.js +242 -0
  191. package/dist/lib/first-party-harness-startup-update.d.ts +26 -0
  192. package/dist/lib/first-party-harness-startup-update.js +166 -0
  193. package/dist/lib/first-party-harness-update.d.ts +46 -0
  194. package/dist/lib/first-party-harness-update.js +289 -0
  195. package/dist/lib/first-party-harness.js +7 -4
  196. package/dist/lib/grants.d.ts +5 -1
  197. package/dist/lib/grants.js +9 -1
  198. package/dist/lib/hosted-machines.d.ts +7 -0
  199. package/dist/lib/mailbox-human-command.d.ts +11 -0
  200. package/dist/lib/mailbox-human-command.js +41 -0
  201. package/dist/lib/managed-instruction-local.d.ts +12 -0
  202. package/dist/lib/managed-instruction-local.js +58 -0
  203. package/dist/lib/managed-instruction-setup.d.ts +35 -0
  204. package/dist/lib/managed-instruction-setup.js +306 -0
  205. package/dist/lib/measures.d.ts +28 -2
  206. package/dist/lib/measures.js +38 -14
  207. package/dist/lib/memory-sync.d.ts +1 -0
  208. package/dist/lib/memory-sync.js +279 -68
  209. package/dist/lib/message-intent.d.ts +6 -2
  210. package/dist/lib/message-intent.js +1 -0
  211. package/dist/lib/northstars.js +2 -1
  212. package/dist/lib/platform-update-local.d.ts +2 -0
  213. package/dist/lib/platform-update-local.js +113 -0
  214. package/dist/lib/platform-update.d.ts +39 -0
  215. package/dist/lib/platform-update.js +84 -0
  216. package/dist/lib/profile-machine-identity-guard.d.ts +2 -0
  217. package/dist/lib/profile-machine-identity-guard.js +13 -0
  218. package/dist/lib/prompt.d.ts +1 -0
  219. package/dist/lib/prompt.js +4 -3
  220. package/dist/lib/proposal-decision-command.d.ts +19 -0
  221. package/dist/lib/proposal-decision-command.js +64 -0
  222. package/dist/lib/proposals.d.ts +8 -0
  223. package/dist/lib/proposals.js +22 -0
  224. package/dist/lib/roles.d.ts +141 -6
  225. package/dist/lib/roles.js +8 -2
  226. package/dist/lib/signals.d.ts +20 -0
  227. package/dist/lib/signals.js +24 -0
  228. package/dist/lib/space-usage.d.ts +3 -0
  229. package/dist/lib/space-usage.js +17 -0
  230. package/dist/lib/stewardship-output.d.ts +4 -0
  231. package/dist/lib/stewardship-output.js +82 -0
  232. package/dist/lib/task-asset-upload.d.ts +3 -1
  233. package/dist/lib/task-asset-upload.js +1 -0
  234. package/dist/lib/task-batch.d.ts +9 -0
  235. package/dist/lib/task-batch.js +94 -17
  236. package/dist/lib/task-economics.d.ts +16 -0
  237. package/dist/lib/task-economics.js +22 -0
  238. package/dist/lib/task-instruction-offer.d.ts +1 -4
  239. package/dist/lib/task-instruction-offer.js +3 -129
  240. package/dist/lib/triggers.d.ts +11 -3
  241. package/dist/lib/triggers.js +58 -1
  242. package/dist/lib/update-check.d.ts +61 -53
  243. package/dist/lib/update-check.js +453 -314
  244. package/dist/lib/usage-discovery.d.ts +10 -0
  245. package/dist/lib/usage-discovery.js +24 -3
  246. package/dist/lib/workflow-run-budget.d.ts +12 -0
  247. package/dist/lib/workflow-run-budget.js +37 -0
  248. package/dist/lib/workspaces.d.ts +48 -0
  249. package/dist/lib/workspaces.js +8 -0
  250. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +4 -0
  251. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +14 -1
  252. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +3 -4
  253. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +3 -4
  254. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.d.ts +82 -0
  255. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.js +105 -0
  256. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.d.ts +110 -0
  257. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.js +945 -0
  258. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.d.ts +9 -0
  259. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.js +49 -0
  260. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.d.ts +32 -0
  261. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.js +62 -0
  262. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -0
  263. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -1
  264. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +35 -5
  265. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.d.ts +63 -0
  266. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.js +169 -0
  267. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.d.ts +2 -0
  268. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.js +44 -0
  269. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +4 -0
  270. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +12 -0
  271. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +3 -4
  272. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +3 -4
  273. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.d.ts +82 -0
  274. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.js +102 -0
  275. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.d.ts +110 -0
  276. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.js +923 -0
  277. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.d.ts +9 -0
  278. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.js +44 -0
  279. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.d.ts +32 -0
  280. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.js +59 -0
  281. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -0
  282. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -0
  283. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +35 -5
  284. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.d.ts +63 -0
  285. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.js +163 -0
  286. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.d.ts +2 -0
  287. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.js +41 -0
  288. package/dist/node_modules/@skrr-ai/auth-core/package.json +41 -1
  289. package/dist/node_modules/@skrr-ai/data-provider/index.js +8069 -7121
  290. package/docs/v1/README.md +28 -0
  291. package/docs/v1/api.md +55 -0
  292. package/docs/v1/changelog.md +16 -0
  293. package/docs/v1/check-examples.mjs +178 -0
  294. package/docs/v1/examples/api-readonly.mjs +144 -0
  295. package/docs/v1/operations.md +87 -0
  296. package/docs/v1/quickstart.md +71 -0
  297. package/oclif.manifest.json +49424 -46174
  298. package/package.json +34 -11
@@ -1,8 +1,49 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseObservationWindow = parseObservationWindow;
4
+ exports.renderObservationWindow = renderObservationWindow;
3
5
  const core_1 = require("@oclif/core");
6
+ const data_provider_1 = require("@skrr-ai/data-provider");
4
7
  const base_command_1 = require("../../base-command");
5
8
  const commitments_1 = require("../../lib/commitments");
9
+ /** Explicit ISO FROM/TO intervals avoid local-time or rolling-window ambiguity. */
10
+ function parseObservationWindow(value) {
11
+ const parts = value.split('/');
12
+ const timestamp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
13
+ const [from, to] = parts.map((entry) => new Date(entry).getTime());
14
+ const realCalendarDay = (part) => {
15
+ const day = part.slice(0, 10);
16
+ const parsed = new Date(`${day}T00:00:00Z`);
17
+ return Number.isFinite(parsed.getTime()) && parsed.toISOString().slice(0, 10) === day;
18
+ };
19
+ if (parts.length !== 2 ||
20
+ !parts.every((part) => timestamp.test(part) && realCalendarDay(part)) ||
21
+ !Number.isFinite(from) ||
22
+ !Number.isFinite(to) ||
23
+ to <= from ||
24
+ to - from > data_provider_1.OBSERVATION_WINDOW_MAX_MS)
25
+ throw new Error('Window requires ISO FROM/TO with an explicit timezone and a positive span of at most 90 days');
26
+ return { from: new Date(from).toISOString(), to: new Date(to).toISOString() };
27
+ }
28
+ function renderObservationWindow(response, log) {
29
+ // Always announce partial data before showing figures, including baseline truncation.
30
+ if (response.truncated)
31
+ log('TRUNCATED: bounded sample only; figures are incomplete.');
32
+ log('Agent-reported samples; as-of interval coverage is not independent verification.');
33
+ const display = (label, value) => {
34
+ const window = value;
35
+ log(`${label}: [${window.from}, ${window.to})`);
36
+ log(`Samples: ${window.count}; numeric: ${window.numericCount}; unreadable: ${window.gapCount}; uncovered ms: ${window.gapMs}`);
37
+ log(`Mean: ${window.mean ?? 'unknown'}; sum: ${window.sum ?? 'unknown'}; first: ${window.first ?? 'unknown'}; last: ${window.last ?? 'unknown'}`);
38
+ };
39
+ if (response.baseline) {
40
+ display('Baseline', response.baseline);
41
+ display('After', response.after);
42
+ log(`Comparison: ${response.sufficient ? `delta=${response.delta}; ratio=${response.ratio}` : `unavailable (${response.reason})`}`);
43
+ }
44
+ else
45
+ display('Window', response.summary);
46
+ }
6
47
  /**
7
48
  * `skrr commitments observations <id>` — what this commitment's runs saw.
8
49
  *
@@ -30,6 +71,18 @@ class CommitmentsObservations extends base_command_1.BaseCommand {
30
71
  };
31
72
  static flags = {
32
73
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
74
+ latest: core_1.Flags.boolean({
75
+ description: 'Latest recorded attempt per current declared key (not history)',
76
+ }),
77
+ key: core_1.Flags.string({ description: 'Observation key for --window' }),
78
+ window: core_1.Flags.string({
79
+ description: 'Half-open ISO FROM/TO interval, at most 90 days',
80
+ dependsOn: ['key'],
81
+ }),
82
+ 'compare-to': core_1.Flags.string({
83
+ description: 'Baseline ISO FROM/TO interval',
84
+ dependsOn: ['window'],
85
+ }),
33
86
  limit: core_1.Flags.integer({ description: 'Rows to include', min: 1, max: 200 }),
34
87
  'declared-only': core_1.Flags.boolean({
35
88
  description: 'Only keys the operator declared — the rows that can move the loop',
@@ -45,10 +98,42 @@ class CommitmentsObservations extends base_command_1.BaseCommand {
45
98
  async run() {
46
99
  this.requireAuth();
47
100
  const { args, flags } = await this.parse(CommitmentsObservations);
101
+ if (flags.latest && (flags.window || flags.cursor || flags['changed-only'] || flags.since))
102
+ this.error('--latest cannot be combined with --window, --cursor, --changed-only, or --since');
48
103
  let response;
104
+ if (flags.window) {
105
+ try {
106
+ if (flags['declared-only'] || flags['changed-only'] || flags.since || flags.cursor)
107
+ this.error('--window cannot be combined with list filters');
108
+ const query = {
109
+ key: flags.key,
110
+ ...parseObservationWindow(flags.window),
111
+ limit: flags.limit,
112
+ };
113
+ const baseline = flags['compare-to'] ? parseObservationWindow(flags['compare-to']) : null;
114
+ response = baseline
115
+ ? await commitments_1.commitmentApi.observationCompare(args.commitmentId, {
116
+ ...query,
117
+ baselineFrom: baseline.from,
118
+ baselineTo: baseline.to,
119
+ })
120
+ : await commitments_1.commitmentApi.observationSeries(args.commitmentId, query);
121
+ }
122
+ catch (err) {
123
+ this.handleApiError(err);
124
+ }
125
+ if (flags.json)
126
+ this.log(JSON.stringify(response, null, 2));
127
+ else
128
+ renderObservationWindow(response, (line) => this.log(line));
129
+ return;
130
+ }
131
+ if (flags.key)
132
+ this.error('--key requires --window');
49
133
  try {
50
134
  response = await commitments_1.commitmentApi.observations(args.commitmentId, {
51
135
  limit: flags.limit,
136
+ latest: flags.latest || undefined,
52
137
  declaredOnly: flags['declared-only'] || undefined,
53
138
  changedOnly: flags['changed-only'] || undefined,
54
139
  since: flags.since,
@@ -66,6 +151,7 @@ class CommitmentsObservations extends base_command_1.BaseCommand {
66
151
  // facts; a filter that empties the list must not claim the first
67
152
  // (OSK-10953).
68
153
  const filters = [
154
+ flags.latest ? '--latest' : null,
69
155
  flags['declared-only'] ? '--declared-only' : null,
70
156
  flags['changed-only'] ? '--changed-only' : null,
71
157
  flags.since ? `--since ${flags.since}` : null,
@@ -73,6 +159,8 @@ class CommitmentsObservations extends base_command_1.BaseCommand {
73
159
  const emptyMessage = filters.length
74
160
  ? `No observations match ${filters.join(' ')}.`
75
161
  : 'No observations recorded.';
162
+ if (response?.truncated)
163
+ this.log(`TRUNCATED: ${response.omitted} current keys omitted; increase --limit (max 200).`);
76
164
  (0, commitments_1.renderCommitmentObservations)(response, (line) => this.log(line), { emptyMessage });
77
165
  }
78
166
  (0, commitments_1.renderListPageHint)(response, (line) => this.log(line));
@@ -1,7 +1,7 @@
1
1
  import { BaseCommand } from '../../base-command';
2
2
  /**
3
3
  * `skrr commitments pause <id>` — stop the control loop without losing the
4
- * contract or its history. The kill-switch for a noisy or misfiring commitment.
4
+ * contract or its history. An already executing turn may continue.
5
5
  */
6
6
  export default class CommitmentsPause extends BaseCommand {
7
7
  static description: string;
@@ -6,10 +6,10 @@ const web_url_1 = require("../../lib/web-url");
6
6
  const commitments_1 = require("../../lib/commitments");
7
7
  /**
8
8
  * `skrr commitments pause <id>` — stop the control loop without losing the
9
- * contract or its history. The kill-switch for a noisy or misfiring commitment.
9
+ * contract or its history. An already executing turn may continue.
10
10
  */
11
11
  class CommitmentsPause extends base_command_1.BaseCommand {
12
- static description = 'Pause a commitment — its control loop stops checking';
12
+ static description = 'Pause a commitment — stops new work; a turn already running may continue until it finishes or reaches its deadline';
13
13
  static examples = ['<%= config.bin %> commitments pause <commitment-id>'];
14
14
  static args = {
15
15
  id: core_1.Args.string({ description: 'Commitment ID', required: true, ignoreStdin: true }),
@@ -30,6 +30,7 @@ class CommitmentsPause extends base_command_1.BaseCommand {
30
30
  return;
31
31
  }
32
32
  this.log(`Paused commitment ${args.id}.`);
33
+ this.log('Stops new work. A turn already running may continue until it finishes or reaches its deadline.');
33
34
  // The status write is the fence and it has landed. The teardown of the
34
35
  // wake tree behind it is best-effort, and used to be silent — so a pause
35
36
  // that left triggers live looked exactly like one that did not. Say which
@@ -39,7 +40,7 @@ class CommitmentsPause extends base_command_1.BaseCommand {
39
40
  .teardown;
40
41
  if (teardown?.status === 'degraded') {
41
42
  this.warn('The control loop is stopped, but some scheduled wakes may not have been retired yet. ' +
42
- 'They cannot run anything — every wake is refused while this Commitment is paused — ' +
43
+ 'They cannot start new work — every wake is refused while this Commitment is paused — ' +
43
44
  `and the reconciler retries automatically. Check with: skrr commitments trigger-status ${args.id}`);
44
45
  }
45
46
  (0, commitments_1.renderCommitmentSummary)(commitment, (line) => this.log(line));
@@ -153,7 +153,8 @@ class CommitmentsPreflight extends base_command_1.BaseCommand {
153
153
  for (const endpoint of report.endpoints || []) {
154
154
  this.log(` ${endpoint.available ? 'ok ' : 'BLOCK'} ${endpoint.label || endpoint.destination}` +
155
155
  ` — ${(endpoint.capabilities || []).join(', ') || 'no capabilities'}` +
156
- (endpoint.reason ? ` (${endpoint.reason})` : ''));
156
+ (endpoint.reason ? ` (${endpoint.reason})` : '') +
157
+ (!endpoint.available && endpoint.detail ? ` — ${endpoint.detail}` : ''));
157
158
  }
158
159
  this.log(`\nAttention: ${report.attention?.allowed ? 'delivery allowed now' : `suppressed by ${report.attention?.suppressedBy || 'policy'}`}`);
159
160
  if (report.verdict === 'blocked')
@@ -19,7 +19,9 @@ export default class CommitmentsReports extends BaseCommand {
19
19
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
20
  add: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
21
21
  target: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
+ review: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
23
  on: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
+ format: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
23
25
  remove: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
26
  };
25
27
  run(): Promise<void>;
@@ -6,16 +6,18 @@ const base_command_1 = require("../../base-command");
6
6
  const commitments_1 = require("../../lib/commitments");
7
7
  const commitment_terms_1 = require("../../lib/commitment-terms");
8
8
  /** Destinations that address one specific place and therefore need a target. */
9
- const DESTINATIONS_NEEDING_TARGET = new Set(['slack_channel']);
9
+ const DESTINATIONS_NEEDING_TARGET = new Set(Object.values(data_provider_1.COMMITMENT_DESTINATION_REGISTRY)
10
+ .filter((destination) => destination.requiresTarget)
11
+ .map((destination) => destination.key));
10
12
  /**
11
13
  * Keep the CLI safe across a rolling data-provider build. Older installed
12
14
  * packages expose the compatibility destination list but not the narrowed
13
15
  * authoring list; derive the same restriction instead of crashing command
14
- * discovery or accidentally offering the inert email transport.
16
+ * discovery.
15
17
  */
16
18
  const AUTHORABLE_REPORT_DESTINATIONS = Array.isArray(data_provider_1.COMMITMENT_AUTHORABLE_REPORT_DESTINATIONS)
17
19
  ? data_provider_1.COMMITMENT_AUTHORABLE_REPORT_DESTINATIONS
18
- : data_provider_1.COMMITMENT_REPORT_DESTINATIONS.filter((destination) => destination !== 'email');
20
+ : data_provider_1.COMMITMENT_REPORT_DESTINATIONS;
19
21
  /**
20
22
  * Identity of a report target = destination + its target id. The same channel
21
23
  * may legitimately appear twice with different `on` conditions (a digest on
@@ -41,6 +43,7 @@ class CommitmentsReports extends base_command_1.BaseCommand {
41
43
  '<%= config.bin %> commitments reports <commitment-id>',
42
44
  '<%= config.bin %> commitments reports <commitment-id> --add slack_channel --target C0123456 --on material_change',
43
45
  '<%= config.bin %> commitments reports <commitment-id> --add main --on completion',
46
+ '<%= config.bin %> commitments reports <commitment-id> --add user_notify --target <user-id> --review owner',
44
47
  '<%= config.bin %> commitments reports <commitment-id> --remove slack_channel:C0123456',
45
48
  ];
46
49
  static args = {
@@ -53,12 +56,20 @@ class CommitmentsReports extends base_command_1.BaseCommand {
53
56
  options: [...AUTHORABLE_REPORT_DESTINATIONS],
54
57
  }),
55
58
  target: core_1.Flags.string({
56
- description: 'Channel id for slack_channel. App and owner-DM destinations need no target.',
59
+ description: 'Channel id for slack_channel, or the user id for user_notify. App, owner-DM and email destinations need no target (email goes to your verified account address).',
60
+ }),
61
+ review: core_1.Flags.string({
62
+ description: 'Hold the exact text for approver approval before it is sent to this target (owner)',
63
+ options: [...data_provider_1.COMMITMENT_REPORT_REVIEW_MODES],
57
64
  }),
58
65
  on: core_1.Flags.string({
59
66
  description: 'When to report (default: material_change)',
60
67
  options: [...data_provider_1.COMMITMENT_REPORT_TRIGGERS],
61
68
  }),
69
+ format: core_1.Flags.string({
70
+ description: 'Optional report format (absent: text)',
71
+ options: ['text', 'report_v1'],
72
+ }),
62
73
  remove: core_1.Flags.string({
63
74
  description: 'Report target to remove: "<destination>" or "<destination>:<target>"',
64
75
  }),
@@ -66,6 +77,8 @@ class CommitmentsReports extends base_command_1.BaseCommand {
66
77
  async run() {
67
78
  this.requireAuth();
68
79
  const { args, flags } = await this.parse(CommitmentsReports);
80
+ if (flags.format && !flags.add)
81
+ this.error('--format requires --add.', { exit: 1 });
69
82
  if (flags.add && flags.remove) {
70
83
  this.error('Pass --add or --remove, not both.', { exit: 1 });
71
84
  }
@@ -89,6 +102,9 @@ class CommitmentsReports extends base_command_1.BaseCommand {
89
102
  try {
90
103
  const current = await (0, commitment_terms_1.loadCommitment)(args.id);
91
104
  const reports = current.reports ?? [];
105
+ if (flags.review && !flags.add) {
106
+ throw new Error('--review applies to a report being added (--add).');
107
+ }
92
108
  if (flags.add) {
93
109
  const destination = flags.add;
94
110
  if (DESTINATIONS_NEEDING_TARGET.has(destination) && !flags.target) {
@@ -102,8 +118,10 @@ class CommitmentsReports extends base_command_1.BaseCommand {
102
118
  const next = {
103
119
  destination,
104
120
  ...(flags.target ? { destinationTarget: flags.target } : {}),
121
+ ...(flags.review ? { review: flags.review } : {}),
105
122
  on: flags.on ?? 'material_change',
106
123
  enabled: true,
124
+ ...(flags.format ? { format: flags.format } : {}),
107
125
  };
108
126
  payload = { reports: (0, commitment_terms_1.appendUnique)(reports, next, reportKey, 'Report target') };
109
127
  }
@@ -30,6 +30,7 @@ export default class CommitmentsUpdate extends BaseCommand {
30
30
  yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
31
31
  'from-json': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
32
32
  'if-version': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
33
+ 'task-review-policy': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
33
34
  title: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
34
35
  description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
35
36
  'agent-directive': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const core_1 = require("@oclif/core");
4
3
  const data_provider_1 = require("@skrr-ai/data-provider");
4
+ const core_1 = require("@oclif/core");
5
+ const commitment_product_1 = require("../../lib/commitment-product");
6
+ const data_provider_2 = require("@skrr-ai/data-provider");
5
7
  const base_command_1 = require("../../base-command");
6
8
  const prompt_1 = require("../../lib/prompt");
7
9
  const web_url_1 = require("../../lib/web-url");
@@ -52,6 +54,10 @@ class CommitmentsUpdate extends base_command_1.BaseCommand {
52
54
  'if-version': core_1.Flags.integer({
53
55
  description: 'Only update this observed contract version; returns a conflict if it changed',
54
56
  }),
57
+ 'task-review-policy': core_1.Flags.string({
58
+ options: [...data_provider_1.TASK_REVIEW_POLICIES],
59
+ description: 'Owner choice for generated Tasks; required records a blocker but does not stop the owner close.',
60
+ }),
55
61
  title: core_1.Flags.string({ description: 'New title' }),
56
62
  description: core_1.Flags.string({ description: 'New description' }),
57
63
  'agent-directive': core_1.Flags.string({
@@ -133,9 +139,22 @@ class CommitmentsUpdate extends base_command_1.BaseCommand {
133
139
  const wantsCadence = flags.cadence !== undefined ||
134
140
  flags['cadence-mode'] !== undefined ||
135
141
  flags.timezone !== undefined;
142
+ let payload;
143
+ try {
144
+ payload = await (0, prompt_2.readJsonBody)(flags['from-json']);
145
+ }
146
+ catch (err) {
147
+ this.error(err.message, { exit: 1 });
148
+ }
149
+ const authoredDelivery = payload.delivery && typeof payload.delivery === 'object' && !Array.isArray(payload.delivery)
150
+ ? payload.delivery
151
+ : undefined;
136
152
  // Read-modify-write: only pay the extra round-trip when we must merge.
137
153
  let current;
138
- if (wantsTarget || wantsCadence) {
154
+ if (wantsTarget ||
155
+ wantsCadence ||
156
+ authoredDelivery ||
157
+ flags['task-review-policy'] !== undefined) {
139
158
  try {
140
159
  const res = await commitments_1.commitmentApi.get(args.id);
141
160
  current = res?.commitment;
@@ -147,11 +166,22 @@ class CommitmentsUpdate extends base_command_1.BaseCommand {
147
166
  this.error(`Commitment ${args.id} not found.`, { exit: 4 });
148
167
  }
149
168
  }
150
- let payload;
151
169
  try {
152
- payload = await (0, prompt_2.readJsonBody)(flags['from-json']);
153
170
  if (flags['if-version'] !== undefined)
154
171
  payload.expectedVersion = flags['if-version'];
172
+ if (authoredDelivery || flags['task-review-policy'] !== undefined) {
173
+ payload.delivery = {
174
+ ...(current?.delivery || { version: 1, mode: 'report_only' }),
175
+ ...authoredDelivery,
176
+ ...(flags['task-review-policy'] !== undefined
177
+ ? { reviewPolicy: (0, commitment_product_1.normalizeTaskReviewPolicy)(flags['task-review-policy']) }
178
+ : {}),
179
+ };
180
+ payload.expectedVersion ??= current?.version;
181
+ }
182
+ const declaredDelivery = payload.delivery;
183
+ if (declaredDelivery?.reviewPolicy !== undefined)
184
+ (0, commitment_product_1.normalizeTaskReviewPolicy)(declaredDelivery.reviewPolicy);
155
185
  if (flags.title)
156
186
  payload.title = flags.title;
157
187
  if (flags.description !== undefined)
@@ -168,7 +198,7 @@ class CommitmentsUpdate extends base_command_1.BaseCommand {
168
198
  // generic PATCH with no ceremony at all, while `commitments delete` —
169
199
  // whose data is all still on disk, and which is now restorable —
170
200
  // prompts. Ceremony should track how hard something is to undo.
171
- if ((0, data_provider_1.isTerminalCommitmentStatus)(flags.status)) {
201
+ if ((0, data_provider_2.isTerminalCommitmentStatus)(flags.status)) {
172
202
  let confirmed;
173
203
  try {
174
204
  confirmed = await (0, prompt_1.confirmDestructive)(`Set ${args.id} to ${flags.status}? This ends the Commitment — making it active again means archiving it first, then resuming.`, flags.yes);
@@ -84,6 +84,10 @@ class DaemonInstall extends core_1.Command {
84
84
  // is how a CLI and its daemon end up on different deployments
85
85
  // (OSK-279) without anyone doing anything wrong.
86
86
  const code = await (0, exec_runtime_binary_1.execRuntimeBinary)(['install', ...(0, daemon_binding_1.bindToCliSession)(argv)]);
87
+ if (code === 0 &&
88
+ !argv.some((arg) => ['--json', '--help', '-h'].includes(arg))) {
89
+ this.log('Coding-agent instructions: sign in with `skrr login`, or review installation choices with `skrr instructions setup`.');
90
+ }
87
91
  this.exit(code);
88
92
  }
89
93
  catch (err) {
@@ -6,7 +6,9 @@ import { BaseCommand } from '../base-command';
6
6
  * POST /api/auth/recovery/generate. The formatted code is printed
7
7
  * exactly once to STDOUT; a warning goes to STDERR.
8
8
  *
9
- * Requires an interactive user session (NOT a CI token).
9
+ * Requires a user sign-in session (NOT a CI token or PAT). When the server
10
+ * requests a fresh proof, this command verifies it for recovery_code_generate
11
+ * on the same session before retrying the generation exactly once.
10
12
  * On success: any previously-active recovery code is revoked server-side
11
13
  * and the new one is returned.
12
14
  *
@@ -19,6 +21,16 @@ export default class GenerateRecoveryCode extends BaseCommand {
19
21
  static examples: string[];
20
22
  static flags: {
21
23
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
24
+ otp: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
25
+ 'backup-code': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
26
+ 'email-code': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
27
  };
23
28
  run(): Promise<void>;
29
+ private generate;
30
+ private proofFor;
31
+ private nonemptyCode;
32
+ private refuseUnavailableMethod;
33
+ private refusePasskeyOnly;
34
+ private refuseStepUp;
35
+ private handleGenerationError;
24
36
  }
@@ -1,8 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
4
5
  const base_command_1 = require("../base-command");
5
6
  const api_fetch_1 = require("../lib/api-fetch");
7
+ const prompt_1 = require("../lib/prompt");
8
+ // Include passkey even though this terminal cannot run a WebAuthn ceremony.
9
+ // The server uses this header when choosing the account's strongest proof:
10
+ // omitting passkey would make a passkey-only account fall back to email.
11
+ // A passkey-only challenge is refused below with a first-party web handoff.
12
+ const STEP_UP_HEADERS = { 'X-Step-Up-Methods': 'passkey,totp,backup_code,email_code' };
13
+ const PURPOSE = data_provider_1.RECOVERY_CODE_GENERATE_STEP_UP_PURPOSE;
6
14
  /**
7
15
  * `skrr generate-recovery-code`
8
16
  *
@@ -10,7 +18,9 @@ const api_fetch_1 = require("../lib/api-fetch");
10
18
  * POST /api/auth/recovery/generate. The formatted code is printed
11
19
  * exactly once to STDOUT; a warning goes to STDERR.
12
20
  *
13
- * Requires an interactive user session (NOT a CI token).
21
+ * Requires a user sign-in session (NOT a CI token or PAT). When the server
22
+ * requests a fresh proof, this command verifies it for recovery_code_generate
23
+ * on the same session before retrying the generation exactly once.
14
24
  * On success: any previously-active recovery code is revoked server-side
15
25
  * and the new one is returned.
16
26
  *
@@ -22,6 +32,8 @@ class GenerateRecoveryCode extends base_command_1.BaseCommand {
22
32
  static description = 'Generate a printable account-recovery code. Shown once — store it safely. Recovery codes use letters A-Z and digits 2-7.';
23
33
  static examples = [
24
34
  '<%= config.bin %> generate-recovery-code',
35
+ '<%= config.bin %> generate-recovery-code --otp 123456',
36
+ '<%= config.bin %> generate-recovery-code --backup-code <unused-code>',
25
37
  '<%= config.bin %> generate-recovery-code --json',
26
38
  ];
27
39
  static flags = {
@@ -29,28 +41,60 @@ class GenerateRecoveryCode extends base_command_1.BaseCommand {
29
41
  description: 'Output as JSON (for scripting). Code still printed only once.',
30
42
  default: false,
31
43
  }),
44
+ otp: core_1.Flags.string({
45
+ description: 'Current code from your authenticator app when a fresh proof is required',
46
+ exclusive: ['backup-code', 'email-code'],
47
+ }),
48
+ 'backup-code': core_1.Flags.string({
49
+ description: 'Unused two-factor backup code when a fresh proof is required (it is spent)',
50
+ exclusive: ['otp', 'email-code'],
51
+ }),
52
+ 'email-code': core_1.Flags.string({
53
+ description: 'Previously requested one-time email code when a fresh proof is required',
54
+ exclusive: ['otp', 'backup-code'],
55
+ }),
32
56
  };
33
57
  async run() {
34
58
  this.requireAuth();
35
59
  const { flags } = await this.parse(GenerateRecoveryCode);
36
- // Guard: CI tokens cannot generate recovery codes (server also enforces this).
37
- if (this.resolvedCredential.kind === 'ci') {
38
- this.error('CI tokens cannot generate recovery codes. Run this command from an interactive session (`skrr login`).', { exit: 2 });
60
+ // Static credentials cannot mint account recovery credentials.
61
+ if (this.resolvedCredential.kind === 'ci' || this.resolvedCredential.kind === 'pat') {
62
+ this.error('CI and personal access tokens cannot generate recovery codes. Sign in with `skrr login` first.', { exit: 2 });
39
63
  }
40
64
  let result;
41
65
  try {
42
- result = await (0, api_fetch_1.apiFetch)('/api/auth/recovery/generate', {
43
- method: 'POST',
44
- credential: this.resolvedCredential,
45
- });
66
+ result = await this.generate();
46
67
  }
47
68
  catch (err) {
48
- if (err instanceof api_fetch_1.ApiFetchError) {
49
- if (err.status === 401 || err.status === 403) {
50
- this.error('Not authorized to generate a recovery code. Ensure you are signed in with a user-scope session.', { exit: 1 });
51
- }
69
+ const challenge = err instanceof api_fetch_1.ApiFetchError && err.status === 403 ? (0, data_provider_1.stepUpChallengeFromError)(err) : null;
70
+ if (!challenge)
71
+ this.handleGenerationError(err);
72
+ if (challenge.purpose !== PURPOSE) {
73
+ this.error('The server asked for verification of a different action. Nothing changed.', {
74
+ code: 'STEP_UP_PURPOSE_MISMATCH',
75
+ exit: 2,
76
+ });
77
+ }
78
+ if (challenge.reason !== 'no_recent_step_up')
79
+ this.refuseStepUp(challenge);
80
+ const proof = await this.proofFor(challenge, flags);
81
+ try {
82
+ await (0, api_fetch_1.apiFetch)('/api/auth/step-up', {
83
+ method: 'POST',
84
+ credential: this.resolvedCredential,
85
+ headers: STEP_UP_HEADERS,
86
+ body: { purpose: PURPOSE, ...proof },
87
+ });
88
+ }
89
+ catch (verifyError) {
90
+ this.handleApiError(verifyError);
91
+ }
92
+ try {
93
+ result = await this.generate();
94
+ }
95
+ catch (retryError) {
96
+ this.handleGenerationError(retryError);
52
97
  }
53
- this.handleApiError(err);
54
98
  }
55
99
  if (flags.json) {
56
100
  // JSON mode: print structured output to stdout for scripting.
@@ -75,5 +119,94 @@ class GenerateRecoveryCode extends base_command_1.BaseCommand {
75
119
  process.stderr.write(` Generated at: ${new Date(result.createdAt).toISOString()}\n`);
76
120
  process.stderr.write('\n');
77
121
  }
122
+ generate() {
123
+ return (0, api_fetch_1.apiFetch)('/api/auth/recovery/generate', {
124
+ method: 'POST',
125
+ credential: this.resolvedCredential,
126
+ headers: STEP_UP_HEADERS,
127
+ });
128
+ }
129
+ async proofFor(challenge, flags) {
130
+ if (challenge.methods.includes('passkey') &&
131
+ !challenge.methods.includes('totp') &&
132
+ !challenge.methods.includes('backup_code')) {
133
+ this.refusePasskeyOnly();
134
+ }
135
+ if (flags.otp !== undefined) {
136
+ if (!challenge.methods.includes('totp'))
137
+ this.refuseUnavailableMethod();
138
+ return { token: this.nonemptyCode(flags.otp) };
139
+ }
140
+ if (flags['backup-code'] !== undefined) {
141
+ if (!challenge.methods.includes('backup_code'))
142
+ this.refuseUnavailableMethod();
143
+ return { backupCode: this.nonemptyCode(flags['backup-code']) };
144
+ }
145
+ if (flags['email-code'] !== undefined) {
146
+ if (!challenge.methods.includes('email_code'))
147
+ this.refuseUnavailableMethod();
148
+ return { emailCode: this.nonemptyCode(flags['email-code']) };
149
+ }
150
+ if (!process.stdin.isTTY) {
151
+ this.error('A fresh identity check is required. Run this command at a terminal, or pass --otp, --backup-code, or a previously requested --email-code. Nothing changed.', { code: 'STEP_UP_PROOF_REQUIRED', exit: 2 });
152
+ }
153
+ if (challenge.methods.includes('totp')) {
154
+ const entered = await (0, prompt_1.prompt)('Authenticator code: ', {
155
+ hidden: true,
156
+ output: process.stderr,
157
+ });
158
+ return { token: this.nonemptyCode(entered) };
159
+ }
160
+ if (challenge.methods.includes('backup_code')) {
161
+ const entered = await (0, prompt_1.prompt)('Unused two-factor backup code: ', {
162
+ hidden: true,
163
+ output: process.stderr,
164
+ });
165
+ return { backupCode: this.nonemptyCode(entered) };
166
+ }
167
+ if (challenge.methods.includes('email_code')) {
168
+ try {
169
+ await (0, api_fetch_1.apiFetch)('/api/auth/step-up/email-code', {
170
+ method: 'POST',
171
+ credential: this.resolvedCredential,
172
+ headers: STEP_UP_HEADERS,
173
+ body: { purpose: PURPOSE },
174
+ });
175
+ }
176
+ catch (requestError) {
177
+ this.handleApiError(requestError);
178
+ }
179
+ process.stderr.write('A one-time code was sent to your account email.\n');
180
+ const entered = await (0, prompt_1.prompt)('Email code: ', { hidden: true, output: process.stderr });
181
+ return { emailCode: this.nonemptyCode(entered) };
182
+ }
183
+ this.refuseUnavailableMethod();
184
+ }
185
+ nonemptyCode(value) {
186
+ const code = value.trim();
187
+ if (!code)
188
+ this.error('A verification code cannot be empty. Nothing changed.', { exit: 2 });
189
+ return code;
190
+ }
191
+ refuseUnavailableMethod() {
192
+ this.error('This login has no identity-check method the CLI can complete. Use Settings → Account in the web app, or enroll an authenticator and try again. Nothing changed.', { code: 'STEP_UP_METHOD_UNAVAILABLE', exit: 2 });
193
+ }
194
+ refusePasskeyOnly() {
195
+ this.error('This account requires a passkey for this identity check. Open Settings → Account in the web app to generate a recovery code. Nothing changed.', { code: 'STEP_UP_PASSKEY_REQUIRED', exit: 2 });
196
+ }
197
+ refuseStepUp(challenge) {
198
+ if (challenge.reason === 'credential_not_session_bound') {
199
+ this.error('This credential has no sign-in session for an identity check. Run `skrr login` and try again. Nothing changed.', { code: 'STEP_UP_SESSION_UNBOUND', exit: 2 });
200
+ }
201
+ this.refuseUnavailableMethod();
202
+ }
203
+ handleGenerationError(err) {
204
+ if (err instanceof api_fetch_1.ApiFetchError && (err.status === 401 || err.status === 403)) {
205
+ this.error('Not authorized to generate a recovery code. Sign in with a user-scope session.', {
206
+ exit: 1,
207
+ });
208
+ }
209
+ this.handleApiError(err);
210
+ }
78
211
  }
79
212
  exports.default = GenerateRecoveryCode;
@@ -20,6 +20,7 @@ export default class GrantsCreate extends BaseCommand {
20
20
  commitment: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
21
21
  provider: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
22
22
  label: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
23
+ access: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
23
24
  description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
25
  env: import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
25
26
  daemon: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;