@skrr-ai/cli 0.1.42 → 0.1.44

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 (120) hide show
  1. package/dist/base-command.d.ts +2 -0
  2. package/dist/base-command.js +1 -0
  3. package/dist/commands/balance/index.js +1 -1
  4. package/dist/commands/balance/overage.js +4 -3
  5. package/dist/commands/balance/plan.js +10 -6
  6. package/dist/commands/balance/show.d.ts +32 -0
  7. package/dist/commands/balance/show.js +74 -3
  8. package/dist/commands/balance/usage/events.js +3 -2
  9. package/dist/commands/balance/usage.js +2 -1
  10. package/dist/commands/browser/install.js +3 -2
  11. package/dist/commands/browser/uninstall.js +3 -2
  12. package/dist/commands/code/handover.d.ts +1 -0
  13. package/dist/commands/code/handover.js +4 -0
  14. package/dist/commands/code/install.js +3 -2
  15. package/dist/commands/code/jobs/run.d.ts +1 -0
  16. package/dist/commands/code/jobs/run.js +4 -0
  17. package/dist/commands/daemon/install.js +7 -0
  18. package/dist/commands/harnesses/install.d.ts +11 -0
  19. package/dist/commands/harnesses/install.js +33 -14
  20. package/dist/commands/harnesses/installers.d.ts +10 -0
  21. package/dist/commands/harnesses/installers.js +31 -0
  22. package/dist/commands/harnesses/leases/show.js +14 -0
  23. package/dist/commands/harnesses/list.js +3 -2
  24. package/dist/commands/instructions/install.d.ts +22 -0
  25. package/dist/commands/instructions/install.js +83 -7
  26. package/dist/commands/instructions/list.js +5 -0
  27. package/dist/commands/instructions/show.d.ts +6 -0
  28. package/dist/commands/instructions/show.js +34 -1
  29. package/dist/commands/instructions/status.js +17 -1
  30. package/dist/commands/payments/wallet.js +2 -2
  31. package/dist/commands/tasks/attachments/set-role.d.ts +24 -0
  32. package/dist/commands/tasks/attachments/set-role.js +60 -0
  33. package/dist/commands/tasks/attachments/upload.d.ts +1 -0
  34. package/dist/commands/tasks/attachments/upload.js +18 -52
  35. package/dist/commands/tasks/comments/add.d.ts +1 -0
  36. package/dist/commands/tasks/comments/add.js +67 -2
  37. package/dist/commands/tasks/complete.d.ts +1 -0
  38. package/dist/commands/tasks/complete.js +56 -1
  39. package/dist/commands/tasks/create.d.ts +6 -0
  40. package/dist/commands/tasks/create.js +26 -3
  41. package/dist/commands/tasks/labels/attach.js +4 -0
  42. package/dist/commands/tasks/list.d.ts +1 -0
  43. package/dist/commands/tasks/list.js +9 -0
  44. package/dist/commands/tasks/result/submit.d.ts +3 -0
  45. package/dist/commands/tasks/result/submit.js +76 -8
  46. package/dist/commands/tasks/show.d.ts +23 -0
  47. package/dist/commands/tasks/show.js +61 -1
  48. package/dist/commands/tasks/update.js +16 -2
  49. package/dist/commands/tasks/updates/add.d.ts +1 -0
  50. package/dist/commands/tasks/updates/add.js +63 -0
  51. package/dist/commands/views/create.js +12 -2
  52. package/dist/commands/views/list.js +3 -2
  53. package/dist/commands/views/show.js +2 -0
  54. package/dist/lib/agentic-stream.d.ts +10 -4
  55. package/dist/lib/agentic-stream.js +25 -11
  56. package/dist/lib/api-fetch.js +7 -2
  57. package/dist/lib/cli-installers.d.ts +23 -4
  58. package/dist/lib/cli-installers.js +56 -8
  59. package/dist/lib/daemon-setup.d.ts +18 -1
  60. package/dist/lib/daemon-setup.js +33 -1
  61. package/dist/lib/dedicated-machines.js +4 -2
  62. package/dist/lib/file-mime.js +1 -1
  63. package/dist/lib/first-party-harness-agent.d.ts +16 -1
  64. package/dist/lib/first-party-harness-agent.js +47 -17
  65. package/dist/lib/first-party-harness-doctor.js +49 -48
  66. package/dist/lib/first-party-harness-managed.d.ts +9 -4
  67. package/dist/lib/first-party-harness-managed.js +13 -10
  68. package/dist/lib/first-party-harness.d.ts +32 -21
  69. package/dist/lib/first-party-harness.js +49 -31
  70. package/dist/lib/harness-provider-input.d.ts +18 -12
  71. package/dist/lib/harness-provider-input.js +18 -12
  72. package/dist/lib/harness-tiers.d.ts +4 -3
  73. package/dist/lib/harness-tiers.js +4 -3
  74. package/dist/lib/harnesses.d.ts +6 -0
  75. package/dist/lib/html-text.js +25 -0
  76. package/dist/lib/instruction-input.d.ts +21 -0
  77. package/dist/lib/instruction-input.js +30 -0
  78. package/dist/lib/instruction-provenance.d.ts +54 -0
  79. package/dist/lib/instruction-provenance.js +84 -0
  80. package/dist/lib/session-task-endpoints.d.ts +1 -1
  81. package/dist/lib/session-task-endpoints.js +3 -0
  82. package/dist/lib/task-asset-upload.d.ts +84 -0
  83. package/dist/lib/task-asset-upload.js +374 -0
  84. package/dist/lib/task-closure.d.ts +15 -0
  85. package/dist/lib/task-closure.js +20 -0
  86. package/dist/lib/task-view-render.d.ts +14 -0
  87. package/dist/lib/task-view-render.js +55 -0
  88. package/dist/lib/tasks.d.ts +26 -0
  89. package/dist/lib/tasks.js +38 -1
  90. package/dist/lib/views/vocabulary.d.ts +1 -1
  91. package/dist/lib/views/vocabulary.js +3 -1
  92. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +183 -58
  93. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +286 -100
  94. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +21 -33
  95. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +24 -50
  96. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.d.ts +19 -7
  97. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.js +26 -10
  98. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.d.ts +9 -5
  99. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +9 -5
  100. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  101. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +1 -13
  102. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.d.ts +21 -1
  103. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.js +75 -19
  104. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +183 -58
  105. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +279 -99
  106. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +21 -33
  107. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +24 -49
  108. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.d.ts +19 -7
  109. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.js +27 -11
  110. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.d.ts +9 -5
  111. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +9 -5
  112. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  113. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -4
  114. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.d.ts +21 -1
  115. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.js +74 -19
  116. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  117. package/dist/node_modules/@skrr-ai/data-provider/index.js +4588 -4509
  118. package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
  119. package/oclif.manifest.json +33980 -33832
  120. package/package.json +3 -3
@@ -20,9 +20,15 @@
20
20
  * leave the legacy name as a relative symlink. (No legacy app name exists
21
21
  * yet; this step is what an engine-namespace rename will use.)
22
22
  * 2. The BINARY follows the provider. Inside `<home>/bin`, if the canonical
23
- * binary does not exist and an alias binary is a real file, rename it and
24
- * each companion (rollback copy, provenance sidecar), leaving a relative
25
- * symlink at every old name.
23
+ * binary does not exist and a binary under an older spelling is a real
24
+ * file, rename it and each companion (rollback copy, provenance sidecar),
25
+ * leaving a relative symlink at every old name.
26
+ *
27
+ * "Older spelling" means every ARTIFACT spelling that is not canonical — the
28
+ * aliases AND the former spellings (`firstPartyHarnessArtifactSpellings`). An
29
+ * installed binary is an artifact: a machine that never ran a post-rename client
30
+ * still has its engine under the old name after that name stops being accepted
31
+ * anywhere, and it must still be moved rather than reinstalled beside.
26
32
  *
27
33
  * The two follow different names on purpose: the home is shared with the
28
34
  * engine, which reads its config and managed `AGENTS.md` from it, so it can only
@@ -73,7 +79,9 @@ export interface FirstPartyHarnessHomeFs {
73
79
  * Move a legacy engine home and binary onto the canonical names. See the file
74
80
  * header for the exact contract. Returns what it did; never throws.
75
81
  *
76
- * Inside `<home>/bin` it resolves the three layouts a rename can leave:
82
+ * Inside `<home>/bin` it resolves the three layouts a rename can leave. An
83
+ * "alias" below is any non-canonical artifact spelling — an accepted alias or a
84
+ * former spelling alike, since the file on disk does not know which it is now:
77
85
  *
78
86
  * - canonical ABSENT, an alias is a real file — the ordinary legacy install.
79
87
  * The alias moves to canonical and a link is left at its name.
@@ -96,13 +104,17 @@ export declare function migrateFirstPartyHarnessHome(options?: {
96
104
  fs?: FirstPartyHarnessHomeFs;
97
105
  }): FirstPartyHarnessHomeMigration;
98
106
  /**
99
- * Leave a relative symlink at every alias name — binary and provenance sidecar —
107
+ * Leave a relative symlink at every older name — binary and provenance sidecar —
100
108
  * that does not exist yet, pointing at the canonical file.
101
109
  *
102
110
  * Call after installing the canonical binary. A platform CLI or daemon installed
103
111
  * before a rename resolves the legacy name; the link keeps it on the current
104
- * engine. It never replaces anything that exists, whether a real file (an older
105
- * engine someone may still run) or a link someone else placed. Never throws.
112
+ * engine. "Older" includes FORMER spellings, not only aliases: an installed CLI
113
+ * cannot be telemetry-gated the way a connected daemon can, and it does not stop
114
+ * existing when its spelling stops being accepted, so the link outlives the
115
+ * alias window (`firstPartyHarnessBinaryNames`). It never replaces anything that
116
+ * exists, whether a real file (an older engine someone may still run) or a link
117
+ * someone else placed. Never throws.
106
118
  */
107
119
  export declare function linkFirstPartyHarnessAliases(options?: {
108
120
  env?: NodeJS.ProcessEnv;
@@ -20,9 +20,15 @@
20
20
  * leave the legacy name as a relative symlink. (No legacy app name exists
21
21
  * yet; this step is what an engine-namespace rename will use.)
22
22
  * 2. The BINARY follows the provider. Inside `<home>/bin`, if the canonical
23
- * binary does not exist and an alias binary is a real file, rename it and
24
- * each companion (rollback copy, provenance sidecar), leaving a relative
25
- * symlink at every old name.
23
+ * binary does not exist and a binary under an older spelling is a real
24
+ * file, rename it and each companion (rollback copy, provenance sidecar),
25
+ * leaving a relative symlink at every old name.
26
+ *
27
+ * "Older spelling" means every ARTIFACT spelling that is not canonical — the
28
+ * aliases AND the former spellings (`firstPartyHarnessArtifactSpellings`). An
29
+ * installed binary is an artifact: a machine that never ran a post-rename client
30
+ * still has its engine under the old name after that name stops being accepted
31
+ * anywhere, and it must still be moved rather than reinstalled beside.
26
32
  *
27
33
  * The two follow different names on purpose: the home is shared with the
28
34
  * engine, which reads its config and managed `AGENTS.md` from it, so it can only
@@ -42,7 +48,11 @@
42
48
  import { lstatSync, readlinkSync, renameSync, symlinkSync } from 'node:fs';
43
49
  import path from 'node:path';
44
50
  import { resolveConfigRoot } from './configRoot.js';
45
- import { FIRST_PARTY_HARNESS, FIRST_PARTY_HARNESS_ENGINE, firstPartyHarnessBinaryName, firstPartyHarnessHomeDirname, } from './firstPartyHarness.js';
51
+ import { FIRST_PARTY_HARNESS, FIRST_PARTY_HARNESS_ENGINE, firstPartyHarnessArtifactSpellings, firstPartyHarnessBinaryName, firstPartyHarnessHomeDirname, } from './firstPartyHarness.js';
52
+ /** Every artifact spelling except the canonical one, newest first. */
53
+ function olderSpellings() {
54
+ return firstPartyHarnessArtifactSpellings().filter((spelling) => spelling !== FIRST_PARTY_HARNESS.provider);
55
+ }
46
56
  /** Suffix of the rollback copy the installer keeps beside the binary. */
47
57
  export const FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = '.previous';
48
58
  /**
@@ -108,7 +118,9 @@ const NEWER_THRESHOLD_MS = 1000;
108
118
  * Move a legacy engine home and binary onto the canonical names. See the file
109
119
  * header for the exact contract. Returns what it did; never throws.
110
120
  *
111
- * Inside `<home>/bin` it resolves the three layouts a rename can leave:
121
+ * Inside `<home>/bin` it resolves the three layouts a rename can leave. An
122
+ * "alias" below is any non-canonical artifact spelling — an accepted alias or a
123
+ * former spelling alike, since the file on disk does not know which it is now:
112
124
  *
113
125
  * - canonical ABSENT, an alias is a real file — the ordinary legacy install.
114
126
  * The alias moves to canonical and a link is left at its name.
@@ -171,7 +183,7 @@ export function migrateFirstPartyHarnessHome(options = {}) {
171
183
  const canonicalName = firstPartyHarnessBinaryName(canonical, platform);
172
184
  const canonicalBinary = path.join(binDir, canonicalName);
173
185
  const canonicalStat = fs.lstat(canonicalBinary);
174
- const aliasNames = FIRST_PARTY_HARNESS.aliases.map((alias) => firstPartyHarnessBinaryName(alias, platform));
186
+ const aliasNames = olderSpellings().map((spelling) => firstPartyHarnessBinaryName(spelling, platform));
175
187
  /** Move `<from><suffix>` onto `<to><suffix>` and leave a link; `replaceable` says what may sit at the target. */
176
188
  const relocate = (fromName, suffix, replaceable) => {
177
189
  const from = path.join(binDir, `${fromName}${suffix}`);
@@ -260,13 +272,17 @@ export function migrateFirstPartyHarnessHome(options = {}) {
260
272
  return { moved, errors };
261
273
  }
262
274
  /**
263
- * Leave a relative symlink at every alias name — binary and provenance sidecar —
275
+ * Leave a relative symlink at every older name — binary and provenance sidecar —
264
276
  * that does not exist yet, pointing at the canonical file.
265
277
  *
266
278
  * Call after installing the canonical binary. A platform CLI or daemon installed
267
279
  * before a rename resolves the legacy name; the link keeps it on the current
268
- * engine. It never replaces anything that exists, whether a real file (an older
269
- * engine someone may still run) or a link someone else placed. Never throws.
280
+ * engine. "Older" includes FORMER spellings, not only aliases: an installed CLI
281
+ * cannot be telemetry-gated the way a connected daemon can, and it does not stop
282
+ * existing when its spelling stops being accepted, so the link outlives the
283
+ * alias window (`firstPartyHarnessBinaryNames`). It never replaces anything that
284
+ * exists, whether a real file (an older engine someone may still run) or a link
285
+ * someone else placed. Never throws.
270
286
  */
271
287
  export function linkFirstPartyHarnessAliases(options = {}) {
272
288
  const env = options.env ?? process.env;
@@ -282,8 +298,8 @@ export function linkFirstPartyHarnessAliases(options = {}) {
282
298
  if (!isRealFile(fs.lstat(path.join(binDir, canonicalName)))) {
283
299
  return { moved, errors };
284
300
  }
285
- for (const alias of FIRST_PARTY_HARNESS.aliases) {
286
- const aliasName = firstPartyHarnessBinaryName(alias, platform);
301
+ for (const spelling of olderSpellings()) {
302
+ const aliasName = firstPartyHarnessBinaryName(spelling, platform);
287
303
  // Sidecar FIRST: a verifier that finds the binary link must find its record.
288
304
  for (const suffix of [FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX, '']) {
289
305
  const target = `${canonicalName}${suffix}`;
@@ -49,11 +49,15 @@ export type HarnessTrustTier = 1 | 2 | 3;
49
49
  * in `daemon/src/harness-trust.ts` beside the gates that read it — the argument
50
50
  * belongs where the enforcement is, not in a shared vocabulary module.
51
51
  *
52
- * It is entered under EVERY spelling (`firstPartyHarnessSpellings`), not only the
53
- * canonical one. A backend name reaches this lookup from a daemon of any version,
54
- * and an alias that fell through to the Tier 3 default would silently strip the
55
- * harness of the trust the owner granted it — a failure that looks like a
56
- * permission prompt storm, not like a rename.
52
+ * It is entered under every ACCEPTED spelling (`firstPartyHarnessSpellings`), not
53
+ * only the canonical one. During a rename a backend name reaches this lookup from
54
+ * a daemon of any version, and an alias that fell through to the Tier 3 default
55
+ * would silently strip the harness of the trust the owner granted it — a failure
56
+ * that looks like a permission prompt storm, not like a rename.
57
+ *
58
+ * A FORMER spelling is deliberately absent. Nothing dispatches under one after
59
+ * contract, and a name that is not accepted must never inherit Tier 1 by being
60
+ * remembered: it falls to the default like any unknown backend.
57
61
  */
58
62
  export declare const HARNESS_TIERS: Readonly<Record<string, HarnessTrustTier>>;
59
63
  /**
@@ -48,11 +48,15 @@ import { firstPartyHarnessSpellings } from './firstPartyHarness.js';
48
48
  * in `daemon/src/harness-trust.ts` beside the gates that read it — the argument
49
49
  * belongs where the enforcement is, not in a shared vocabulary module.
50
50
  *
51
- * It is entered under EVERY spelling (`firstPartyHarnessSpellings`), not only the
52
- * canonical one. A backend name reaches this lookup from a daemon of any version,
53
- * and an alias that fell through to the Tier 3 default would silently strip the
54
- * harness of the trust the owner granted it — a failure that looks like a
55
- * permission prompt storm, not like a rename.
51
+ * It is entered under every ACCEPTED spelling (`firstPartyHarnessSpellings`), not
52
+ * only the canonical one. During a rename a backend name reaches this lookup from
53
+ * a daemon of any version, and an alias that fell through to the Tier 3 default
54
+ * would silently strip the harness of the trust the owner granted it — a failure
55
+ * that looks like a permission prompt storm, not like a rename.
56
+ *
57
+ * A FORMER spelling is deliberately absent. Nothing dispatches under one after
58
+ * contract, and a name that is not accepted must never inherit Tier 1 by being
59
+ * remembered: it falls to the default like any unknown backend.
56
60
  */
57
61
  export const HARNESS_TIERS = Object.freeze({
58
62
  // --- Tier 1: vendor first-party / audited ---
@@ -37,7 +37,7 @@ export { CONFIG_DIR_NAME, LEGACY_CONFIG_DIR_NAMES, NATIVE_ID_PREFIX, LEGACY_NATI
37
37
  export { findLegacyLocalState, describeLegacyState, type LegacyStateFinding, type LegacyStateReport, } from './legacyStatePreflight.js';
38
38
  export * from './firstPartyHarness.js';
39
39
  export { FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX, FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX, firstPartyHarnessBinDir, firstPartyHarnessHomeDir, firstPartyHarnessInstalledBinaryPath, linkFirstPartyHarnessAliases, migrateFirstPartyHarnessHome, type FirstPartyHarnessHomeFs, type FirstPartyHarnessHomeMigration, } from './firstPartyHarnessHome.js';
40
- export { DEFAULT_FIRST_PARTY_HARNESS_CHANNEL, FIRST_PARTY_HARNESS_CHANNELS, firstPartyHarnessChannelPrefix, firstPartyHarnessFeedBase, firstPartyHarnessFeedCandidates, isFirstPartyHarnessChannel, resolveChannelManifestUrl, resolveFirstPartyHarnessChannel, type FirstPartyHarnessChannel, type FirstPartyHarnessFeedCandidate, DEFAULT_SKY_CODE_CHANNEL, DEFAULT_SKY_CODE_FEED_BASE, SKY_CODE_CHANNELS, SKY_CODE_CHANNEL_ENV, SKY_CODE_MANIFEST_FILE, isSkyCodeChannel, resolveSkyCodeChannel, skyCodeChannelPrefix, skyCodeFeedBase, SKY_CODE_FEED_BASE_ENV, type SkyCodeChannel, } from './firstPartyHarnessChannels.js';
40
+ export { DEFAULT_FIRST_PARTY_HARNESS_CHANNEL, FIRST_PARTY_HARNESS_CHANNELS, firstPartyHarnessChannelPrefix, firstPartyHarnessFeedBase, firstPartyHarnessFeedCandidates, isFirstPartyHarnessChannel, resolveChannelManifestUrl, resolveFirstPartyHarnessChannel, type FirstPartyHarnessChannel, type FirstPartyHarnessFeedCandidate, } from './firstPartyHarnessChannels.js';
41
41
  export { OVERSKY_RELEASE_PUBLIC_KEYS, type ReleaseKey } from './releaseKeys.js';
42
42
  export { MAX_SIGS, buildArtifactMessage, buildManifestMessage, getDeltaFor, verifyArtifact, verifyManifest, type DaemonVersionManifest, type DeltaDescriptor, type ManifestSignature, type PlatformArtifact, type VerifyResult, } from './releaseManifest.js';
43
43
  export { resolveConfigRoot, configRootPath, isConfigRootOverridden } from './configRoot.js';
@@ -149,10 +149,7 @@ export * from './firstPartyHarness.js';
149
149
  // Where the first-party harness lives under the skrr root, and the one-time move
150
150
  // that keeps legacy installs working across a rename. Node-only.
151
151
  export { FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX, FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX, firstPartyHarnessBinDir, firstPartyHarnessHomeDir, firstPartyHarnessInstalledBinaryPath, linkFirstPartyHarnessAliases, migrateFirstPartyHarnessHome, } from './firstPartyHarnessHome.js';
152
- export { DEFAULT_FIRST_PARTY_HARNESS_CHANNEL, FIRST_PARTY_HARNESS_CHANNELS, firstPartyHarnessChannelPrefix, firstPartyHarnessFeedBase, firstPartyHarnessFeedCandidates, isFirstPartyHarnessChannel, resolveChannelManifestUrl, resolveFirstPartyHarnessChannel,
153
- // Deprecated names kept for installed CLIs that resolve this published package
154
- // with a caret range. Removed in the contract phase with a minor bump.
155
- DEFAULT_SKY_CODE_CHANNEL, DEFAULT_SKY_CODE_FEED_BASE, SKY_CODE_CHANNELS, SKY_CODE_CHANNEL_ENV, SKY_CODE_MANIFEST_FILE, isSkyCodeChannel, resolveSkyCodeChannel, skyCodeChannelPrefix, skyCodeFeedBase, SKY_CODE_FEED_BASE_ENV, } from './firstPartyHarnessChannels.js';
152
+ export { DEFAULT_FIRST_PARTY_HARNESS_CHANNEL, FIRST_PARTY_HARNESS_CHANNELS, firstPartyHarnessChannelPrefix, firstPartyHarnessFeedBase, firstPartyHarnessFeedCandidates, isFirstPartyHarnessChannel, resolveChannelManifestUrl, resolveFirstPartyHarnessChannel, } from './firstPartyHarnessChannels.js';
156
153
  // Daemon release trust: the pinned ed25519 keys and the canonical signature
157
154
  // messages. Shared because BOTH the daemon (self-update) and the CLI
158
155
  // (`skrr daemon install`, which runs when there is no daemon to ask) verify the
@@ -55,4 +55,24 @@ export interface LegacyStateReport {
55
55
  * real `~` passes or fails based on who runs it.
56
56
  */
57
57
  export declare function findLegacyLocalState(home?: string): LegacyStateReport;
58
- export declare function describeLegacyState(report: LegacyStateReport, binaryName: string): string;
58
+ /**
59
+ * Where the full procedure lives, by repository path. The refusal used to say
60
+ * "follow the team cutover runbook" without naming one, which stopped a person
61
+ * at a pointer they could not follow (OSK-6895). The steps the refusal prints
62
+ * are the runbook's own, so the path is for the parts a terminal message cannot
63
+ * carry: Linux and Windows service removal, Keychain entries, the old app.
64
+ */
65
+ export declare const LEGACY_CUTOVER_RUNBOOK = "docs/runbooks/skrr-cutover-dev-machine.md";
66
+ /**
67
+ * The message shown when preflight refuses.
68
+ *
69
+ * Written for someone who has just been stopped and wants to know why and what
70
+ * to do — so it names the exact paths, says explicitly that nothing was touched,
71
+ * and prints the cutover as commands to run rather than a pointer to follow. It
72
+ * does NOT offer a `--force`: the operator's next step is the backup, and a flag
73
+ * that skips a safety check is the flag everyone learns to paste.
74
+ *
75
+ * `command` is what the operator re-runs to install — `skrrd` when the runtime
76
+ * refused, `skrr daemon` when the CLI refused before fetching one.
77
+ */
78
+ export declare function describeLegacyState(report: LegacyStateReport, command: string, platform?: NodeJS.Platform): string;
@@ -43,22 +43,69 @@ export function findLegacyLocalState(home = os.homedir()) {
43
43
  return { findings, hasLegacyState: findings.length > 0 };
44
44
  }
45
45
  /**
46
- * The message shown when preflight refuses.
47
- *
48
- * Written for someone who has just been stopped and wants to know why and what
49
- * to do — so it names the exact paths, says explicitly that nothing was touched,
50
- * and points at the runbook rather than improvising a fix. It does NOT offer a
51
- * `--force`: the operator's next step is the backup the runbook specifies, and
52
- * a flag that skips a safety check is the flag everyone learns to paste.
53
- */
54
- /**
55
- * The pre-rename runtime binary. Named in the refusal above because deregistering
46
+ * The pre-rename runtime binary. Named in the refusal below because deregistering
56
47
  * the OLD service is the one step the NEW binary cannot do: `uninstall` resolves
57
48
  * the current profile's label (`ai.skrr.daemon.*`), and the legacy sweep is wired
58
49
  * only into the install path.
59
50
  */
60
51
  const LEGACY_BINARY_NAME = 'oversky';
61
- export function describeLegacyState(report, binaryName) {
52
+ /**
53
+ * Where the full procedure lives, by repository path. The refusal used to say
54
+ * "follow the team cutover runbook" without naming one, which stopped a person
55
+ * at a pointer they could not follow (OSK-6895). The steps the refusal prints
56
+ * are the runbook's own, so the path is for the parts a terminal message cannot
57
+ * carry: Linux and Windows service removal, Keychain entries, the old app.
58
+ */
59
+ export const LEGACY_CUTOVER_RUNBOOK = 'docs/runbooks/skrr-cutover-dev-machine.md';
60
+ /** Credential file patterns the runbook strips from its backup (§5.5). */
61
+ const BACKUP_CREDENTIAL_PATTERNS = ['auth.json', 'cli-auth.json', '*token*'];
62
+ function posixQuote(value) {
63
+ return `'${value.split("'").join(`'\\''`)}'`;
64
+ }
65
+ function powershellQuote(value) {
66
+ return `'${value.split("'").join("''")}'`;
67
+ }
68
+ /**
69
+ * The commands for the three steps a person runs by hand, in the shell they are
70
+ * actually in. A POSIX `rm -rf` pasted into PowerShell is not an instruction.
71
+ */
72
+ function cutoverCommands(paths, platform) {
73
+ if (platform === 'win32') {
74
+ const backupDir = '"$HOME\\skrr-cutover-backup"';
75
+ return {
76
+ backup: [
77
+ `New-Item -ItemType Directory -Force ${backupDir} | Out-Null`,
78
+ ...paths.map((p) => `Copy-Item -Recurse ${powershellQuote(p)} ${backupDir}`),
79
+ `Get-ChildItem -Recurse -File ${backupDir} -Include ${BACKUP_CREDENTIAL_PATTERNS.join(',')} | Remove-Item`,
80
+ ],
81
+ remove: paths.map((p) => `Remove-Item -Recurse -Force ${powershellQuote(p)}`),
82
+ };
83
+ }
84
+ const names = BACKUP_CREDENTIAL_PATTERNS.map((n) => `-name '${n}'`).join(' -o ');
85
+ return {
86
+ backup: [
87
+ 'mkdir -p ~/skrr-cutover-backup && chmod 700 ~/skrr-cutover-backup',
88
+ `cp -a ${paths.map(posixQuote).join(' ')} ~/skrr-cutover-backup/`,
89
+ `find ~/skrr-cutover-backup -type f \\( ${names} \\) -delete`,
90
+ ],
91
+ remove: [`rm -rf ${paths.map(posixQuote).join(' ')}`],
92
+ };
93
+ }
94
+ /**
95
+ * The message shown when preflight refuses.
96
+ *
97
+ * Written for someone who has just been stopped and wants to know why and what
98
+ * to do — so it names the exact paths, says explicitly that nothing was touched,
99
+ * and prints the cutover as commands to run rather than a pointer to follow. It
100
+ * does NOT offer a `--force`: the operator's next step is the backup, and a flag
101
+ * that skips a safety check is the flag everyone learns to paste.
102
+ *
103
+ * `command` is what the operator re-runs to install — `skrrd` when the runtime
104
+ * refused, `skrr daemon` when the CLI refused before fetching one.
105
+ */
106
+ export function describeLegacyState(report, command, platform = process.platform) {
107
+ const paths = report.findings.map((f) => f.path);
108
+ const { backup, remove } = cutoverCommands(paths, platform);
62
109
  const lines = [];
63
110
  lines.push('Found state from the previous product identity on this machine:');
64
111
  lines.push('');
@@ -69,20 +116,28 @@ export function describeLegacyState(report, binaryName) {
69
116
  lines.push('Installing beside it risks two runtimes holding two different service');
70
117
  lines.push('labels, each believing it owns the single-instance lock.');
71
118
  lines.push('');
72
- lines.push('Nothing above has been read, copied or removed. Follow the team cutover');
73
- lines.push('runbook — it takes a backup first — and then run this again:');
119
+ lines.push('Nothing above has been read, copied or removed, and no service was registered.');
120
+ lines.push('To finish the cutover:');
74
121
  lines.push('');
122
+ lines.push(' 1. Keep a copy, with its credentials removed, in case you need an old setting:');
123
+ for (const line of backup)
124
+ lines.push(` ${line}`);
75
125
  // The OLD binary, deliberately: it is the one that knows the old service
76
126
  // label. `<current> uninstall` resolves the CURRENT profile's label and would
77
127
  // report success while leaving the pre-rename service registered. Guarded by
78
128
  // "only if" rather than omitted, because this module inspects directories and
79
129
  // has no way to know whether that binary is still on PATH.
80
- lines.push(` ${LEGACY_BINARY_NAME} uninstall # only if the old runtime is still installed —`);
81
- lines.push(` # it is the one that knows the old service label`);
82
- lines.push(' # then remove the directories listed above, per the runbook');
130
+ lines.push(' 2. If the old runtime is still installed, deregister it with the old binary —');
131
+ lines.push(' it is the one that knows the old service label:');
132
+ lines.push(` ${LEGACY_BINARY_NAME} uninstall`);
133
+ lines.push(' 3. Remove the directories listed above:');
134
+ for (const line of remove)
135
+ lines.push(` ${line}`);
136
+ lines.push(' 4. Run this again:');
137
+ lines.push(` ${command} install`);
83
138
  lines.push('');
84
- lines.push(`Any pre-rename service still registered is swept by \`${binaryName} install\``);
85
- lines.push('once those directories are gone, so re-running the install completes the');
86
- lines.push('cutover either way.');
139
+ lines.push(`Any pre-rename service still registered is swept by \`${command} install\``);
140
+ lines.push('once those directories are gone, so step 4 completes the cutover either way.');
141
+ lines.push(`Full runbook, including Keychain entries and the old app: ${LEGACY_CUTOVER_RUNBOOK}`);
87
142
  return lines.join('\n');
88
143
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skrr-ai/auth-core",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "main": "dist/cjs/index.js",
5
5
  "types": "dist/esm/index.d.ts",
6
6
  "exports": {