@skrr-ai/cli 0.1.93 → 0.1.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +153 -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/agents/operating-plan.d.ts +11 -0
  8. package/dist/commands/agents/operating-plan.js +54 -0
  9. package/dist/commands/agents/roles/example.d.ts +11 -0
  10. package/dist/commands/agents/roles/example.js +30 -0
  11. package/dist/commands/{commitments/mode.d.ts → agents/roles/export.d.ts} +5 -5
  12. package/dist/commands/agents/roles/export.js +81 -0
  13. package/dist/commands/agents/roles/install.d.ts +12 -0
  14. package/dist/commands/agents/roles/install.js +48 -0
  15. package/dist/commands/agents/roles/list.d.ts +11 -0
  16. package/dist/commands/agents/roles/list.js +36 -0
  17. package/dist/commands/agents/roles/preview.d.ts +14 -0
  18. package/dist/commands/agents/roles/preview.js +49 -0
  19. package/dist/commands/agents/roles/withdraw.d.ts +13 -0
  20. package/dist/commands/agents/roles/withdraw.js +43 -0
  21. package/dist/commands/code/doctor.d.ts +1 -0
  22. package/dist/commands/code/doctor.js +6 -0
  23. package/dist/commands/code/index.js +4 -0
  24. package/dist/commands/code/install.d.ts +5 -62
  25. package/dist/commands/code/install.js +86 -279
  26. package/dist/commands/code/rollback.d.ts +8 -0
  27. package/dist/commands/code/rollback.js +20 -0
  28. package/dist/commands/code/update.d.ts +8 -37
  29. package/dist/commands/code/update.js +134 -213
  30. package/dist/commands/commitments/{autonomy-requests → cadence-requests}/decide.d.ts +5 -7
  31. package/dist/commands/commitments/{autonomy-requests → cadence-requests}/decide.js +13 -15
  32. package/dist/commands/commitments/cost.d.ts +1 -1
  33. package/dist/commands/commitments/cost.js +1 -1
  34. package/dist/commands/commitments/create.d.ts +0 -7
  35. package/dist/commands/commitments/create.js +9 -98
  36. package/dist/commands/commitments/delivery.js +3 -21
  37. package/dist/commands/commitments/doctor.js +28 -27
  38. package/dist/commands/commitments/effective-policy.js +5 -36
  39. package/dist/commands/commitments/evaluations.d.ts +15 -0
  40. package/dist/commands/commitments/evaluations.js +54 -0
  41. package/dist/commands/commitments/pack/activate.d.ts +11 -0
  42. package/dist/commands/commitments/pack/activate.js +22 -0
  43. package/dist/commands/commitments/pack/bind.js +1 -1
  44. package/dist/commands/commitments/remediate.js +2 -5
  45. package/dist/commands/commitments/show.js +1 -1
  46. package/dist/commands/commitments/update.js +9 -9
  47. package/dist/commands/commitments/wake-capabilities.d.ts +1 -3
  48. package/dist/commands/commitments/wake-capabilities.js +5 -21
  49. package/dist/commands/daemon/install.js +4 -0
  50. package/dist/commands/generate-recovery-code.d.ts +13 -1
  51. package/dist/commands/generate-recovery-code.js +146 -13
  52. package/dist/commands/instructions/guide.d.ts +20 -0
  53. package/dist/commands/instructions/guide.js +84 -0
  54. package/dist/commands/instructions/install.js +57 -33
  55. package/dist/commands/instructions/setup.d.ts +14 -0
  56. package/dist/commands/instructions/setup.js +72 -0
  57. package/dist/commands/integrations/execute.js +1 -1
  58. package/dist/commands/login.d.ts +5 -6
  59. package/dist/commands/login.js +27 -11
  60. package/dist/commands/logout.js +7 -7
  61. package/dist/commands/security/overview.d.ts +11 -0
  62. package/dist/commands/security/overview.js +158 -0
  63. package/dist/commands/spaces/claim.d.ts +12 -0
  64. package/dist/commands/spaces/claim.js +42 -0
  65. package/dist/commands/spaces/ownership.d.ts +12 -0
  66. package/dist/commands/spaces/ownership.js +46 -0
  67. package/dist/commands/tasks/cancel-cascade.js +1 -1
  68. package/dist/commands/tasks/create-tree.d.ts +5 -1
  69. package/dist/commands/tasks/create-tree.js +35 -7
  70. package/dist/commands/tasks/interrupt.js +2 -2
  71. package/dist/commands/tasks/pause-schedule.js +1 -1
  72. package/dist/commands/token/list.d.ts +3 -5
  73. package/dist/commands/token/list.js +99 -57
  74. package/dist/commands/update.d.ts +14 -0
  75. package/dist/commands/update.js +61 -0
  76. package/dist/generated/managed-instruction-guides.json +9 -0
  77. package/dist/hooks/init.d.ts +5 -0
  78. package/dist/hooks/init.js +27 -0
  79. package/dist/lib/commitment-product.d.ts +0 -6
  80. package/dist/lib/commitment-product.js +1 -30
  81. package/dist/lib/commitments.d.ts +9 -53
  82. package/dist/lib/commitments.js +23 -146
  83. package/dist/lib/daemon-installer.d.ts +2 -0
  84. package/dist/lib/daemon-installer.js +100 -24
  85. package/dist/lib/first-party-harness-doctor.js +31 -1
  86. package/dist/lib/first-party-harness-remote-install.d.ts +71 -0
  87. package/dist/lib/first-party-harness-remote-install.js +310 -0
  88. package/dist/lib/first-party-harness-remote-update.d.ts +46 -0
  89. package/dist/lib/first-party-harness-remote-update.js +242 -0
  90. package/dist/lib/first-party-harness-startup-update.d.ts +26 -0
  91. package/dist/lib/first-party-harness-startup-update.js +166 -0
  92. package/dist/lib/first-party-harness-update.d.ts +46 -0
  93. package/dist/lib/first-party-harness-update.js +289 -0
  94. package/dist/lib/first-party-harness.js +7 -4
  95. package/dist/lib/managed-instruction-local.d.ts +12 -0
  96. package/dist/lib/managed-instruction-local.js +58 -0
  97. package/dist/lib/managed-instruction-setup.d.ts +35 -0
  98. package/dist/lib/managed-instruction-setup.js +306 -0
  99. package/dist/lib/memory-sync.d.ts +1 -0
  100. package/dist/lib/memory-sync.js +279 -68
  101. package/dist/lib/northstars.js +1 -1
  102. package/dist/lib/platform-update-local.d.ts +2 -0
  103. package/dist/lib/platform-update-local.js +113 -0
  104. package/dist/lib/platform-update.d.ts +39 -0
  105. package/dist/lib/platform-update.js +84 -0
  106. package/dist/lib/profile-machine-identity-guard.d.ts +2 -0
  107. package/dist/lib/profile-machine-identity-guard.js +13 -0
  108. package/dist/lib/prompt.d.ts +1 -0
  109. package/dist/lib/prompt.js +4 -3
  110. package/dist/lib/roles.d.ts +98 -0
  111. package/dist/lib/roles.js +45 -0
  112. package/dist/lib/task-batch.d.ts +9 -0
  113. package/dist/lib/task-batch.js +94 -17
  114. package/dist/lib/task-instruction-offer.d.ts +1 -4
  115. package/dist/lib/task-instruction-offer.js +3 -129
  116. package/dist/lib/update-check.d.ts +61 -53
  117. package/dist/lib/update-check.js +453 -314
  118. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/commitmentExecutionContract.d.ts +45 -0
  119. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/commitmentExecutionContract.js +550 -0
  120. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +3 -4
  121. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +3 -4
  122. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.d.ts +82 -0
  123. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.js +105 -0
  124. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.d.ts +110 -0
  125. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.js +945 -0
  126. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.d.ts +32 -0
  127. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.js +62 -0
  128. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -0
  129. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -1
  130. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +35 -5
  131. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.d.ts +63 -0
  132. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.js +169 -0
  133. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.d.ts +2 -0
  134. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.js +44 -0
  135. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.d.ts +7 -44
  136. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.js +8 -47
  137. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/commitmentExecutionContract.d.ts +45 -0
  138. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/commitmentExecutionContract.js +545 -0
  139. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +3 -4
  140. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +3 -4
  141. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.d.ts +82 -0
  142. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.js +102 -0
  143. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.d.ts +110 -0
  144. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.js +923 -0
  145. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.d.ts +32 -0
  146. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.js +59 -0
  147. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -0
  148. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -0
  149. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +35 -5
  150. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.d.ts +63 -0
  151. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.js +163 -0
  152. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.d.ts +2 -0
  153. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.js +41 -0
  154. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.d.ts +7 -44
  155. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.js +8 -47
  156. package/dist/node_modules/@skrr-ai/auth-core/package.json +41 -1
  157. package/dist/node_modules/@skrr-ai/data-provider/index.js +7362 -7232
  158. package/dist/node_modules/@skrr-ai/data-provider/package.json +2 -2
  159. package/docs/v1/README.md +28 -0
  160. package/docs/v1/api.md +55 -0
  161. package/docs/v1/changelog.md +16 -0
  162. package/docs/v1/check-examples.mjs +178 -0
  163. package/docs/v1/examples/api-readonly.mjs +144 -0
  164. package/docs/v1/operations.md +87 -0
  165. package/docs/v1/quickstart.md +71 -0
  166. package/oclif.manifest.json +46381 -45478
  167. package/package.json +16 -11
  168. package/dist/commands/commitments/autonomy.d.ts +0 -30
  169. package/dist/commands/commitments/autonomy.js +0 -67
  170. package/dist/commands/commitments/mode.js +0 -58
  171. package/dist/commands/commitments/pack/promote.d.ts +0 -19
  172. package/dist/commands/commitments/pack/promote.js +0 -46
  173. package/dist/commands/commitments/pack/shadow.d.ts +0 -20
  174. package/dist/commands/commitments/pack/shadow.js +0 -42
@@ -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;
@@ -0,0 +1,20 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class InstructionsGuide extends Command {
3
+ static baseFlags: {
4
+ bare: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
5
+ token: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
6
+ workspace: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ profile: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ raw: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ static description: string;
11
+ static args: {
12
+ name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
13
+ };
14
+ static flags: {
15
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
16
+ offline: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
17
+ };
18
+ static examples: string[];
19
+ run(): Promise<void>;
20
+ }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const core_1 = require("@oclif/core");
7
+ const node_crypto_1 = require("node:crypto");
8
+ const base_command_1 = require("../../base-command");
9
+ const managed_instruction_guides_json_1 = __importDefault(require("../../generated/managed-instruction-guides.json"));
10
+ const api_fetch_1 = require("../../lib/api-fetch");
11
+ const auth_core_init_1 = require("../../lib/auth-core-init");
12
+ const credential_resolver_1 = require("../../lib/credential-resolver");
13
+ const harness_context_1 = require("../../lib/harness-context");
14
+ const keychain_1 = require("../../lib/keychain");
15
+ // Documentation stays available when a daemon-issued Agent credential is
16
+ // unavailable. Do not run BaseCommand's mandatory credential initialization.
17
+ class InstructionsGuide extends core_1.Command {
18
+ static baseFlags = base_command_1.BaseCommand.baseFlags;
19
+ static description = 'Read the detailed task-management guide on demand';
20
+ static args = {
21
+ name: core_1.Args.string({
22
+ description: 'Guide to read',
23
+ options: ['task-management'],
24
+ default: 'task-management',
25
+ ignoreStdin: true,
26
+ }),
27
+ };
28
+ static flags = {
29
+ json: core_1.Flags.boolean({ description: 'Output the guide and source provenance as JSON' }),
30
+ offline: core_1.Flags.boolean({
31
+ description: 'Read the bundled release guide without checking credentials or the server',
32
+ }),
33
+ };
34
+ static examples = [
35
+ '<%= config.bin %> instructions guide task-management',
36
+ '<%= config.bin %> instructions guide --json',
37
+ ];
38
+ async run() {
39
+ const { flags } = await this.parse(InstructionsGuide);
40
+ let guide = managed_instruction_guides_json_1.default['oversky-task-management'];
41
+ let source = 'bundled';
42
+ let reason = flags.offline ? 'offline-requested' : 'no-session';
43
+ if (!flags.offline) {
44
+ try {
45
+ (0, keychain_1.setActiveProfile)(flags.profile ?? process.env.OVERSKY_PROFILE ?? '');
46
+ const autonomous = (0, credential_resolver_1.resolveTrustedAutonomousCredential)();
47
+ if (!autonomous && !flags.bare && !(0, harness_context_1.isHarnessCliContext)())
48
+ await (0, auth_core_init_1.prefetchCliAuth)();
49
+ const credential = autonomous ??
50
+ (await (0, credential_resolver_1.resolveCredential)({
51
+ flagToken: flags.token,
52
+ bareMode: flags.bare,
53
+ allowUnreadableStoredCredential: true,
54
+ }));
55
+ if (credential.token) {
56
+ reason = 'server-unavailable';
57
+ const response = await (0, api_fetch_1.apiFetch)('/api/instructions/presets/oversky-task-management/guide', { credential, signal: AbortSignal.timeout(3_000) });
58
+ if ((0, node_crypto_1.createHash)('sha256').update(response.body).digest('hex') !== response.hash) {
59
+ throw new Error('The server guide hash does not match its body.');
60
+ }
61
+ guide = response;
62
+ source = 'server';
63
+ reason = undefined;
64
+ }
65
+ }
66
+ catch {
67
+ // A missing/unreadable auth envelope or delegated daemon cannot make
68
+ // a read-only local guide unavailable, and must never emit auth JSON.
69
+ reason ??= 'server-unavailable';
70
+ }
71
+ }
72
+ if (flags.json)
73
+ this.log(JSON.stringify({
74
+ ...guide,
75
+ provenance: { source, fallback: source === 'bundled', ...(reason ? { reason } : {}) },
76
+ }, null, 2));
77
+ else {
78
+ if (source === 'bundled')
79
+ this.logToStderr(`Using the CLI’s bundled task-management guide (template v${guide.templateVersion}).`);
80
+ this.log(guide.body);
81
+ }
82
+ }
83
+ }
84
+ exports.default = InstructionsGuide;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.conflictingTargetOf = conflictingTargetOf;
4
4
  exports.describeTargetConflict = describeTargetConflict;
5
5
  const core_1 = require("@oclif/core");
6
+ const node_crypto_1 = require("node:crypto");
6
7
  const auth_core_1 = require("@skrr-ai/auth-core");
7
8
  const data_provider_1 = require("@skrr-ai/data-provider");
8
9
  const base_command_1 = require("../../base-command");
@@ -107,41 +108,67 @@ class InstructionsInstall extends base_command_1.BaseCommand {
107
108
  exit: 2,
108
109
  });
109
110
  }
110
- let previewBody;
111
+ const daemonId = await this.resolveDaemon(flags.daemon);
112
+ const inputs = providers.map((provider) => ({
113
+ daemonId,
114
+ bundleId: flags.bundle,
115
+ provider,
116
+ scope,
117
+ mode: flags.mode,
118
+ projectPath: flags.project,
119
+ trackingMode: flags.pin ? 'pinned' : 'latest',
120
+ pinnedVersionId: flags.pin,
121
+ label: flags.label,
122
+ }));
123
+ // Inspect the selected machine before any consent or target write. This is
124
+ // also required for --yes: unattended consent does not waive readability.
125
+ let preparedTargets;
111
126
  try {
112
- const bundle = (await data_provider_1.dataService.getManagedInstructionBundle(flags.bundle)).bundle;
113
- if (flags.pin) {
114
- const versions = await data_provider_1.dataService.listManagedInstructionVersions(flags.bundle, true);
115
- const pinned = versions.versions.find((version) => version.id === flags.pin);
116
- if (!pinned || pinned.archivedAt) {
117
- this.error('The requested pinned version is missing or archived', { exit: 2 });
118
- }
119
- previewBody = pinned.body;
120
- }
121
- else {
122
- if (!bundle.currentVersion)
123
- this.error('The prompt has no current version', { exit: 2 });
124
- previewBody = bundle.currentVersion.body;
125
- }
127
+ preparedTargets = await Promise.all(inputs.map((input) => data_provider_1.dataService.prepareManagedInstructionTarget(input)));
126
128
  }
127
129
  catch (error) {
128
130
  this.handleApiError(error);
129
131
  }
130
- if (providers.includes('codex') && Buffer.byteLength(previewBody, 'utf8') > 30 * 1024) {
131
- this.error('Codex managed instruction bodies are limited to 30 KiB; no targets were created', { exit: 2 });
132
+ for (let index = 0; index < preparedTargets.length; index += 1) {
133
+ const entry = preparedTargets[index];
134
+ const provider = providers[index];
135
+ const placement = entry.providers.find((candidate) => candidate.provider === provider);
136
+ const preview = entry.previews.find((candidate) => candidate.provider === provider);
137
+ if (entry.daemonId !== daemonId || entry.readiness !== 'ready') {
138
+ this.error(entry.message ||
139
+ 'The selected daemon cannot inspect its instruction files. Update or reconnect it and retry; no targets were created.', { exit: 1 });
140
+ }
141
+ if (!placement?.canInstall ||
142
+ !placement.instructionPath ||
143
+ placement.loadingStatus === 'shadowed') {
144
+ this.error(`${(0, harness_provider_input_1.canonicalHarnessInput)(provider)} cannot use this placement: ${placement?.issues.map((issue) => issue.message).join(' ') || placement?.loadingStatus || 'preflight unavailable'}. No targets were created.`, { exit: 1 });
145
+ }
146
+ if (!preview || (0, node_crypto_1.createHash)('sha256').update(preview.body).digest('hex') !== preview.hash) {
147
+ this.error('The instruction preview could not be verified; no targets were created.', {
148
+ exit: 1,
149
+ });
150
+ }
151
+ if (!flags.json) {
152
+ this.log(`${(0, harness_provider_input_1.canonicalHarnessInput)(provider)} on ${daemonId}: ${placement.instructionPath} (${placement.loadingStatus})`);
153
+ for (const issue of placement.issues)
154
+ this.log(` ${issue.severity}: ${issue.message}`);
155
+ }
132
156
  }
133
157
  // The body is shown so the confirmation below is informed. With `--yes`
134
158
  // there is no confirmation to inform, and printing a ~9 KB body anyway put
135
159
  // the command's actual outcome — often a refusal — a screen of scrollback
136
160
  // away (OSK-4631). Say what was authorized, and where to read it.
137
161
  if (!flags.json && !flags.yes) {
138
- this.log('Exact managed instruction body:');
139
- this.log('---');
140
- this.log(previewBody);
141
- this.log('---');
162
+ for (let index = 0; index < providers.length; index += 1) {
163
+ const preview = preparedTargets[index].previews.find((entry) => entry.provider === providers[index]);
164
+ this.log(`Exact ${(0, harness_provider_input_1.canonicalHarnessInput)(providers[index])} managed instruction body (SHA-256 ${preview.hash}):`);
165
+ this.log('---');
166
+ this.log(preview.body);
167
+ this.log('---');
168
+ }
142
169
  }
143
170
  else if (!flags.json) {
144
- this.log(`Authorizing ${Buffer.byteLength(previewBody, 'utf8').toLocaleString()} bytes of instruction ` +
171
+ this.log(`Authorizing ${Buffer.byteLength(preparedTargets[0].version.body, 'utf8').toLocaleString()} bytes of instruction ` +
145
172
  `${flags.bundle}; \`${this.config.bin} instructions show ${flags.bundle}\` prints them.`);
146
173
  }
147
174
  const teamWarning = scope === 'project' && flags.mode === 'team'
@@ -150,20 +177,17 @@ class InstructionsInstall extends base_command_1.BaseCommand {
150
177
  const confirmed = await (0, prompt_1.confirmDestructive)(`Authorize continuous ${providers.join(', ')} reconciliation on this target?${teamWarning}`, flags.yes);
151
178
  if (!confirmed)
152
179
  return this.log('Aborted.');
153
- const daemonId = await this.resolveDaemon(flags.daemon);
154
180
  const results = [];
155
- for (const provider of providers) {
181
+ for (let index = 0; index < providers.length; index += 1) {
182
+ const provider = providers[index];
183
+ const entry = preparedTargets[index];
156
184
  try {
157
185
  results.push(await data_provider_1.dataService.createManagedInstructionTarget({
158
- daemonId,
159
- bundleId: flags.bundle,
160
- provider,
161
- scope,
162
- mode: flags.mode,
163
- projectPath: flags.project,
164
- trackingMode: flags.pin ? 'pinned' : 'latest',
165
- pinnedVersionId: flags.pin,
166
- label: flags.label,
186
+ ...inputs[index],
187
+ expectedHash: entry.version.hash,
188
+ expectedVersionId: entry.version.id,
189
+ expectedRenderedHash: entry.previews.find((preview) => preview.provider === provider)
190
+ .hash,
167
191
  }));
168
192
  }
169
193
  catch (error) {
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class InstructionsSetup extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ daemon: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ provider: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ 'update-policy': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ review: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const auth_core_1 = require("@skrr-ai/auth-core");
5
+ const base_command_1 = require("../../base-command");
6
+ const harness_provider_input_1 = require("../../lib/harness-provider-input");
7
+ const managed_instruction_setup_1 = require("../../lib/managed-instruction-setup");
8
+ class InstructionsSetup extends base_command_1.BaseCommand {
9
+ static description = 'Review and install removable global instructions for coding agents on this machine.\n\n' +
10
+ 'Verifies the local daemon’s account, server and profile, then asks that daemon to check ' +
11
+ 'its real instruction files. Choose providers separately and choose automatic updates or ' +
12
+ 'a fixed current version. Existing installation decisions are preserved. Pass --daemon ' +
13
+ 'to explicitly choose another connected machine. No files are written by the CLI.';
14
+ static examples = [
15
+ '<%= config.bin %> instructions setup',
16
+ '<%= config.bin %> instructions setup --provider codex --review',
17
+ '<%= config.bin %> instructions setup --provider claude --update-policy pinned --yes --json',
18
+ '<%= config.bin %> instructions setup --daemon <id> --provider codex --update-policy latest --yes',
19
+ ];
20
+ static flags = {
21
+ daemon: core_1.Flags.string({
22
+ description: 'Explicit connected daemon ID; defaults only to a verified daemon on this computer',
23
+ }),
24
+ provider: core_1.Flags.string({
25
+ description: 'Provider to set up or revisit (repeatable); otherwise offers newly detected providers',
26
+ options: (0, harness_provider_input_1.harnessFlagOptions)(['claude', 'codex', auth_core_1.FIRST_PARTY_HARNESS.provider]),
27
+ multiple: true,
28
+ }),
29
+ 'update-policy': core_1.Flags.string({
30
+ description: 'Track automatic updates, or pin today’s reviewed version',
31
+ options: ['latest', 'pinned'],
32
+ }),
33
+ yes: core_1.Flags.boolean({
34
+ char: 'y',
35
+ description: 'Authorize selected providers and update policy without prompting; requires --provider and --update-policy',
36
+ }),
37
+ review: core_1.Flags.boolean({
38
+ description: 'Show exact previews and daemon preflight without installing or changing decisions',
39
+ }),
40
+ json: core_1.Flags.boolean({ description: 'Output one JSON result; requires --review or --yes' }),
41
+ };
42
+ async run() {
43
+ this.requireAuth();
44
+ const { flags } = await this.parse(InstructionsSetup);
45
+ if (flags.json && !flags.review && !flags.yes)
46
+ this.error('--json requires --review or --yes', { exit: 2 });
47
+ if (flags.review && flags.yes)
48
+ this.error('--review cannot be combined with --yes', { exit: 2 });
49
+ try {
50
+ const result = await (0, managed_instruction_setup_1.runManagedInstructionSetup)({
51
+ trigger: 'manual',
52
+ log: flags.json ? () => { } : (message) => this.log(message),
53
+ daemonId: flags.daemon,
54
+ providers: flags.provider
55
+ ? (0, harness_provider_input_1.canonicalHarnessInputs)(flags.provider)
56
+ : undefined,
57
+ trackingMode: flags['update-policy'],
58
+ yes: flags.yes,
59
+ review: flags.review,
60
+ json: flags.json,
61
+ });
62
+ if (flags.json)
63
+ this.log(JSON.stringify(result, null, 2));
64
+ if (result.status === 'unavailable')
65
+ this.exit(1);
66
+ }
67
+ catch (error) {
68
+ this.handleApiError(error);
69
+ }
70
+ }
71
+ }
72
+ exports.default = InstructionsSetup;
@@ -33,7 +33,7 @@ class IntegrationsExecute extends base_command_1.BaseCommand {
33
33
  description: 'Agent ID (falls back to OVERSKY_AGENT_ID, then primary agent)',
34
34
  }),
35
35
  'request-id': core_1.Flags.string({
36
- description: 'Idempotency key — reuse to safely retry side-effecting tools (auto-generated if omitted)',
36
+ description: 'Reuse only for the same tool, arguments and connection. A pending or unknown outcome requires reconciliation before another write (auto-generated if omitted)',
37
37
  }),
38
38
  json: core_1.Flags.boolean({
39
39
  description: 'Output full response as JSON (recommended for daemon use)',
@@ -12,14 +12,13 @@ import { BaseCommand } from '../base-command';
12
12
  * Refresh, rotation, family revoke, and the cross-process auth.lock are
13
13
  * all the same substrate the daemon uses (@skrr-ai/auth-core).
14
14
  *
15
- * CI. There is no long-lived token that `skrr` commands accept yet (OSK-13208).
16
- * `skrr create-token` mints an `osk_ci_*` token, and that token only
17
- * bootstraps a DAEMON (`OVERSKY_TOKEN=osk_ci_... skrrd start`): every `skrr`
18
- * command rejects it with 401 (see create-token.ts and base-command.ts). What
19
- * works today is a session minted for the machine with
15
+ * CI. `skrr tokens create` mints an expiring, revocable personal access token
16
+ * (`osk_pat_*`) for SKRR_TOKEN. It does not rotate and is distinct from a
17
+ * per-machine SESSION minted with
20
18
  * `skrr token mint --for <id>`, restored by the `OVERSKY_REFRESH_TOKEN` path
21
19
  * below — whose refresh token rotates on use — or `OVERSKY_AUTH_HELPER`.
22
- * docs/cli/SKRR_AUTH.md ("CI today") states the caveats.
20
+ * docs/cli/SKRR_AUTH.md states credential precedence and revocation scopes.
21
+ * Legacy `skrr create-token` credentials only bootstrap a daemon.
23
22
  *
24
23
  * L13 — non-interactive restore from a refresh token. When
25
24
  * `OVERSKY_REFRESH_TOKEN` is set in the environment, the browser /