@skrr-ai/cli 0.1.8 → 0.1.9

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 (169) 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 +73 -31
  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/lib/agentic-stream.js +21 -1
  102. package/dist/lib/api-fetch.d.ts +1 -1
  103. package/dist/lib/api-fetch.js +1 -1
  104. package/dist/lib/auth-core-init.js +23 -3
  105. package/dist/lib/auth-storage.d.ts +0 -12
  106. package/dist/lib/auth-storage.js +37 -2
  107. package/dist/lib/config.d.ts +3 -3
  108. package/dist/lib/config.js +327 -26
  109. package/dist/lib/created-line.d.ts +22 -0
  110. package/dist/lib/created-line.js +25 -0
  111. package/dist/lib/daemon-binding.d.ts +29 -0
  112. package/dist/lib/daemon-binding.js +63 -0
  113. package/dist/lib/daemon-ref.d.ts +31 -0
  114. package/dist/lib/daemon-ref.js +47 -0
  115. package/dist/lib/daemonBroker.js +1 -1
  116. package/dist/lib/delegated-cli.js +3 -3
  117. package/dist/lib/exec-oversky.d.ts +18 -1
  118. package/dist/lib/exec-oversky.js +21 -4
  119. package/dist/lib/html-text.d.ts +15 -0
  120. package/dist/lib/html-text.js +40 -0
  121. package/dist/lib/keychain.d.ts +43 -0
  122. package/dist/lib/keychain.js +145 -4
  123. package/dist/lib/login.js +3 -3
  124. package/dist/lib/node-adapter.js +1 -1
  125. package/dist/lib/oauthLogin.js +17 -6
  126. package/dist/lib/sky-code-agent.d.ts +1 -1
  127. package/dist/lib/sky-code-agent.js +8 -8
  128. package/dist/lib/sky-code-broker.js +18 -2
  129. package/dist/lib/sky-code-doctor.js +120 -4
  130. package/dist/lib/sky-code-managed.d.ts +12 -0
  131. package/dist/lib/sky-code-managed.js +21 -7
  132. package/dist/lib/sky-code.d.ts +16 -1
  133. package/dist/lib/sky-code.js +102 -4
  134. package/dist/lib/task-execution.js +4 -11
  135. package/dist/lib/tasks.d.ts +1 -0
  136. package/dist/lib/tasks.js +9 -6
  137. package/dist/lib/update-check.js +1 -1
  138. package/dist/lib/usage-discovery.js +1 -1
  139. package/dist/lib/web-url.js +12 -1
  140. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.d.ts +1 -1
  141. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.js +2 -2
  142. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -0
  143. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +13 -1
  144. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/linux.js +16 -3
  145. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.d.ts +0 -9
  146. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.js +18 -1
  147. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/loginLocalhost.js +3 -3
  148. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +3 -3
  149. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/runtime.d.ts +7 -3
  150. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.d.ts +96 -0
  151. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.js +115 -0
  152. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.d.ts +1 -1
  153. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.js +2 -2
  154. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -0
  155. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -0
  156. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/linux.js +16 -3
  157. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.d.ts +0 -9
  158. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.js +18 -1
  159. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/loginLocalhost.js +3 -3
  160. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +3 -3
  161. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/runtime.d.ts +7 -3
  162. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.d.ts +96 -0
  163. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.js +107 -0
  164. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  165. package/dist/node_modules/@skrr-ai/data-provider/index.js +4206 -4191
  166. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.js +2 -2
  167. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.js +2 -2
  168. package/oclif.manifest.json +9924 -3352
  169. package/package.json +22 -19
@@ -0,0 +1,22 @@
1
+ /**
2
+ * One shape for "you just created a thing".
3
+ *
4
+ * The four core entities each printed a different confirmation: the identifier
5
+ * was a human ref for a project, a raw uuid for a goal and a space, and both for
6
+ * a task; the separator was an em dash for two and a hyphen for the other two;
7
+ * and goals and spaces appended context (`in space <uuid>`,
8
+ * `(subspace of <uuid>)`) that the link on the next line already carries.
9
+ *
10
+ * Reading four of these in a row, you cannot tell what you are being handed —
11
+ * which is what makes the difference between "did this work" and "what do I
12
+ * paste into the next command" harder than it should be.
13
+ *
14
+ * The rule: prefer the ref a person can type, fall back to the id, and put the
15
+ * title after a single em dash. The `View it at <url>` line that follows is
16
+ * already consistent and is left alone.
17
+ */
18
+ export declare function createdLine(entity: string, opts: {
19
+ ref?: string | null;
20
+ id?: string | null;
21
+ title?: string | null;
22
+ }): string;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createdLine = createdLine;
4
+ /**
5
+ * One shape for "you just created a thing".
6
+ *
7
+ * The four core entities each printed a different confirmation: the identifier
8
+ * was a human ref for a project, a raw uuid for a goal and a space, and both for
9
+ * a task; the separator was an em dash for two and a hyphen for the other two;
10
+ * and goals and spaces appended context (`in space <uuid>`,
11
+ * `(subspace of <uuid>)`) that the link on the next line already carries.
12
+ *
13
+ * Reading four of these in a row, you cannot tell what you are being handed —
14
+ * which is what makes the difference between "did this work" and "what do I
15
+ * paste into the next command" harder than it should be.
16
+ *
17
+ * The rule: prefer the ref a person can type, fall back to the id, and put the
18
+ * title after a single em dash. The `View it at <url>` line that follows is
19
+ * already consistent and is left alone.
20
+ */
21
+ function createdLine(entity, opts) {
22
+ const label = (opts.ref ?? '').trim() || (opts.id ?? '').trim() || '?';
23
+ const title = (opts.title ?? '').trim() || '(no title)';
24
+ return `Created ${entity} ${label} — ${title}`;
25
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Defaults for the flags that decide WHICH deployment and WHICH profile a
3
+ * newly installed daemon binds to.
4
+ *
5
+ * `skrr daemon *` are pass-throughs to the `skrrd` binary, and the two
6
+ * programs keep SEPARATE configuration: the CLI's deployment and profile live
7
+ * in `cli-config.json`, the daemon's in `daemon.json`. Nothing has ever made
8
+ * one follow the other, so the ordinary way to end up split is to do nothing
9
+ * unusual at all — log the CLI into one deployment and leave the daemon on
10
+ * whichever it was installed against. That is OSK-279, observed on this
11
+ * machine as a daemon on dev serving a CLI on prod.
12
+ *
13
+ * The fix is deliberately scoped to INSTALL, the moment a daemon is created
14
+ * and the only moment its binding is not yet somebody's decision. `start` is
15
+ * left alone on purpose: it starts a daemon that already has a binding, and
16
+ * silently overriding it would break the operator who split the two knowingly
17
+ * — running a dev daemon against a prod CLI is a legitimate thing to do, and
18
+ * it is not this function's place to overrule it. `skrr daemon status` reports
19
+ * a split instead of correcting one.
20
+ */
21
+ /** Whether `argv` already carries `--name` or `--name=value` (or `-alias`). */
22
+ export declare function hasFlag(argv: readonly string[], name: string, alias?: string): boolean;
23
+ /**
24
+ * Append `--server` / `--profile` describing the CLI's own session, for any
25
+ * the caller did not pass. An explicit flag always wins — this only fills a
26
+ * silence, so `skrr daemon install --env prod` still installs against prod
27
+ * while the CLI sits on dev.
28
+ */
29
+ export declare function bindToCliSession(argv: readonly string[]): string[];
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasFlag = hasFlag;
4
+ exports.bindToCliSession = bindToCliSession;
5
+ const config_1 = require("./config");
6
+ const keychain_1 = require("./keychain");
7
+ /**
8
+ * Defaults for the flags that decide WHICH deployment and WHICH profile a
9
+ * newly installed daemon binds to.
10
+ *
11
+ * `skrr daemon *` are pass-throughs to the `skrrd` binary, and the two
12
+ * programs keep SEPARATE configuration: the CLI's deployment and profile live
13
+ * in `cli-config.json`, the daemon's in `daemon.json`. Nothing has ever made
14
+ * one follow the other, so the ordinary way to end up split is to do nothing
15
+ * unusual at all — log the CLI into one deployment and leave the daemon on
16
+ * whichever it was installed against. That is OSK-279, observed on this
17
+ * machine as a daemon on dev serving a CLI on prod.
18
+ *
19
+ * The fix is deliberately scoped to INSTALL, the moment a daemon is created
20
+ * and the only moment its binding is not yet somebody's decision. `start` is
21
+ * left alone on purpose: it starts a daemon that already has a binding, and
22
+ * silently overriding it would break the operator who split the two knowingly
23
+ * — running a dev daemon against a prod CLI is a legitimate thing to do, and
24
+ * it is not this function's place to overrule it. `skrr daemon status` reports
25
+ * a split instead of correcting one.
26
+ */
27
+ /** Whether `argv` already carries `--name` or `--name=value` (or `-alias`). */
28
+ function hasFlag(argv, name, alias) {
29
+ return argv.some((arg) => {
30
+ if (arg === '--')
31
+ return false;
32
+ return (arg === name ||
33
+ arg.startsWith(`${name}=`) ||
34
+ (alias !== undefined && (arg === alias || arg.startsWith(`${alias}=`))));
35
+ });
36
+ }
37
+ /**
38
+ * Append `--server` / `--profile` describing the CLI's own session, for any
39
+ * the caller did not pass. An explicit flag always wins — this only fills a
40
+ * silence, so `skrr daemon install --env prod` still installs against prod
41
+ * while the CLI sits on dev.
42
+ */
43
+ function bindToCliSession(argv) {
44
+ const extra = [];
45
+ // `--env` resolves to a server URL inside the daemon, so passing one is
46
+ // passing the other. Adding `--server` beside it would be an ambiguous
47
+ // instruction where the daemon's own precedence, not ours, decides.
48
+ if (!hasFlag(argv, '--server', '-s') && !hasFlag(argv, '--env', '-e')) {
49
+ const { baseURL } = (0, config_1.loadConfig)();
50
+ if (baseURL)
51
+ extra.push('--server', baseURL);
52
+ }
53
+ if (!hasFlag(argv, '--profile')) {
54
+ // The CLI's own profile, resolved the same way `BaseCommand` resolves it.
55
+ // These commands extend `Command`, so `init()` never ran and the module
56
+ // state `getActiveProfile()` reads was never set — asking it here would
57
+ // report `default` no matter what the user typed.
58
+ const profile = (0, keychain_1.resolveProfileFromArgv)(argv) ?? process.env.OVERSKY_PROFILE ?? null;
59
+ if (profile && profile !== keychain_1.DEFAULT_PROFILE)
60
+ extra.push('--profile', profile);
61
+ }
62
+ return [...argv, ...extra];
63
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The two halves of the daemon-reference contract: how a daemon id is DISPLAYED
3
+ * and how a displayed one is READ BACK.
4
+ *
5
+ * `list-daemons` prints a decorated short form — `d_6a323ae4...` — and the two
6
+ * commands that consume a daemon reference (`revoke-daemon`,
7
+ * `set-capabilities`) matched the raw argument against `id.startsWith(prefix)`.
8
+ * So the value the product printed in a column literally headed DAEMON REF was
9
+ * rejected by every command that takes one, and the failure looked like "no such
10
+ * daemon" rather than "that is not the format I want". The sibling
11
+ * `machines show` accepts what `machines list` prints, which is the behaviour
12
+ * people reasonably expect here too.
13
+ *
14
+ * Keeping the decoration and accepting it back is the right trade: the `d_`
15
+ * prefix and the ellipsis are what tell a reader the value is truncated, and
16
+ * dropping them would make an 8-character fragment look like a full id.
17
+ */
18
+ /**
19
+ * Format a full daemon ObjectId as a short readable label:
20
+ * `d_xY3p...` — prefix + first 8 hex chars + an ellipsis.
21
+ */
22
+ export declare function formatDaemonRef(id: string): string;
23
+ /**
24
+ * Read back anything `formatDaemonRef` may have produced, plus what a person
25
+ * plausibly pastes: with or without the `d_`, with or without a trailing `...`
26
+ * or a real ellipsis character, and with surrounding whitespace.
27
+ *
28
+ * A value that was never decorated passes through untouched, so a full id and a
29
+ * hand-typed prefix keep working exactly as before.
30
+ */
31
+ export declare function normalizeDaemonRef(input: string): string;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * The two halves of the daemon-reference contract: how a daemon id is DISPLAYED
4
+ * and how a displayed one is READ BACK.
5
+ *
6
+ * `list-daemons` prints a decorated short form — `d_6a323ae4...` — and the two
7
+ * commands that consume a daemon reference (`revoke-daemon`,
8
+ * `set-capabilities`) matched the raw argument against `id.startsWith(prefix)`.
9
+ * So the value the product printed in a column literally headed DAEMON REF was
10
+ * rejected by every command that takes one, and the failure looked like "no such
11
+ * daemon" rather than "that is not the format I want". The sibling
12
+ * `machines show` accepts what `machines list` prints, which is the behaviour
13
+ * people reasonably expect here too.
14
+ *
15
+ * Keeping the decoration and accepting it back is the right trade: the `d_`
16
+ * prefix and the ellipsis are what tell a reader the value is truncated, and
17
+ * dropping them would make an 8-character fragment look like a full id.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.formatDaemonRef = formatDaemonRef;
21
+ exports.normalizeDaemonRef = normalizeDaemonRef;
22
+ /** Prefix the short form carries so a truncated id is visibly truncated. */
23
+ const DISPLAY_PREFIX = 'd_';
24
+ /**
25
+ * Format a full daemon ObjectId as a short readable label:
26
+ * `d_xY3p...` — prefix + first 8 hex chars + an ellipsis.
27
+ */
28
+ function formatDaemonRef(id) {
29
+ if (!id || id.length < 8)
30
+ return id || '—';
31
+ return `${DISPLAY_PREFIX}${id.slice(0, 8)}...`;
32
+ }
33
+ /**
34
+ * Read back anything `formatDaemonRef` may have produced, plus what a person
35
+ * plausibly pastes: with or without the `d_`, with or without a trailing `...`
36
+ * or a real ellipsis character, and with surrounding whitespace.
37
+ *
38
+ * A value that was never decorated passes through untouched, so a full id and a
39
+ * hand-typed prefix keep working exactly as before.
40
+ */
41
+ function normalizeDaemonRef(input) {
42
+ let out = input.trim();
43
+ if (out.toLowerCase().startsWith(DISPLAY_PREFIX))
44
+ out = out.slice(DISPLAY_PREFIX.length);
45
+ out = out.replace(/(?:\.{3}|…)$/, '');
46
+ return out.trim();
47
+ }
@@ -286,7 +286,7 @@ async function attemptDaemonBrokerLogin(opts) {
286
286
  return {
287
287
  ok: false,
288
288
  reason: 'base_url_mismatch',
289
- detail: `No local oversky daemon is logged in to ${opts.baseURL}; available daemon is logged in to ${mismatchedBootstrap.serverUrl}.`,
289
+ detail: `No local skrr runtime is logged in to ${opts.baseURL}; available daemon is logged in to ${mismatchedBootstrap.serverUrl}.`,
290
290
  daemonServerUrl: mismatchedBootstrap.serverUrl,
291
291
  };
292
292
  }
@@ -22,7 +22,7 @@ function isDelegatedCliSession(env = process.env) {
22
22
  function trustedLoopbackUrl(raw) {
23
23
  const url = new URL(raw);
24
24
  if (url.protocol !== 'http:' || !['127.0.0.1', 'localhost', '::1'].includes(url.hostname)) {
25
- throw new Error('Delegated Sky CLI broker must be a loopback HTTP URL.');
25
+ throw new Error('Delegated skrr CLI broker must be a loopback HTTP URL.');
26
26
  }
27
27
  return url.toString();
28
28
  }
@@ -82,11 +82,11 @@ async function fetchDelegatedCliCredential(env = process.env, options = {}) {
82
82
  if (body?.code === 'DELEGATED_CLI_CAPABILITY_REJECTED')
83
83
  return null;
84
84
  }
85
- throw new Error(`Delegated Sky CLI credential broker returned HTTP ${res.status}.`);
85
+ throw new Error(`Delegated skrr CLI credential broker returned HTTP ${res.status}.`);
86
86
  }
87
87
  const body = (await res.json());
88
88
  if (typeof body.accessToken !== 'string' || body.accessToken.length === 0) {
89
- throw new Error('Delegated Sky CLI credential broker returned no access token.');
89
+ throw new Error('Delegated skrr CLI credential broker returned no access token.');
90
90
  }
91
91
  const credential = {
92
92
  token: body.accessToken,
@@ -3,8 +3,25 @@ export declare const RUNTIME_BINARY_NAMES: readonly ["skrrd", "oversky"];
3
3
  /** Resolve the absolute path of `oversky` if it exists, else return null. */
4
4
  export declare function findOverskyBinary(): string | null;
5
5
  /**
6
- * Pretty hint shown when `oversky` isn't installed. Kept short — points
6
+ * Pretty hint shown when the runtime isn't installed. Kept short — points
7
7
  * users at the canonical install paths rather than enumerating them.
8
+ *
9
+ * THE ADDRESSES BELOW STILL SAY `oversky`, ON PURPOSE.
10
+ * ---------------------------------------------------
11
+ * The rename moved this text to `skrr` and pointed it at
12
+ * `dush1023/skrr/skrrd`, `raw.githubusercontent.com/dush1023/skrr/...` and
13
+ * `github.com/dush1023/skrr` — none of which exist. The tap
14
+ * `dush1023/homebrew-skrr` 404s; only `dush1023/homebrew-oversky` is
15
+ * published, and the repository is still `dush1023/OverSky`.
16
+ *
17
+ * An install instruction is only worth what it installs. Renaming these
18
+ * before the tap and repository move produces a message that reads correct
19
+ * and fails when followed — the same defect as the "install it with
20
+ * `sky code install`" hint, which named a command that never existed.
21
+ *
22
+ * They move when the Homebrew tap is published (that work is tracked
23
+ * separately) and when the repository is renamed — not before, and not as
24
+ * part of a copy sweep.
8
25
  */
9
26
  export declare function notInstalledMessage(): string;
10
27
  /**
@@ -76,17 +76,34 @@ function isExecutable(p) {
76
76
  }
77
77
  }
78
78
  /**
79
- * Pretty hint shown when `oversky` isn't installed. Kept short — points
79
+ * Pretty hint shown when the runtime isn't installed. Kept short — points
80
80
  * users at the canonical install paths rather than enumerating them.
81
+ *
82
+ * THE ADDRESSES BELOW STILL SAY `oversky`, ON PURPOSE.
83
+ * ---------------------------------------------------
84
+ * The rename moved this text to `skrr` and pointed it at
85
+ * `dush1023/skrr/skrrd`, `raw.githubusercontent.com/dush1023/skrr/...` and
86
+ * `github.com/dush1023/skrr` — none of which exist. The tap
87
+ * `dush1023/homebrew-skrr` 404s; only `dush1023/homebrew-oversky` is
88
+ * published, and the repository is still `dush1023/OverSky`.
89
+ *
90
+ * An install instruction is only worth what it installs. Renaming these
91
+ * before the tap and repository move produces a message that reads correct
92
+ * and fails when followed — the same defect as the "install it with
93
+ * `sky code install`" hint, which named a command that never existed.
94
+ *
95
+ * They move when the Homebrew tap is published (that work is tracked
96
+ * separately) and when the repository is renamed — not before, and not as
97
+ * part of a copy sweep.
81
98
  */
82
99
  function notInstalledMessage() {
83
100
  return [
84
101
  'The skrr local runtime is not installed on this machine.',
85
102
  '',
86
103
  'Install:',
87
- ' macOS: brew install dush1023/skrr/skrrd',
88
- ' Linux: curl -fsSL https://raw.githubusercontent.com/dush1023/skrr/main/scripts/install-daemon.sh | bash',
89
- ' Other: https://github.com/dush1023/skrr#installation',
104
+ ' macOS: brew install dush1023/oversky/oversky',
105
+ ' Linux: curl -fsSL https://raw.githubusercontent.com/dush1023/OverSky/main/scripts/install-daemon.sh | bash',
106
+ ' Other: https://github.com/dush1023/OverSky#installation',
90
107
  '',
91
108
  'Once installed, re-run your command.',
92
109
  ].join('\n');
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Render server-stored rich text as something a terminal can read.
3
+ *
4
+ * Task, goal and project descriptions are persisted as HTML — `--description
5
+ * -format markdown` is converted on the way in — so a CLI that prints the field
6
+ * verbatim shows the user `<p>…</p>` and `<code>…</code>` around their own
7
+ * words. They wrote markdown and got tags back.
8
+ *
9
+ * Deliberately NOT a general HTML renderer. It converts the block boundaries
10
+ * that carry meaning in a paragraph of prose, drops the rest of the tags, and
11
+ * decodes the entity set the server actually emits. Anything that does not look
12
+ * like markup is returned untouched, so a plain-text description keeps its own
13
+ * whitespace instead of being reflowed by a converter it never needed.
14
+ */
15
+ export declare function htmlToDisplayText(value: string): string;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * Render server-stored rich text as something a terminal can read.
4
+ *
5
+ * Task, goal and project descriptions are persisted as HTML — `--description
6
+ * -format markdown` is converted on the way in — so a CLI that prints the field
7
+ * verbatim shows the user `<p>…</p>` and `<code>…</code>` around their own
8
+ * words. They wrote markdown and got tags back.
9
+ *
10
+ * Deliberately NOT a general HTML renderer. It converts the block boundaries
11
+ * that carry meaning in a paragraph of prose, drops the rest of the tags, and
12
+ * decodes the entity set the server actually emits. Anything that does not look
13
+ * like markup is returned untouched, so a plain-text description keeps its own
14
+ * whitespace instead of being reflowed by a converter it never needed.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.htmlToDisplayText = htmlToDisplayText;
18
+ /** Cheap, deliberate: a tag-shaped substring is the only trigger. */
19
+ function looksLikeHtml(value) {
20
+ return /<\/?[a-z][^>]*>/i.test(value);
21
+ }
22
+ function htmlToDisplayText(value) {
23
+ if (!looksLikeHtml(value))
24
+ return value;
25
+ return (value
26
+ .replace(/<br\s*\/?>/gi, '\n')
27
+ .replace(/<\/(p|div|li|h[1-6])>/gi, '\n')
28
+ .replace(/<li[^>]*>/gi, ' - ')
29
+ .replace(/<[^>]+>/g, '')
30
+ .replace(/&nbsp;/g, ' ')
31
+ .replace(/&#39;/g, "'")
32
+ .replace(/&quot;/g, '"')
33
+ .replace(/&lt;/g, '<')
34
+ .replace(/&gt;/g, '>')
35
+ // `&amp;` LAST, or `&amp;lt;` would decode twice into a bare `<`.
36
+ .replace(/&amp;/g, '&')
37
+ .replace(/[ \t]+/g, ' ')
38
+ .replace(/\n{3,}/g, '\n\n')
39
+ .trim());
40
+ }
@@ -1,4 +1,15 @@
1
+ /** Pre-Phase-K namespace; reserved for a future migration helper. */
2
+ export declare const DEFAULT_PROFILE = "default";
1
3
  export declare function setActiveProfile(profile: string): void;
4
+ /**
5
+ * Whether the profile has been chosen for this process.
6
+ *
7
+ * Borrowed from `daemon/src/config.ts`, which warns when a writer runs before
8
+ * `setActiveProfile()` and says why: such a caller "is a bug — they're writing
9
+ * to the legacy single-tenant root instead of the active profile's dir". The
10
+ * failure is silent and permanent, so it needs to be noisy once.
11
+ */
12
+ export declare function isProfileResolved(): boolean;
2
13
  export declare function getActiveProfile(): string;
3
14
  /** @internal exposed for tests / diagnostics. */
4
15
  export declare function __currentServiceForTest(): string;
@@ -38,8 +49,40 @@ export declare function writeServerOrigin(origin: string): boolean;
38
49
  export declare function deleteServerOrigin(): void;
39
50
  export declare function deleteToken(): void;
40
51
  export declare function deleteRefreshToken(): void;
52
+ /**
53
+ * Remove the credentials this product wrote under its previous brand.
54
+ *
55
+ * `logout` used to delete only the CURRENT service and report an unqualified
56
+ * "Signed out." On a machine carried through the rename that left the
57
+ * pre-rename CLI credential, the pre-rename daemon credentials, and BOTH KEKs
58
+ * in place — so the sealed envelope and the key that unwraps it were still
59
+ * sitting there after the product said you were signed out. `logout --help`
60
+ * offers `--all-devices` for "a stolen-laptop incident", which is the one
61
+ * situation where that is exactly wrong.
62
+ *
63
+ * Returns the services it actually removed something from, so the caller can
64
+ * say what happened instead of destroying credentials silently.
65
+ *
66
+ * NOTE this also signs out the retired `oversky` binary, if one is still
67
+ * installed. That is the intent — same account, same machine — and the cutover
68
+ * runbook already lists these items for removal.
69
+ */
70
+ export declare function deleteLegacyBrandEntries(): string[];
41
71
  /**
42
72
  * Remove every CLI keychain slot for the active profile. Used by `skrr logout`.
43
73
  * Safe to call on a clean system — missing items are not an error.
44
74
  */
45
75
  export declare function deleteAll(): void;
76
+ /**
77
+ * Pull `--profile` out of raw argv.
78
+ *
79
+ * Deliberately minimal: it recognises `--profile x` and `--profile=x` and
80
+ * nothing else. This runs before any validation, so it must not throw, must not
81
+ * consume anything it does not own, and must not care about flag ordering —
82
+ * anything it gets wrong is corrected microseconds later when oclif parses the
83
+ * same argv properly and rejects a malformed invocation.
84
+ *
85
+ * Returns null rather than the default, so the caller can fall through to
86
+ * `OVERSKY_PROFILE` without a flag having to out-rank an env var it never saw.
87
+ */
88
+ export declare function resolveProfileFromArgv(argv: readonly string[] | undefined): string | null;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_PROFILE = void 0;
3
4
  exports.setActiveProfile = setActiveProfile;
5
+ exports.isProfileResolved = isProfileResolved;
4
6
  exports.getActiveProfile = getActiveProfile;
5
7
  exports.__currentServiceForTest = __currentServiceForTest;
6
8
  exports.isAvailable = isAvailable;
@@ -14,7 +16,9 @@ exports.writeServerOrigin = writeServerOrigin;
14
16
  exports.deleteServerOrigin = deleteServerOrigin;
15
17
  exports.deleteToken = deleteToken;
16
18
  exports.deleteRefreshToken = deleteRefreshToken;
19
+ exports.deleteLegacyBrandEntries = deleteLegacyBrandEntries;
17
20
  exports.deleteAll = deleteAll;
21
+ exports.resolveProfileFromArgv = resolveProfileFromArgv;
18
22
  /**
19
23
  * keychain.ts — macOS Keychain-backed token storage for the Sky CLI.
20
24
  *
@@ -44,10 +48,11 @@ exports.deleteAll = deleteAll;
44
48
  */
45
49
  const node_child_process_1 = require("node:child_process");
46
50
  const cred_envelope_1 = require("./cred-envelope");
51
+ const auth_core_1 = require("@skrr-ai/auth-core");
47
52
  /** Service-name prefix. Always followed by `.<profile>`. */
48
53
  const SERVICE_BASE = 'ai.skrr.cli';
49
54
  /** Pre-Phase-K namespace; reserved for a future migration helper. */
50
- const DEFAULT_PROFILE = 'default';
55
+ exports.DEFAULT_PROFILE = 'default';
51
56
  const ACCOUNT_TOKEN = 'cli-token';
52
57
  const ACCOUNT_EXPIRES = 'cli-token-expires';
53
58
  const ACCOUNT_REFRESH = 'cli-refresh';
@@ -58,14 +63,35 @@ const SECURITY_ITEM_NOT_FOUND = 44;
58
63
  /** Cached availability — probed once at first call. */
59
64
  let _availability = null;
60
65
  /**
61
- * Active profile for this process. Phase K will wire `setActiveProfile('work')`
62
- * onto a `--profile` flag; today everything resolves to `default`.
66
+ * Active profile for this process.
67
+ *
68
+ * Wired from `--profile` / `OVERSKY_PROFILE` in `BaseCommand.init()`, before
69
+ * anything reads a credential. The comment here used to say "Phase K will wire
70
+ * `setActiveProfile('work')` onto a `--profile` flag; today everything resolves
71
+ * to `default`" — and that stayed true long enough to cost something: with one
72
+ * slot, signing into a second deployment EVICTED the first, so a round trip
73
+ * meant two interactive logins and an agent could not make one at all
74
+ * (OSK-290).
63
75
  */
64
- let _activeProfile = DEFAULT_PROFILE;
76
+ let _activeProfile = exports.DEFAULT_PROFILE;
77
+ /** True once a caller has chosen, so a writer can tell "default" from "unset". */
78
+ let _profileResolved = false;
65
79
  function setActiveProfile(profile) {
80
+ _profileResolved = true;
66
81
  if (profile && profile.length > 0)
67
82
  _activeProfile = profile;
68
83
  }
84
+ /**
85
+ * Whether the profile has been chosen for this process.
86
+ *
87
+ * Borrowed from `daemon/src/config.ts`, which warns when a writer runs before
88
+ * `setActiveProfile()` and says why: such a caller "is a bug — they're writing
89
+ * to the legacy single-tenant root instead of the active profile's dir". The
90
+ * failure is silent and permanent, so it needs to be noisy once.
91
+ */
92
+ function isProfileResolved() {
93
+ return _profileResolved;
94
+ }
69
95
  function getActiveProfile() {
70
96
  return _activeProfile;
71
97
  }
@@ -307,6 +333,88 @@ function deleteRefreshToken() {
307
333
  deleteSlot(ACCOUNT_REFRESH);
308
334
  deleteSlot(ACCOUNT_REFRESH_EXPIRES);
309
335
  }
336
+ /**
337
+ * Every Keychain service this product wrote under its PREVIOUS brand.
338
+ *
339
+ * Discovered rather than enumerated, because the daemon's services carry a
340
+ * per-profile hash (`ai.oversky.daemon.dev-f00ad7c1`) that cannot be
341
+ * reconstructed from a name. Metadata only — `dump-keychain` without `-d`
342
+ * prints service names and does not prompt for or reveal any secret.
343
+ *
344
+ * Filtered to `LEGACY_NATIVE_ID_PREFIXES` followed by a dot, so a third-party
345
+ * item that merely starts with the same letters is never in scope.
346
+ */
347
+ function discoverLegacyBrandServices() {
348
+ let dump;
349
+ try {
350
+ dump = (0, node_child_process_1.execFileSync)('security', ['dump-keychain'], {
351
+ encoding: 'utf-8',
352
+ stdio: ['ignore', 'pipe', 'ignore'],
353
+ });
354
+ }
355
+ catch {
356
+ return [];
357
+ }
358
+ const found = new Set();
359
+ for (const match of dump.matchAll(/"svce"<blob>="([^"]*)"/g)) {
360
+ const service = match[1];
361
+ if (auth_core_1.LEGACY_NATIVE_ID_PREFIXES.some((prefix) => service.startsWith(`${prefix}.`))) {
362
+ found.add(service);
363
+ }
364
+ }
365
+ return [...found].sort();
366
+ }
367
+ /**
368
+ * Delete every item under one service, without needing its account names.
369
+ *
370
+ * `delete-generic-password -s <svc>` removes ONE matching item, so a service
371
+ * holding a token, its expiry and a refresh pair needs several passes. Bounded,
372
+ * because a delete that never reports "not found" would otherwise spin.
373
+ */
374
+ function deleteEntireService(service) {
375
+ let removedAny = false;
376
+ for (let i = 0; i < 32; i += 1) {
377
+ try {
378
+ (0, node_child_process_1.execFileSync)('security', ['delete-generic-password', '-s', service], {
379
+ stdio: ['ignore', 'ignore', 'ignore'],
380
+ });
381
+ removedAny = true;
382
+ }
383
+ catch (err) {
384
+ const status = err.status;
385
+ if (status === SECURITY_ITEM_NOT_FOUND)
386
+ return removedAny;
387
+ if (process.env.DEBUG) {
388
+ console.error(`[cli/keychain] legacy sweep failed for ${service} (status=${status ?? 'unknown'})`);
389
+ }
390
+ return removedAny;
391
+ }
392
+ }
393
+ return removedAny;
394
+ }
395
+ /**
396
+ * Remove the credentials this product wrote under its previous brand.
397
+ *
398
+ * `logout` used to delete only the CURRENT service and report an unqualified
399
+ * "Signed out." On a machine carried through the rename that left the
400
+ * pre-rename CLI credential, the pre-rename daemon credentials, and BOTH KEKs
401
+ * in place — so the sealed envelope and the key that unwraps it were still
402
+ * sitting there after the product said you were signed out. `logout --help`
403
+ * offers `--all-devices` for "a stolen-laptop incident", which is the one
404
+ * situation where that is exactly wrong.
405
+ *
406
+ * Returns the services it actually removed something from, so the caller can
407
+ * say what happened instead of destroying credentials silently.
408
+ *
409
+ * NOTE this also signs out the retired `oversky` binary, if one is still
410
+ * installed. That is the intent — same account, same machine — and the cutover
411
+ * runbook already lists these items for removal.
412
+ */
413
+ function deleteLegacyBrandEntries() {
414
+ if (!isAvailable())
415
+ return [];
416
+ return discoverLegacyBrandServices().filter((service) => deleteEntireService(service));
417
+ }
310
418
  /**
311
419
  * Remove every CLI keychain slot for the active profile. Used by `skrr logout`.
312
420
  * Safe to call on a clean system — missing items are not an error.
@@ -316,3 +424,36 @@ function deleteAll() {
316
424
  deleteRefreshToken();
317
425
  deleteServerOrigin();
318
426
  }
427
+ /**
428
+ * Pull `--profile` out of raw argv.
429
+ *
430
+ * Deliberately minimal: it recognises `--profile x` and `--profile=x` and
431
+ * nothing else. This runs before any validation, so it must not throw, must not
432
+ * consume anything it does not own, and must not care about flag ordering —
433
+ * anything it gets wrong is corrected microseconds later when oclif parses the
434
+ * same argv properly and rejects a malformed invocation.
435
+ *
436
+ * Returns null rather than the default, so the caller can fall through to
437
+ * `OVERSKY_PROFILE` without a flag having to out-rank an env var it never saw.
438
+ */
439
+ function resolveProfileFromArgv(argv) {
440
+ // `undefined` is a real input, not a defensive flourish: a command
441
+ // constructed directly — as several suites do — has no argv yet when `init()`
442
+ // runs. The contract above says this must not throw, and it did.
443
+ if (!Array.isArray(argv))
444
+ return null;
445
+ for (let i = 0; i < argv.length; i += 1) {
446
+ const token = argv[i];
447
+ if (token === '--')
448
+ break;
449
+ if (token === '--profile') {
450
+ const value = argv[i + 1];
451
+ return value && !value.startsWith('-') ? value : null;
452
+ }
453
+ if (token.startsWith('--profile=')) {
454
+ const value = token.slice('--profile='.length);
455
+ return value.length > 0 ? value : null;
456
+ }
457
+ }
458
+ return null;
459
+ }
package/dist/lib/login.js CHANGED
@@ -82,7 +82,7 @@ async function cliLogin(opts = {}) {
82
82
  });
83
83
  if (brokerResult.ok) {
84
84
  console.log('');
85
- console.log(' Authorized via the local oversky daemon (no browser needed).');
85
+ console.log(' Authorized via the local skrr runtime (no browser needed).');
86
86
  return {
87
87
  token: brokerResult.accessToken,
88
88
  refreshToken: brokerResult.refreshToken,
@@ -105,7 +105,7 @@ async function cliLogin(opts = {}) {
105
105
  // broker failures — missing bootstrap, network blip, server 5xx —
106
106
  // silently fall through to PKCE / device-code below.
107
107
  if (brokerResult.reason === 'ci_token_refused') {
108
- throw new Error('The local oversky daemon is bootstrapped from a CI token, which cannot broker an interactive CLI login. ' +
108
+ throw new Error('The local skrr runtime is bootstrapped from a CI token, which cannot broker an interactive CLI login. ' +
109
109
  'Use `skrr create-token --name <name>` to mint a long-lived CI credential directly, ' +
110
110
  'or run `skrr login` on a machine where the daemon was authenticated interactively.');
111
111
  }
@@ -116,7 +116,7 @@ async function cliLogin(opts = {}) {
116
116
  // "I'm logging into a different server than the daemon".
117
117
  if (brokerResult.reason === 'base_url_mismatch' && brokerResult.daemonServerUrl) {
118
118
  console.log('');
119
- console.log(` The local oversky daemon is logged in to ${brokerResult.daemonServerUrl}, but skrr is configured for ${serverUrl}.`);
119
+ console.log(` The local skrr runtime is logged in to ${brokerResult.daemonServerUrl}, but skrr is configured for ${serverUrl}.`);
120
120
  console.log(` Skipping the daemon-broker handoff. Run \`skrr login --base-url ${brokerResult.daemonServerUrl}\` to use the daemon instead.`);
121
121
  }
122
122
  }