@skrr-ai/cli 0.1.8 → 0.1.10

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 (170) hide show
  1. package/dist/base-command.d.ts +27 -0
  2. package/dist/base-command.js +83 -11
  3. package/dist/commands/agent/message/send.js +1 -1
  4. package/dist/commands/agents/chat.d.ts +1 -1
  5. package/dist/commands/agents/chat.js +1 -1
  6. package/dist/commands/browser/install.d.ts +1 -1
  7. package/dist/commands/browser/install.js +10 -2
  8. package/dist/commands/browser/mcp-config.js +3 -1
  9. package/dist/commands/browser/skill/show.js +1 -1
  10. package/dist/commands/browser/status.js +4 -1
  11. package/dist/commands/browser/uninstall.d.ts +1 -1
  12. package/dist/commands/browser/uninstall.js +10 -2
  13. package/dist/commands/browser.d.ts +1 -1
  14. package/dist/commands/browser.js +3 -3
  15. package/dist/commands/code/index.js +6 -3
  16. package/dist/commands/code/install.d.ts +61 -0
  17. package/dist/commands/code/install.js +247 -0
  18. package/dist/commands/code/oversky-agent.d.ts +2 -1
  19. package/dist/commands/code/oversky-agent.js +12 -8
  20. package/dist/commands/commands.js +1 -1
  21. package/dist/commands/context.js +1 -1
  22. package/dist/commands/create-token.js +1 -1
  23. package/dist/commands/daemon/byok.d.ts +1 -0
  24. package/dist/commands/daemon/byok.js +14 -1
  25. package/dist/commands/daemon/index.js +18 -7
  26. package/dist/commands/daemon/install.d.ts +1 -0
  27. package/dist/commands/daemon/install.js +22 -3
  28. package/dist/commands/daemon/logs.d.ts +1 -0
  29. package/dist/commands/daemon/logs.js +4 -0
  30. package/dist/commands/daemon/start.d.ts +1 -0
  31. package/dist/commands/daemon/start.js +15 -2
  32. package/dist/commands/daemon/status.d.ts +1 -0
  33. package/dist/commands/daemon/status.js +14 -1
  34. package/dist/commands/daemon/stop.d.ts +1 -0
  35. package/dist/commands/daemon/stop.js +14 -1
  36. package/dist/commands/daemon/uninstall.d.ts +1 -0
  37. package/dist/commands/daemon/uninstall.js +15 -2
  38. package/dist/commands/daemon/usage.d.ts +1 -0
  39. package/dist/commands/daemon/usage.js +5 -1
  40. package/dist/commands/doctor.js +2 -2
  41. package/dist/commands/github/allow-tools.d.ts +1 -0
  42. package/dist/commands/github/app/install.d.ts +1 -0
  43. package/dist/commands/github/app/status.d.ts +1 -0
  44. package/dist/commands/github/connect.d.ts +1 -0
  45. package/dist/commands/github/connections.d.ts +1 -0
  46. package/dist/commands/github/disconnect.d.ts +1 -0
  47. package/dist/commands/github/finalize.d.ts +1 -0
  48. package/dist/commands/github/reconnect.d.ts +1 -0
  49. package/dist/commands/github/run.d.ts +1 -0
  50. package/dist/commands/github/tools.d.ts +1 -0
  51. package/dist/commands/goals/create.js +2 -2
  52. package/dist/commands/goals/show.js +5 -0
  53. package/dist/commands/harnesses/list.js +14 -3
  54. package/dist/commands/harnesses/show.js +14 -0
  55. package/dist/commands/instructions/activate.js +1 -1
  56. package/dist/commands/instructions/archive-version.js +1 -1
  57. package/dist/commands/instructions/delete.js +1 -1
  58. package/dist/commands/instructions/diff.js +1 -1
  59. package/dist/commands/instructions/install.js +7 -1
  60. package/dist/commands/instructions/restore.js +1 -1
  61. package/dist/commands/instructions/show.js +17 -1
  62. package/dist/commands/instructions/update.js +1 -1
  63. package/dist/commands/integrations/connections/connect.d.ts +1 -0
  64. package/dist/commands/integrations/connections/disconnect.d.ts +1 -0
  65. package/dist/commands/integrations/connections/finalize.d.ts +1 -0
  66. package/dist/commands/integrations/connections/list.d.ts +1 -0
  67. package/dist/commands/integrations/connections/reconnect.d.ts +1 -0
  68. package/dist/commands/integrations/execute.d.ts +1 -0
  69. package/dist/commands/integrations/search.d.ts +1 -0
  70. package/dist/commands/integrations/toolkits.d.ts +1 -0
  71. package/dist/commands/integrations/tools/allow.d.ts +1 -0
  72. package/dist/commands/integrations/tools/list.d.ts +1 -0
  73. package/dist/commands/list-daemons.d.ts +2 -7
  74. package/dist/commands/list-daemons.js +13 -13
  75. package/dist/commands/login.js +78 -33
  76. package/dist/commands/logout.js +52 -1
  77. package/dist/commands/machines/hosted/start.js +1 -1
  78. package/dist/commands/pair.js +2 -2
  79. package/dist/commands/projects/create.js +6 -1
  80. package/dist/commands/projects/tasks.js +6 -1
  81. package/dist/commands/revoke-daemon.js +6 -2
  82. package/dist/commands/set-capabilities.js +6 -2
  83. package/dist/commands/skills/install.js +1 -1
  84. package/dist/commands/skills/library.js +2 -2
  85. package/dist/commands/spaces/create.js +6 -2
  86. package/dist/commands/tasks/complete.d.ts +1 -1
  87. package/dist/commands/tasks/complete.js +21 -8
  88. package/dist/commands/tasks/create.js +4 -1
  89. package/dist/commands/tasks/current.js +1 -1
  90. package/dist/commands/tasks/delete.d.ts +1 -0
  91. package/dist/commands/tasks/delete.js +9 -0
  92. package/dist/commands/tasks/export.js +5 -1
  93. package/dist/commands/tasks/follow-up.js +2 -1
  94. package/dist/commands/tasks/list.js +5 -1
  95. package/dist/commands/tasks/pr.js +1 -1
  96. package/dist/commands/tasks/show.js +10 -1
  97. package/dist/commands/tasks/triage.js +2 -1
  98. package/dist/commands/tasks/update.js +4 -1
  99. package/dist/commands/tasks/upsert.js +4 -1
  100. package/dist/commands/usage.js +1 -1
  101. package/dist/commands/whoami.js +4 -1
  102. package/dist/lib/agentic-stream.js +21 -1
  103. package/dist/lib/api-fetch.d.ts +1 -1
  104. package/dist/lib/api-fetch.js +1 -1
  105. package/dist/lib/auth-core-init.js +23 -3
  106. package/dist/lib/auth-storage.d.ts +0 -12
  107. package/dist/lib/auth-storage.js +37 -2
  108. package/dist/lib/config.d.ts +43 -3
  109. package/dist/lib/config.js +391 -31
  110. package/dist/lib/created-line.d.ts +22 -0
  111. package/dist/lib/created-line.js +25 -0
  112. package/dist/lib/daemon-binding.d.ts +29 -0
  113. package/dist/lib/daemon-binding.js +63 -0
  114. package/dist/lib/daemon-ref.d.ts +31 -0
  115. package/dist/lib/daemon-ref.js +47 -0
  116. package/dist/lib/daemonBroker.js +1 -1
  117. package/dist/lib/delegated-cli.js +3 -3
  118. package/dist/lib/exec-oversky.d.ts +18 -1
  119. package/dist/lib/exec-oversky.js +21 -4
  120. package/dist/lib/html-text.d.ts +15 -0
  121. package/dist/lib/html-text.js +40 -0
  122. package/dist/lib/keychain.d.ts +43 -0
  123. package/dist/lib/keychain.js +145 -4
  124. package/dist/lib/login.js +3 -3
  125. package/dist/lib/node-adapter.js +1 -1
  126. package/dist/lib/oauthLogin.js +17 -6
  127. package/dist/lib/sky-code-agent.d.ts +1 -1
  128. package/dist/lib/sky-code-agent.js +8 -8
  129. package/dist/lib/sky-code-broker.js +18 -2
  130. package/dist/lib/sky-code-doctor.js +120 -4
  131. package/dist/lib/sky-code-managed.d.ts +12 -0
  132. package/dist/lib/sky-code-managed.js +21 -7
  133. package/dist/lib/sky-code.d.ts +16 -1
  134. package/dist/lib/sky-code.js +110 -7
  135. package/dist/lib/task-execution.js +4 -11
  136. package/dist/lib/tasks.d.ts +1 -0
  137. package/dist/lib/tasks.js +9 -6
  138. package/dist/lib/update-check.js +1 -1
  139. package/dist/lib/usage-discovery.js +1 -1
  140. package/dist/lib/web-url.js +12 -1
  141. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.d.ts +1 -1
  142. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.js +2 -2
  143. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -0
  144. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +13 -1
  145. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/linux.js +16 -3
  146. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.d.ts +0 -9
  147. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.js +18 -1
  148. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/loginLocalhost.js +3 -3
  149. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +3 -3
  150. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/runtime.d.ts +7 -3
  151. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.d.ts +96 -0
  152. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.js +115 -0
  153. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.d.ts +1 -1
  154. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.js +2 -2
  155. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -0
  156. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -0
  157. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/linux.js +16 -3
  158. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.d.ts +0 -9
  159. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.js +18 -1
  160. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/loginLocalhost.js +3 -3
  161. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +3 -3
  162. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/runtime.d.ts +7 -3
  163. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.d.ts +96 -0
  164. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.js +107 -0
  165. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  166. package/dist/node_modules/@skrr-ai/data-provider/index.js +4206 -4191
  167. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.js +2 -2
  168. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.js +2 -2
  169. package/oclif.manifest.json +7969 -1397
  170. package/package.json +22 -19
@@ -1,4 +1,5 @@
1
1
  import { Command, Flags, type Interfaces } from '@oclif/core';
2
+ export { resolveProfileFromArgv } from './lib/keychain';
2
3
  import { type AssigneeResolution, type MemberKind, type SpaceMember } from './lib/assignee-resolver';
3
4
  import { type CliConfig } from './lib/config';
4
5
  import { type OutboxMethod } from './lib/outbox';
@@ -56,6 +57,7 @@ export declare abstract class BaseCommand extends Command {
56
57
  bare: Interfaces.BooleanFlag<boolean>;
57
58
  token: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
58
59
  workspace: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
60
+ profile: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
59
61
  };
60
62
  protected cliConfig: CliConfig;
61
63
  /**
@@ -393,5 +395,30 @@ export declare function describeAssigneeNotInSpace(serverMessage: string | undef
393
395
  *
394
396
  * Exported for tests.
395
397
  */
398
+ /**
399
+ * What a 403 means, given whether the server said anything.
400
+ *
401
+ * Exported and pure so the wording is testable without an oclif runtime — the
402
+ * convention `isRetryableFailure` follows below.
403
+ *
404
+ * A 403 WITH a body is the API deciding, and it says why; we relay it. A 403
405
+ * with NO body did not come from the API — every application 403 in this
406
+ * codebase carries a message — so it is the edge refusing the request before it
407
+ * arrived, and the two must not share a sentence.
408
+ *
409
+ * This used to assert "your account does not have access to this resource" for
410
+ * both. For an edge refusal that names a cause this process never observed, and
411
+ * it is the expensive kind of wrong: it sends the user to audit their
412
+ * membership, their role and their token, which is the one place the problem is
413
+ * not. Dogfooded as OSK-263 — `skrr tasks create` refused a task description
414
+ * containing a dotted AWS-credentials path, in a project the same session had
415
+ * created a minute earlier, as an ADMIN who owned the space, and finding the
416
+ * real cause took a line-by-line bisect of the description body.
417
+ *
418
+ * The 404 branch has always done the honest thing (`bodyMessage || 'Not
419
+ * found.'`, which claims nothing); this brings 403 into line and adds the one
420
+ * lead worth having.
421
+ */
422
+ export declare function describeForbidden(bodyMessage?: string | null): string;
396
423
  export declare function isRetryableFailure(status: number | undefined, body: Record<string, unknown> | null): boolean;
397
424
  export { Flags };
@@ -1,14 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Flags = exports.BaseCommand = void 0;
3
+ exports.Flags = exports.BaseCommand = exports.resolveProfileFromArgv = void 0;
4
4
  exports.assigneePayloadNeedsResolution = assigneePayloadNeedsResolution;
5
5
  exports.parseGlobalFlags = parseGlobalFlags;
6
6
  exports.withSubject = withSubject;
7
7
  exports.describeHarnessUnavailable = describeHarnessUnavailable;
8
8
  exports.describeAssigneeNotInSpace = describeAssigneeNotInSpace;
9
+ exports.describeForbidden = describeForbidden;
9
10
  exports.isRetryableFailure = isRetryableFailure;
10
11
  const core_1 = require("@oclif/core");
11
12
  Object.defineProperty(exports, "Flags", { enumerable: true, get: function () { return core_1.Flags; } });
13
+ // Re-exported from its home in `lib/keychain`, beside the other profile
14
+ // functions. It lived here while `init()` was its only caller; a lib module
15
+ // needing it would otherwise have to import the command base class, and with
16
+ // it the whole auth stack, to read two strings out of argv.
17
+ var keychain_1 = require("./lib/keychain");
18
+ Object.defineProperty(exports, "resolveProfileFromArgv", { enumerable: true, get: function () { return keychain_1.resolveProfileFromArgv; } });
12
19
  const data_provider_1 = require("@skrr-ai/data-provider");
13
20
  const auth_core_1 = require("@skrr-ai/auth-core");
14
21
  const assignee_resolver_1 = require("./lib/assignee-resolver");
@@ -16,7 +23,7 @@ const task_ref_resolver_1 = require("./lib/task-ref-resolver");
16
23
  const space_resolver_1 = require("./lib/space-resolver");
17
24
  const projects_1 = require("./lib/projects");
18
25
  const config_1 = require("./lib/config");
19
- const keychain_1 = require("./lib/keychain");
26
+ const keychain_2 = require("./lib/keychain");
20
27
  const outbox_1 = require("./lib/outbox");
21
28
  const node_adapter_1 = require("./lib/node-adapter");
22
29
  const migrate_1 = require("./lib/migrate");
@@ -96,6 +103,10 @@ class BaseCommand extends core_1.Command {
96
103
  workspace: core_1.Flags.string({
97
104
  description: 'Workspace ID for workspace-scoped requests (also sets X-Workspace-Id).',
98
105
  }),
106
+ profile: core_1.Flags.string({
107
+ description: 'Which stored session to use. Each profile keeps its own credential and deployment, ' +
108
+ 'so signing into one does not sign you out of another. Also OVERSKY_PROFILE.',
109
+ }),
99
110
  };
100
111
  cliConfig;
101
112
  /**
@@ -108,6 +119,19 @@ class BaseCommand extends core_1.Command {
108
119
  bareMode = false;
109
120
  async init() {
110
121
  await super.init();
122
+ // The profile is resolved from raw argv, BEFORE oclif parses anything.
123
+ //
124
+ // It has to be. `parseBaseFlags()` runs far below, after migration and
125
+ // after the credential machinery has already read the keychain — and the
126
+ // profile decides WHICH keychain service and WHICH config file all of that
127
+ // touches. Waiting for the parser would mean choosing a profile after
128
+ // having already read the wrong one's credential.
129
+ //
130
+ // The daemon has the same constraint and solves it with commander's
131
+ // `preAction` hook; oclif has no equivalent that beats `init()`, so this
132
+ // scans argv itself. The flag is still declared in `baseFlags` above, which
133
+ // is what puts it in `--help` and keeps oclif from rejecting it.
134
+ (0, keychain_2.setActiveProfile)((0, keychain_2.resolveProfileFromArgv)(this.argv) ?? process.env.OVERSKY_PROFILE ?? '');
111
135
  // A daemon-stamped autonomous process is a separate principal, not a
112
136
  // convenient place to inherit the user's CLI login. Resolve this before
113
137
  // touching migration, Keychain/file auth, refresh, or daemon auto-broker
@@ -119,7 +143,7 @@ class BaseCommand extends core_1.Command {
119
143
  const delegatedCredential = await (0, delegated_cli_1.fetchDelegatedCliCredential)();
120
144
  if (!delegatedCredential) {
121
145
  this.failWithCliError({
122
- message: 'Daemon delegated Sky credential is unavailable.',
146
+ message: 'Daemon delegated skrr credential is unavailable.',
123
147
  code: 'DELEGATED_CLI_CREDENTIAL_UNAVAILABLE',
124
148
  exit: 75,
125
149
  retryable: true,
@@ -130,7 +154,7 @@ class BaseCommand extends core_1.Command {
130
154
  }
131
155
  catch (err) {
132
156
  this.failWithCliError({
133
- message: `Daemon delegated Sky credential is unavailable: ${err.message}`,
157
+ message: `Daemon delegated skrr credential is unavailable: ${err.message}`,
134
158
  code: 'DELEGATED_CLI_CREDENTIAL_UNAVAILABLE',
135
159
  exit: 75,
136
160
  retryable: true,
@@ -269,7 +293,7 @@ class BaseCommand extends core_1.Command {
269
293
  // The user is about to see "Not signed in" — give them the
270
294
  // exact fix. One stderr line so it doesn't pollute --json
271
295
  // consumers on stdout.
272
- process.stderr.write(`[sky] Local oversky daemon is logged in to ${autoResult.outcome.daemonServerUrl}, ` +
296
+ process.stderr.write(`[skrr] Local skrr runtime is logged in to ${autoResult.outcome.daemonServerUrl}, ` +
273
297
  `but skrr is configured for ${this.cliConfig.baseURL}. ` +
274
298
  `Run \`skrr login --base-url ${autoResult.outcome.daemonServerUrl}\` to use the daemon.\n`);
275
299
  }
@@ -277,12 +301,12 @@ class BaseCommand extends core_1.Command {
277
301
  }
278
302
  if (!this.resolvedCredential.token && ignoredStoredCredential) {
279
303
  if (ignoredStoredCredential.originStatus === 'mismatch') {
280
- process.stderr.write(`[sky] Ignoring stored credentials issued by ${ignoredStoredCredential.storedServerOrigin}; ` +
304
+ process.stderr.write(`[skrr] Ignoring stored credentials issued by ${ignoredStoredCredential.storedServerOrigin}; ` +
281
305
  `skrr is configured for ${this.cliConfig.baseURL}. Run \`skrr login --base-url ${this.cliConfig.baseURL}\` ` +
282
306
  `or switch back to the issuing server.\n`);
283
307
  }
284
308
  else if (ignoredStoredCredential.originStatus === 'unbound') {
285
- process.stderr.write(`[sky] Ignoring legacy credentials that are not bound to a server. ` +
309
+ process.stderr.write(`[skrr] Ignoring legacy credentials that are not bound to a server. ` +
286
310
  `Run \`skrr login --base-url ${this.cliConfig.baseURL}\` once to bind a fresh session.\n`);
287
311
  }
288
312
  }
@@ -809,7 +833,7 @@ class BaseCommand extends core_1.Command {
809
833
  this.failWithCliError({
810
834
  message: say(presentedCiToken
811
835
  ? `Authentication failed: this is an \`osk_ci_*\` token, which only bootstraps a ` +
812
- `daemon (\`OVERSKY_TOKEN=<token> oversky start\`). ${this.config.bin} commands do ` +
836
+ `daemon (\`OVERSKY_TOKEN=<token> skrrd start\`). ${this.config.bin} commands do ` +
813
837
  `not accept it — sign in with \`${this.config.bin} login\` instead.`
814
838
  : `Authentication failed. Run \`${this.config.bin} login\` to sign in again.`),
815
839
  code: bodyCode || 'AUTHENTICATION_FAILED',
@@ -819,8 +843,24 @@ class BaseCommand extends core_1.Command {
819
843
  });
820
844
  }
821
845
  else if (e.status === 403) {
846
+ // A 403 WITH a body is the API deciding; it says why, and we relay it.
847
+ //
848
+ // A 403 with NO body did not come from the API — every application 403
849
+ // here carries a message — so it is the edge refusing the request before
850
+ // it arrived. Asserting "your account does not have access" for that case
851
+ // is a cause this process never observed, and it is the expensive kind of
852
+ // wrong: it sends the user to audit their membership, their role and
853
+ // their token, which is the one place the problem is not.
854
+ //
855
+ // Dogfooded (OSK-263): `skrr tasks create` refused a task description
856
+ // containing a dotted AWS-credentials path, in a project the same
857
+ // session had created a minute earlier, as an ADMIN who owned the space.
858
+ // Finding it took a line-by-line bisect of the body, because the message
859
+ // named a cause and the cause was fiction. The 404 branch below has
860
+ // always done the honest thing — `bodyMessage || 'Not found.'`, which
861
+ // claims nothing.
822
862
  this.failWithCliError({
823
- message: say(bodyMessage || 'Forbidden: your account does not have access to this resource.'),
863
+ message: say(describeForbidden(bodyMessage)),
824
864
  code: bodyCode || 'FORBIDDEN',
825
865
  status: e.status,
826
866
  exit: 3,
@@ -931,7 +971,7 @@ class BaseCommand extends core_1.Command {
931
971
  */
932
972
  noteControlPlaneOutage(status) {
933
973
  try {
934
- (0, outbox_1.recordIncident)({ baseURL: this.cliConfig.baseURL, status }, (0, keychain_1.getActiveProfile)());
974
+ (0, outbox_1.recordIncident)({ baseURL: this.cliConfig.baseURL, status }, (0, keychain_2.getActiveProfile)());
935
975
  }
936
976
  catch {
937
977
  /* the surface is a convenience; never let it mask the real error */
@@ -987,7 +1027,7 @@ class BaseCommand extends core_1.Command {
987
1027
  ...(e.code ? { code: e.code } : {}),
988
1028
  message: e.message || 'control plane unavailable',
989
1029
  },
990
- }, (0, keychain_1.getActiveProfile)());
1030
+ }, (0, keychain_2.getActiveProfile)());
991
1031
  queuedId = entry.id;
992
1032
  }
993
1033
  catch {
@@ -1216,6 +1256,38 @@ const TERMINAL_ERROR_CODES = new Set(['task_output_required', 'task_output_not_a
1216
1256
  *
1217
1257
  * Exported for tests.
1218
1258
  */
1259
+ /**
1260
+ * What a 403 means, given whether the server said anything.
1261
+ *
1262
+ * Exported and pure so the wording is testable without an oclif runtime — the
1263
+ * convention `isRetryableFailure` follows below.
1264
+ *
1265
+ * A 403 WITH a body is the API deciding, and it says why; we relay it. A 403
1266
+ * with NO body did not come from the API — every application 403 in this
1267
+ * codebase carries a message — so it is the edge refusing the request before it
1268
+ * arrived, and the two must not share a sentence.
1269
+ *
1270
+ * This used to assert "your account does not have access to this resource" for
1271
+ * both. For an edge refusal that names a cause this process never observed, and
1272
+ * it is the expensive kind of wrong: it sends the user to audit their
1273
+ * membership, their role and their token, which is the one place the problem is
1274
+ * not. Dogfooded as OSK-263 — `skrr tasks create` refused a task description
1275
+ * containing a dotted AWS-credentials path, in a project the same session had
1276
+ * created a minute earlier, as an ADMIN who owned the space, and finding the
1277
+ * real cause took a line-by-line bisect of the description body.
1278
+ *
1279
+ * The 404 branch has always done the honest thing (`bodyMessage || 'Not
1280
+ * found.'`, which claims nothing); this brings 403 into line and adds the one
1281
+ * lead worth having.
1282
+ */
1283
+ function describeForbidden(bodyMessage) {
1284
+ if (bodyMessage)
1285
+ return bodyMessage;
1286
+ return ('Forbidden: the server refused this request and gave no reason. That usually means an ' +
1287
+ 'edge rule rejected it before it reached the API rather than a permission you lack — if ' +
1288
+ 'you can read this resource elsewhere, try again with less of the request body (a path ' +
1289
+ 'or token-shaped string in your text is enough to trigger one).');
1290
+ }
1219
1291
  function isRetryableFailure(status, body) {
1220
1292
  const declared = body?.retryable;
1221
1293
  if (typeof declared === 'boolean')
@@ -527,7 +527,7 @@ function messageSegmentationAdvisory({ destination, text, followingParts, }) {
527
527
  return undefined;
528
528
  if (text.length <= exports.OWNER_DM_SEGMENTATION_SOFT_LIMIT)
529
529
  return undefined;
530
- return (`[sky] Owner-DM message is ${text.length} characters. It may read better as 2–4 short thoughts: ` +
530
+ return (`[skrr] Owner-DM message is ${text.length} characters. It may read better as 2–4 short thoughts: ` +
531
531
  'use --part with one explicit --idempotency-key. This is only an advisory; ' +
532
532
  'use --no-message-lint to suppress it.');
533
533
  }
@@ -59,7 +59,7 @@ export default class AgentsChat extends BaseCommand {
59
59
  /**
60
60
  * OSK-2946 — a task reference sitting in the prompt with no binding.
61
61
  *
62
- * Both reported Codex sessions named an skrr task UUID in the prompt, but
62
+ * Both reported Codex sessions named a skrr task UUID in the prompt, but
63
63
  * the daemon environment exposed `OVERSKY_SESSION_TASK_CONTEXT=main_chat` with
64
64
  * no `OVERSKY_SESSION_TASK_ID`, so `skrr tasks show` (no id) answered
65
65
  * `TASK_CONTEXT_MISSING`. Both agents then spent large context on registry /
@@ -114,7 +114,7 @@ class AgentsChat extends base_command_1.BaseCommand {
114
114
  /**
115
115
  * OSK-2946 — a task reference sitting in the prompt with no binding.
116
116
  *
117
- * Both reported Codex sessions named an skrr task UUID in the prompt, but
117
+ * Both reported Codex sessions named a skrr task UUID in the prompt, but
118
118
  * the daemon environment exposed `OVERSKY_SESSION_TASK_CONTEXT=main_chat` with
119
119
  * no `OVERSKY_SESSION_TASK_ID`, so `skrr tasks show` (no id) answered
120
120
  * `TASK_CONTEXT_MISSING`. Both agents then spent large context on registry /
@@ -2,7 +2,7 @@ import { Command } from '@oclif/core';
2
2
  export default class BrowserInstall extends Command {
3
3
  static description: string;
4
4
  static flags: {
5
- harness: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
5
+ harness: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
6
6
  config: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
7
  'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
8
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -3,9 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const browser_1 = require("../browser");
5
5
  class BrowserInstall extends core_1.Command {
6
- static description = 'Add a sentinel-owned Sky Browser MCP entry without overwriting user configuration';
6
+ static description = 'Add a sentinel-owned skrr Browser MCP entry without overwriting user configuration';
7
7
  static flags = {
8
- harness: core_1.Flags.string({ description: 'all, claude, codex, generic, or sky-code' }),
8
+ harness: core_1.Flags.string({
9
+ // The default and the --config exclusion are both enforced by the
10
+ // runtime this forwards to. Stating them only there meant a bare
11
+ // `skrr browser install` edited every detected harness config with
12
+ // nothing in this help to say so.
13
+ description: 'all, claude, codex, generic, or sky-code. `all` touches every detected ' +
14
+ 'harness config and cannot be combined with --config.',
15
+ default: 'all',
16
+ }),
9
17
  config: core_1.Flags.string({ description: 'Explicit config file for a generic harness' }),
10
18
  'dry-run': core_1.Flags.boolean({ description: 'Show the intended edit without writing it' }),
11
19
  json: core_1.Flags.boolean({ description: 'Output JSON' }),
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const browser_1 = require("../browser");
5
5
  class BrowserMcpConfig extends core_1.Command {
6
- static description = 'Print the copyable stdio MCP configuration for a generic harness';
6
+ static description = 'Print the copyable stdio MCP configuration for a generic harness. Paste it into that ' +
7
+ "harness's own MCP config; `skrr browser install` does this for the harnesses that " +
8
+ 'have an adapter.';
7
9
  async run() {
8
10
  await this.parse(BrowserMcpConfig);
9
11
  await (0, browser_1.forwardBrowserCommand)(this, ['mcp-config']);
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const browser_1 = require("../../browser");
5
5
  class BrowserSkillShow extends core_1.Command {
6
- static description = 'Print the version-matched Sky Browser agent skill';
6
+ static description = 'Print the version-matched skrr Browser agent skill';
7
7
  static args = {
8
8
  name: core_1.Args.string({ description: 'Skill name (core)' }),
9
9
  };
@@ -3,7 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const browser_1 = require("../browser");
5
5
  class BrowserStatus extends core_1.Command {
6
- static description = 'Show attached, managed, E2B, and broker-bridge browser availability';
6
+ static description =
7
+ // E2B is not a row of its own: it is the managed runtime's LOCATION, and
8
+ // listing it alongside the others promised a line the output never has.
9
+ 'Show attached, managed (including its E2B location), and broker-bridge browser availability';
7
10
  static flags = {
8
11
  json: core_1.Flags.boolean({ description: 'Output JSON' }),
9
12
  };
@@ -2,7 +2,7 @@ import { Command } from '@oclif/core';
2
2
  export default class BrowserUninstall extends Command {
3
3
  static description: string;
4
4
  static flags: {
5
- harness: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
5
+ harness: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
6
6
  config: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
7
  'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
8
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -3,9 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const browser_1 = require("../browser");
5
5
  class BrowserUninstall extends core_1.Command {
6
- static description = 'Remove only the Sky Browser MCP entries owned by this installation';
6
+ static description = 'Remove only the skrr Browser MCP entries owned by this installation';
7
7
  static flags = {
8
- harness: core_1.Flags.string({ description: 'all, claude, codex, generic, or sky-code' }),
8
+ harness: core_1.Flags.string({
9
+ // The default and the --config exclusion are both enforced by the
10
+ // runtime this forwards to. Stating them only there meant a bare
11
+ // `skrr browser uninstall` edited every detected harness config with
12
+ // nothing in this help to say so.
13
+ description: 'all, claude, codex, generic, or sky-code. `all` touches every detected ' +
14
+ 'harness config and cannot be combined with --config.',
15
+ default: 'all',
16
+ }),
9
17
  config: core_1.Flags.string({ description: 'Explicit config file for a generic harness' }),
10
18
  'dry-run': core_1.Flags.boolean({ description: 'Show the intended removal without writing it' }),
11
19
  json: core_1.Flags.boolean({ description: 'Output JSON' }),
@@ -1,7 +1,7 @@
1
1
  import { Command } from '@oclif/core';
2
2
  export declare function buildBrowserArgs(passthrough: readonly string[]): string[];
3
3
  /**
4
- * Forward one concrete Sky Browser command to the daemon. Browser operations
4
+ * Forward one concrete skrr Browser command to the daemon. Browser operations
5
5
  * are implemented by the local daemon, not by the REST CLI, so every oclif
6
6
  * leaf uses this one bridge and preserves the capability boundary.
7
7
  */
@@ -8,7 +8,7 @@ function buildBrowserArgs(passthrough) {
8
8
  return ['browser', ...passthrough];
9
9
  }
10
10
  /**
11
- * Forward one concrete Sky Browser command to the daemon. Browser operations
11
+ * Forward one concrete skrr Browser command to the daemon. Browser operations
12
12
  * are implemented by the local daemon, not by the REST CLI, so every oclif
13
13
  * leaf uses this one bridge and preserves the capability boundary.
14
14
  */
@@ -18,7 +18,7 @@ async function forwardBrowserCommand(command, passthrough) {
18
18
  // releases. Probe the command list before forwarding so an install command
19
19
  // cannot turn into a surprising service mutation.
20
20
  if ((await (0, exec_oversky_1.overskySupportsSubcommand)('browser')) === false) {
21
- command.error('The local `oversky` daemon does not support Sky Browser yet. Update or reinstall the daemon, then retry `skrr browser`.', { exit: 2 });
21
+ command.error('The local `oversky` daemon does not support skrr Browser yet. Update or reinstall the daemon, then retry `skrr browser`.', { exit: 2 });
22
22
  }
23
23
  command.exit(await (0, exec_oversky_1.execOversky)(buildBrowserArgs(passthrough)));
24
24
  }
@@ -36,7 +36,7 @@ async function forwardBrowserCommand(command, passthrough) {
36
36
  * Chrome/profile handling or a second, divergent MCP implementation.
37
37
  */
38
38
  class Browser extends core_1.Command {
39
- static description = 'Install, diagnose, and configure capability-scoped Sky Browser access for local and hosted harnesses';
39
+ static description = 'Install, diagnose, and configure capability-scoped skrr Browser access for local and hosted harnesses';
40
40
  static strict = false;
41
41
  static examples = [
42
42
  '<%= config.bin %> browser status',
@@ -17,12 +17,15 @@ const sky_code_managed_1 = require("../../lib/sky-code-managed");
17
17
  * that parses successfully and is then silently dropped.
18
18
  */
19
19
  class Code extends core_1.Command {
20
- static description = 'Start the skrr Code interactive coding agent in this directory';
20
+ static description = 'Start the skrr Code interactive coding agent in this directory. Everything after ' +
21
+ `\`code\` is forwarded to the engine unchanged, so engine flags are not listed here — ` +
22
+ `run \`skrr code run --help\` for this wrapper, or the engine's own help for its ` +
23
+ `flags. ${sky_code_managed_1.SKRR_AGENT_FLAG} is the one flag this wrapper consumes itself.`;
21
24
  static strict = false;
22
25
  static examples = [
23
26
  '<%= config.bin %> code',
24
27
  '<%= config.bin %> code --model claude-opus-5',
25
- `<%= config.bin %> code ${sky_code_managed_1.OVERSKY_AGENT_FLAG} Engineer`,
28
+ `<%= config.bin %> code ${sky_code_managed_1.SKRR_AGENT_FLAG} Engineer`,
26
29
  '<%= config.bin %> code run "fix the failing test"',
27
30
  '<%= config.bin %> code doctor',
28
31
  ];
@@ -69,7 +72,7 @@ async function forwardToEngine(cmd, subcommand, passthrough, options = {}) {
69
72
  // error at the other end.
70
73
  const say = (line) => cmd.logToStderr(line);
71
74
  if (agentRef && !options.managed) {
72
- say(`${sky_code_managed_1.OVERSKY_AGENT_FLAG} has no effect here — this command does not host a managed ` +
75
+ say(`${sky_code_managed_1.SKRR_AGENT_FLAG} has no effect here — this command does not host a managed ` +
73
76
  'session. It was removed from the arguments passed to the engine.');
74
77
  }
75
78
  const managed = options.managed
@@ -0,0 +1,61 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * `skrr code install` — install the engine from the terminal.
4
+ *
5
+ * ── Why this command exists ────────────────────────────────────────────────
6
+ *
7
+ * `skrr code`'s not-installed message offered exactly two routes and neither
8
+ * worked without a browser: "In the web app: the runtime menu … → Install", or
9
+ * point `OVERSKY_SKY_CODE_PATH` at a build you already have — which is the thing
10
+ * you are trying to obtain, and the route that gives up provenance. So the
11
+ * ordinary case for a coding harness (a dev box over SSH, a CI runner, a cloud
12
+ * VM) had no way in (dogfood OSK-276).
13
+ *
14
+ * Nothing underneath was missing. The daemon has performed the verified install
15
+ * since 2026-08-08, and `POST /api/daemons/:id/cli-installers/:provider/install`
16
+ * has fronted it since June. The install was API-exposed and CLI-unreachable —
17
+ * the shape the root `CLAUDE.md` names under CLI ↔ API parity: "an endpoint the
18
+ * API exposes must be reachable from `skrr`."
19
+ *
20
+ * So this adds a SURFACE and no mechanism. Same route the web app posts, same
21
+ * daemon job, same signature verification. A second install path would be a
22
+ * second thing to keep in step with the first, and the first is the one that
23
+ * verifies the signed manifest.
24
+ */
25
+ export default class CodeInstall extends BaseCommand {
26
+ static description: string;
27
+ static examples: string[];
28
+ static flags: {
29
+ daemon: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
30
+ wait: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
31
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
32
+ };
33
+ run(): Promise<void>;
34
+ /**
35
+ * Which daemon does the install.
36
+ *
37
+ * Refuses rather than guessing when several are active. Picking one would mean
38
+ * installing a hundred megabytes onto a machine the user was not thinking
39
+ * about, and the list this reads is the same one `skrr list-daemons` prints —
40
+ * so the error can name the exact values that work.
41
+ */
42
+ private resolveDaemon;
43
+ /**
44
+ * What the daemon says about this provider, when it started no job.
45
+ *
46
+ * Read from the SAME list route the web app uses, so the CLI reports the
47
+ * daemon's own words rather than inventing an explanation for silence. Best
48
+ * effort: a failure here means falling back to the honest "does not say why",
49
+ * never to a guess.
50
+ */
51
+ private installerNote;
52
+ /**
53
+ * Poll the job until it settles, printing each new log line.
54
+ *
55
+ * The daemon already writes a progress log — "Fetching the signed skrr Code
56
+ * release manifest", "Verified and installed …" — and it is the only account
57
+ * of a hundred-megabyte download the user is waiting on. Printing only the
58
+ * lines not yet seen keeps a poll loop from repeating the whole log.
59
+ */
60
+ private followJob;
61
+ }