@skrr-ai/cli 0.1.32 → 0.1.34

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 (114) hide show
  1. package/bin/run.js +2 -2
  2. package/dist/base-command.js +5 -0
  3. package/dist/commands/agents/list.d.ts +2 -1
  4. package/dist/commands/agents/list.js +2 -1
  5. package/dist/commands/agents/show.js +2 -1
  6. package/dist/commands/balance/statement.js +8 -4
  7. package/dist/commands/browser/install.js +6 -2
  8. package/dist/commands/browser/uninstall.js +6 -2
  9. package/dist/commands/code/acp.js +2 -1
  10. package/dist/commands/code/doctor.d.ts +1 -1
  11. package/dist/commands/code/doctor.js +6 -5
  12. package/dist/commands/code/handover.d.ts +2 -2
  13. package/dist/commands/code/handover.js +2 -2
  14. package/dist/commands/code/index.d.ts +2 -2
  15. package/dist/commands/code/index.js +18 -17
  16. package/dist/commands/code/install.d.ts +3 -3
  17. package/dist/commands/code/install.js +18 -11
  18. package/dist/commands/code/migrate.js +3 -2
  19. package/dist/commands/code/oversky-agent.d.ts +5 -4
  20. package/dist/commands/code/oversky-agent.js +24 -22
  21. package/dist/commands/code/run.js +2 -1
  22. package/dist/commands/harnesses/list.d.ts +9 -1
  23. package/dist/commands/harnesses/list.js +28 -7
  24. package/dist/commands/harnesses/show.js +7 -2
  25. package/dist/commands/instructions/install.js +16 -6
  26. package/dist/commands/learning/delete.d.ts +9 -0
  27. package/dist/commands/learning/delete.js +40 -0
  28. package/dist/commands/learning/export.d.ts +9 -0
  29. package/dist/commands/learning/export.js +56 -0
  30. package/dist/commands/learning/reset.d.ts +9 -0
  31. package/dist/commands/learning/reset.js +36 -0
  32. package/dist/commands/learning/set.d.ts +10 -0
  33. package/dist/commands/learning/set.js +52 -0
  34. package/dist/commands/learning/status.d.ts +8 -0
  35. package/dist/commands/learning/status.js +35 -0
  36. package/dist/commands/logout.js +10 -5
  37. package/dist/commands/machines/dedicated/attach.js +2 -1
  38. package/dist/commands/tasks/runs.js +3 -1
  39. package/dist/commands/workspaces/use.js +2 -1
  40. package/dist/lib/adaptive-learning.d.ts +37 -0
  41. package/dist/lib/adaptive-learning.js +39 -0
  42. package/dist/lib/auth-core-init.d.ts +23 -0
  43. package/dist/lib/auth-core-init.js +61 -0
  44. package/dist/lib/code-handover.d.ts +1 -1
  45. package/dist/lib/code-handover.js +3 -2
  46. package/dist/lib/config.d.ts +30 -4
  47. package/dist/lib/config.js +2 -1
  48. package/dist/lib/daemon-installer.d.ts +1 -1
  49. package/dist/lib/daemon-target.js +2 -2
  50. package/dist/lib/dedicated-machines.d.ts +2 -2
  51. package/dist/lib/dedicated-machines.js +11 -4
  52. package/dist/lib/exec-runtime-binary.d.ts +2 -2
  53. package/dist/lib/exec-runtime-binary.js +2 -2
  54. package/dist/lib/{sky-code-agent.d.ts → first-party-harness-agent.d.ts} +20 -19
  55. package/dist/lib/{sky-code-agent.js → first-party-harness-agent.js} +85 -47
  56. package/dist/lib/{sky-code-broker.d.ts → first-party-harness-broker.d.ts} +6 -6
  57. package/dist/lib/{sky-code-broker.js → first-party-harness-broker.js} +23 -23
  58. package/dist/lib/{sky-code-doctor.d.ts → first-party-harness-doctor.d.ts} +12 -2
  59. package/dist/lib/{sky-code-doctor.js → first-party-harness-doctor.js} +181 -94
  60. package/dist/lib/{sky-code-managed.d.ts → first-party-harness-managed.d.ts} +55 -30
  61. package/dist/lib/{sky-code-managed.js → first-party-harness-managed.js} +92 -35
  62. package/dist/lib/{sky-code.d.ts → first-party-harness.d.ts} +107 -36
  63. package/dist/lib/{sky-code.js → first-party-harness.js} +172 -73
  64. package/dist/lib/harness-provider-input.d.ts +35 -0
  65. package/dist/lib/harness-provider-input.js +57 -0
  66. package/dist/lib/harness-tiers.d.ts +8 -2
  67. package/dist/lib/harness-tiers.js +11 -6
  68. package/dist/lib/task-instruction-offer.js +10 -4
  69. package/dist/lib/worklogs.js +2 -2
  70. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/configRoot.d.ts +2 -2
  71. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/configRoot.js +2 -2
  72. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +248 -0
  73. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +361 -0
  74. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +147 -0
  75. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +180 -0
  76. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.d.ts +111 -0
  77. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.js +314 -0
  78. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.d.ts +11 -5
  79. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +14 -7
  80. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +3 -1
  81. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +51 -14
  82. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseManifest.js +1 -1
  83. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +4 -4
  84. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +4 -4
  85. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/configRoot.d.ts +2 -2
  86. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/configRoot.js +2 -2
  87. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +248 -0
  88. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +332 -0
  89. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +147 -0
  90. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +170 -0
  91. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.d.ts +111 -0
  92. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.js +303 -0
  93. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.d.ts +11 -5
  94. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +14 -7
  95. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +3 -1
  96. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +12 -2
  97. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseManifest.js +1 -1
  98. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +4 -4
  99. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +4 -4
  100. package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
  101. package/dist/node_modules/@skrr-ai/data-provider/index.js +4816 -4782
  102. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/index.d.ts +1 -1
  103. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/index.js +1 -1
  104. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.js +1 -1
  105. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/index.d.ts +1 -1
  106. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/index.js +1 -1
  107. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.js +1 -1
  108. package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
  109. package/oclif.manifest.json +34286 -33962
  110. package/package.json +5 -2
  111. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.d.ts +0 -96
  112. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.js +0 -115
  113. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.d.ts +0 -96
  114. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.js +0 -107
@@ -0,0 +1,147 @@
1
+ /**
2
+ * First-party harness update channels and feed resolution — the vocabulary, in
3
+ * the ONE place both readers resolve.
4
+ *
5
+ * It lived in `daemon/src/sky-code/channels.ts`, whose only importer was the
6
+ * daemon's own installer. That was fine while the daemon was the only thing that
7
+ * needed it, and it stopped being fine the moment a user could be told to go
8
+ * looking: `skrr code`'s not-installed message promised that `skrr code doctor`
9
+ * would name "which channel this machine follows and whether that channel has a
10
+ * published release", and the CLI had no access to any of it — so the sentence
11
+ * described a diagnosis nobody had written (dogfood OSK-274).
12
+ *
13
+ * A MIRROR in the CLI was the obvious alternative and is the wrong answer here,
14
+ * with precedent: OSK-3894 shipped exactly that for the harness trust tiers and
15
+ * OSK-3897 removed it, because a mirror proves two copies agree rather than
16
+ * proving there is one copy. `harnessTrust.ts` moved into this package for that
17
+ * reason, the daemon re-exports it, and a static check keeps the daemon from
18
+ * quietly redeclaring. This follows that road rather than reopening the one it
19
+ * replaced.
20
+ *
21
+ * Only the VOCABULARY lives here — channel names, the env var, the default, the
22
+ * URL shape and the feed candidates. The installer, the signature verification
23
+ * and the promotion machinery stay in the daemon, which is the only process that
24
+ * performs them.
25
+ *
26
+ * Every product-named value comes from `firstPartyHarness.ts`; nothing in this
27
+ * file spells the product.
28
+ */
29
+ /** Channels in promotion order. Index order IS the promotion order. */
30
+ export declare const FIRST_PARTY_HARNESS_CHANNELS: readonly ["internal", "canary", "beta", "stable"];
31
+ export type FirstPartyHarnessChannel = (typeof FIRST_PARTY_HARNESS_CHANNELS)[number];
32
+ /**
33
+ * The channel a build without an explicit setting follows.
34
+ *
35
+ * `stable`, and never inferred from anything else. A default that drifted with
36
+ * the build (say, dev builds silently on `canary`) would mean a user's update
37
+ * path depended on how their binary happened to be produced.
38
+ */
39
+ export declare const DEFAULT_FIRST_PARTY_HARNESS_CHANNEL: FirstPartyHarnessChannel;
40
+ export declare function isFirstPartyHarnessChannel(value: unknown): value is FirstPartyHarnessChannel;
41
+ /**
42
+ * The channel this process should follow.
43
+ *
44
+ * An unrecognised value falls back to the default rather than throwing. A
45
+ * typo'd channel must not brick updates altogether — that turns a harmless
46
+ * mistake into an un-updatable machine, which is the failure the updater exists
47
+ * to prevent. The caller is handed `invalid` so it can say so out loud, and
48
+ * `source` so it can name the variable it read.
49
+ */
50
+ export declare function resolveFirstPartyHarnessChannel(env?: NodeJS.ProcessEnv): {
51
+ channel: FirstPartyHarnessChannel;
52
+ invalid?: string;
53
+ source?: string;
54
+ };
55
+ /**
56
+ * Where a channel's signed manifest lives, relative to a feed base.
57
+ *
58
+ * `stable` is NOT special-cased to `latest/`. Two paths that must always agree
59
+ * is a pair that eventually will not, and the one that disagrees silently is
60
+ * the one serving production. `latest/` remains for the daemon feed and for
61
+ * anything already pointing at it; first-party harness channel clients read
62
+ * `channels/<name>/` uniformly.
63
+ */
64
+ export declare function firstPartyHarnessChannelPrefix(channel: FirstPartyHarnessChannel): string;
65
+ /**
66
+ * Manifest URL for a channel, or for a pinned version.
67
+ *
68
+ * A pinned version wins over the channel and reads its immutable per-release
69
+ * copy: pinning means "this exact build", and a channel pointer that moved
70
+ * underneath would make the pin a suggestion.
71
+ *
72
+ * Builds only MANIFEST urls, never artifact ones. `buildManifestMessage` signs
73
+ * version, minimum, forceUpdate and the per-platform sha256s; `buildArtifactMessage`
74
+ * signs the artifact URL. Neither signs a channel — so artifact URLs stay
75
+ * channel-independent and promotion is a COPY OF AN ALREADY-SIGNED OBJECT that
76
+ * never needs the release key. Put the channel in the artifact URL instead and
77
+ * every promotion becomes a re-signing ceremony, which decides whether the
78
+ * ed25519 key has to be reachable from routine automation. A helper here that
79
+ * adjusted artifact urls would silently reintroduce exactly that coupling.
80
+ *
81
+ * The same property is what lets one signed manifest be served under every
82
+ * spelling's prefix: the manifest's own path is not inside the signature.
83
+ */
84
+ export declare function resolveChannelManifestUrl(input: {
85
+ base: string;
86
+ manifestFilename: string;
87
+ channel: FirstPartyHarnessChannel;
88
+ pinnedVersion?: string | null;
89
+ }): string;
90
+ /**
91
+ * The feed base this process uses for the CANONICAL spelling.
92
+ *
93
+ * Explicit-only override: a production user must never be silently enrolled
94
+ * onto a different feed. Selecting a feed changes WHICH signed manifest is read,
95
+ * never WHETHER the signature is checked — every artifact is verified against
96
+ * the pinned keys regardless of where it came from.
97
+ */
98
+ export declare function firstPartyHarnessFeedBase(env?: NodeJS.ProcessEnv): string;
99
+ export interface FirstPartyHarnessFeedCandidate {
100
+ /** Feed base URL, no trailing slash. */
101
+ readonly base: string;
102
+ /** Manifest file name under that base. */
103
+ readonly manifestFilename: string;
104
+ /** The spelling this candidate was published under. */
105
+ readonly provider: string;
106
+ }
107
+ /**
108
+ * Every place a manifest may be published, in the order an installer should
109
+ * try them: the canonical spelling first, then each alias.
110
+ *
111
+ * Why a list: publishing writes every spelling, but an installer can ship before
112
+ * the first release that does, and a machine can point at a feed published by
113
+ * an older pipeline. An installer that asked only for the canonical path would
114
+ * then find NOTHING and report "no release" on a feed that has one. Trying the
115
+ * aliases on not-found closes that window in both directions without anyone
116
+ * having to deploy in a particular order.
117
+ *
118
+ * An operator override keeps its base and still tries each manifest filename,
119
+ * since a dev feed staged by an older script names the file the old way.
120
+ */
121
+ export declare function firstPartyHarnessFeedCandidates(env?: NodeJS.ProcessEnv): readonly FirstPartyHarnessFeedCandidate[];
122
+ /** @deprecated Use `FIRST_PARTY_HARNESS_CHANNELS`. */
123
+ export declare const SKY_CODE_CHANNELS: readonly ["internal", "canary", "beta", "stable"];
124
+ /** @deprecated Use `FirstPartyHarnessChannel`. */
125
+ export type SkyCodeChannel = FirstPartyHarnessChannel;
126
+ /** @deprecated Use `DEFAULT_FIRST_PARTY_HARNESS_CHANNEL`. */
127
+ export declare const DEFAULT_SKY_CODE_CHANNEL: "stable";
128
+ /** @deprecated Read the channel with `resolveFirstPartyHarnessChannel`; the variable has a neutral name now. */
129
+ export declare const SKY_CODE_CHANNEL_ENV: string;
130
+ /** @deprecated Use `firstPartyHarnessManifestFile()`. Names the legacy file. */
131
+ export declare const SKY_CODE_MANIFEST_FILE: string;
132
+ /** @deprecated Use `isFirstPartyHarnessChannel`. */
133
+ export declare const isSkyCodeChannel: typeof isFirstPartyHarnessChannel;
134
+ /** @deprecated Use `resolveFirstPartyHarnessChannel`. */
135
+ export declare const resolveSkyCodeChannel: typeof resolveFirstPartyHarnessChannel;
136
+ /** @deprecated Use `firstPartyHarnessChannelPrefix`. */
137
+ export declare const skyCodeChannelPrefix: typeof firstPartyHarnessChannelPrefix;
138
+ /** @deprecated Use `firstPartyHarnessDefaultFeedBase()`. Names the legacy feed. */
139
+ export declare const DEFAULT_SKY_CODE_FEED_BASE: string;
140
+ /** @deprecated Read the feed with `firstPartyHarnessFeedBase`; the variable has a neutral name now. */
141
+ export declare const SKY_CODE_FEED_BASE_ENV: string;
142
+ /**
143
+ * @deprecated Use `firstPartyHarnessFeedCandidates`. Keeps the LEGACY feed as its
144
+ * default, because that is the only feed a caller compiled against this name knows
145
+ * how to read.
146
+ */
147
+ export declare function skyCodeFeedBase(env?: NodeJS.ProcessEnv): string;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ /**
3
+ * First-party harness update channels and feed resolution — the vocabulary, in
4
+ * the ONE place both readers resolve.
5
+ *
6
+ * It lived in `daemon/src/sky-code/channels.ts`, whose only importer was the
7
+ * daemon's own installer. That was fine while the daemon was the only thing that
8
+ * needed it, and it stopped being fine the moment a user could be told to go
9
+ * looking: `skrr code`'s not-installed message promised that `skrr code doctor`
10
+ * would name "which channel this machine follows and whether that channel has a
11
+ * published release", and the CLI had no access to any of it — so the sentence
12
+ * described a diagnosis nobody had written (dogfood OSK-274).
13
+ *
14
+ * A MIRROR in the CLI was the obvious alternative and is the wrong answer here,
15
+ * with precedent: OSK-3894 shipped exactly that for the harness trust tiers and
16
+ * OSK-3897 removed it, because a mirror proves two copies agree rather than
17
+ * proving there is one copy. `harnessTrust.ts` moved into this package for that
18
+ * reason, the daemon re-exports it, and a static check keeps the daemon from
19
+ * quietly redeclaring. This follows that road rather than reopening the one it
20
+ * replaced.
21
+ *
22
+ * Only the VOCABULARY lives here — channel names, the env var, the default, the
23
+ * URL shape and the feed candidates. The installer, the signature verification
24
+ * and the promotion machinery stay in the daemon, which is the only process that
25
+ * performs them.
26
+ *
27
+ * Every product-named value comes from `firstPartyHarness.ts`; nothing in this
28
+ * file spells the product.
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.SKY_CODE_FEED_BASE_ENV = exports.DEFAULT_SKY_CODE_FEED_BASE = exports.skyCodeChannelPrefix = exports.resolveSkyCodeChannel = exports.isSkyCodeChannel = exports.SKY_CODE_MANIFEST_FILE = exports.SKY_CODE_CHANNEL_ENV = exports.DEFAULT_SKY_CODE_CHANNEL = exports.SKY_CODE_CHANNELS = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL = exports.FIRST_PARTY_HARNESS_CHANNELS = void 0;
32
+ exports.isFirstPartyHarnessChannel = isFirstPartyHarnessChannel;
33
+ exports.resolveFirstPartyHarnessChannel = resolveFirstPartyHarnessChannel;
34
+ exports.firstPartyHarnessChannelPrefix = firstPartyHarnessChannelPrefix;
35
+ exports.resolveChannelManifestUrl = resolveChannelManifestUrl;
36
+ exports.firstPartyHarnessFeedBase = firstPartyHarnessFeedBase;
37
+ exports.firstPartyHarnessFeedCandidates = firstPartyHarnessFeedCandidates;
38
+ exports.skyCodeFeedBase = skyCodeFeedBase;
39
+ const firstPartyHarness_js_1 = require("./firstPartyHarness.js");
40
+ /** Channels in promotion order. Index order IS the promotion order. */
41
+ exports.FIRST_PARTY_HARNESS_CHANNELS = ['internal', 'canary', 'beta', 'stable'];
42
+ /**
43
+ * The channel a build without an explicit setting follows.
44
+ *
45
+ * `stable`, and never inferred from anything else. A default that drifted with
46
+ * the build (say, dev builds silently on `canary`) would mean a user's update
47
+ * path depended on how their binary happened to be produced.
48
+ */
49
+ exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL = 'stable';
50
+ function isFirstPartyHarnessChannel(value) {
51
+ return (typeof value === 'string' && exports.FIRST_PARTY_HARNESS_CHANNELS.includes(value));
52
+ }
53
+ /**
54
+ * The channel this process should follow.
55
+ *
56
+ * An unrecognised value falls back to the default rather than throwing. A
57
+ * typo'd channel must not brick updates altogether — that turns a harmless
58
+ * mistake into an un-updatable machine, which is the failure the updater exists
59
+ * to prevent. The caller is handed `invalid` so it can say so out loud, and
60
+ * `source` so it can name the variable it read.
61
+ */
62
+ function resolveFirstPartyHarnessChannel(env = process.env) {
63
+ const reading = (0, firstPartyHarness_js_1.readFirstPartyHarnessEnv)(env, 'channel');
64
+ if (!reading.value)
65
+ return { channel: exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL };
66
+ if (isFirstPartyHarnessChannel(reading.value)) {
67
+ return { channel: reading.value, source: reading.name };
68
+ }
69
+ return {
70
+ channel: exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL,
71
+ invalid: reading.value,
72
+ source: reading.name,
73
+ };
74
+ }
75
+ /**
76
+ * Where a channel's signed manifest lives, relative to a feed base.
77
+ *
78
+ * `stable` is NOT special-cased to `latest/`. Two paths that must always agree
79
+ * is a pair that eventually will not, and the one that disagrees silently is
80
+ * the one serving production. `latest/` remains for the daemon feed and for
81
+ * anything already pointing at it; first-party harness channel clients read
82
+ * `channels/<name>/` uniformly.
83
+ */
84
+ function firstPartyHarnessChannelPrefix(channel) {
85
+ return `channels/${channel}/`;
86
+ }
87
+ /**
88
+ * Manifest URL for a channel, or for a pinned version.
89
+ *
90
+ * A pinned version wins over the channel and reads its immutable per-release
91
+ * copy: pinning means "this exact build", and a channel pointer that moved
92
+ * underneath would make the pin a suggestion.
93
+ *
94
+ * Builds only MANIFEST urls, never artifact ones. `buildManifestMessage` signs
95
+ * version, minimum, forceUpdate and the per-platform sha256s; `buildArtifactMessage`
96
+ * signs the artifact URL. Neither signs a channel — so artifact URLs stay
97
+ * channel-independent and promotion is a COPY OF AN ALREADY-SIGNED OBJECT that
98
+ * never needs the release key. Put the channel in the artifact URL instead and
99
+ * every promotion becomes a re-signing ceremony, which decides whether the
100
+ * ed25519 key has to be reachable from routine automation. A helper here that
101
+ * adjusted artifact urls would silently reintroduce exactly that coupling.
102
+ *
103
+ * The same property is what lets one signed manifest be served under every
104
+ * spelling's prefix: the manifest's own path is not inside the signature.
105
+ */
106
+ function resolveChannelManifestUrl(input) {
107
+ const base = input.base.replace(/\/+$/, '');
108
+ if (input.pinnedVersion) {
109
+ return `${base}/releases/${input.pinnedVersion}/${input.manifestFilename}`;
110
+ }
111
+ return `${base}/${firstPartyHarnessChannelPrefix(input.channel)}${input.manifestFilename}`;
112
+ }
113
+ /**
114
+ * The feed base this process uses for the CANONICAL spelling.
115
+ *
116
+ * Explicit-only override: a production user must never be silently enrolled
117
+ * onto a different feed. Selecting a feed changes WHICH signed manifest is read,
118
+ * never WHETHER the signature is checked — every artifact is verified against
119
+ * the pinned keys regardless of where it came from.
120
+ */
121
+ function firstPartyHarnessFeedBase(env = process.env) {
122
+ const override = (0, firstPartyHarness_js_1.readFirstPartyHarnessEnv)(env, 'updateFeedBase').value;
123
+ return (override || (0, firstPartyHarness_js_1.firstPartyHarnessDefaultFeedBase)()).replace(/\/+$/, '');
124
+ }
125
+ /**
126
+ * Every place a manifest may be published, in the order an installer should
127
+ * try them: the canonical spelling first, then each alias.
128
+ *
129
+ * Why a list: publishing writes every spelling, but an installer can ship before
130
+ * the first release that does, and a machine can point at a feed published by
131
+ * an older pipeline. An installer that asked only for the canonical path would
132
+ * then find NOTHING and report "no release" on a feed that has one. Trying the
133
+ * aliases on not-found closes that window in both directions without anyone
134
+ * having to deploy in a particular order.
135
+ *
136
+ * An operator override keeps its base and still tries each manifest filename,
137
+ * since a dev feed staged by an older script names the file the old way.
138
+ */
139
+ function firstPartyHarnessFeedCandidates(env = process.env) {
140
+ const override = (0, firstPartyHarness_js_1.readFirstPartyHarnessEnv)(env, 'updateFeedBase').value?.replace(/\/+$/, '');
141
+ return (0, firstPartyHarness_js_1.firstPartyHarnessSpellings)().map((provider) => ({
142
+ base: override || (0, firstPartyHarness_js_1.firstPartyHarnessDefaultFeedBase)(provider),
143
+ manifestFilename: (0, firstPartyHarness_js_1.firstPartyHarnessManifestFile)(provider),
144
+ provider,
145
+ }));
146
+ }
147
+ /* ── Deprecated names ───────────────────────────────────────────────────────
148
+ *
149
+ * `@skrr-ai/auth-core` is PUBLISHED, and the released CLI resolves it with a
150
+ * caret range. An installed CLI that picks up this version must still find the
151
+ * names it was compiled against, or it crashes at import. These aliases leave in
152
+ * the contract phase (OSK-8663), with a minor-version bump — never a patch.
153
+ */
154
+ /** @deprecated Use `FIRST_PARTY_HARNESS_CHANNELS`. */
155
+ exports.SKY_CODE_CHANNELS = exports.FIRST_PARTY_HARNESS_CHANNELS;
156
+ /** @deprecated Use `DEFAULT_FIRST_PARTY_HARNESS_CHANNEL`. */
157
+ exports.DEFAULT_SKY_CODE_CHANNEL = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL;
158
+ /** @deprecated Read the channel with `resolveFirstPartyHarnessChannel`; the variable has a neutral name now. */
159
+ exports.SKY_CODE_CHANNEL_ENV = firstPartyHarness_js_1.FIRST_PARTY_HARNESS_ENV.channel.legacy[0];
160
+ /** @deprecated Use `firstPartyHarnessManifestFile()`. Names the legacy file. */
161
+ exports.SKY_CODE_MANIFEST_FILE = (0, firstPartyHarness_js_1.firstPartyHarnessManifestFile)(firstPartyHarness_js_1.FIRST_PARTY_HARNESS.wireProvider);
162
+ /** @deprecated Use `isFirstPartyHarnessChannel`. */
163
+ exports.isSkyCodeChannel = isFirstPartyHarnessChannel;
164
+ /** @deprecated Use `resolveFirstPartyHarnessChannel`. */
165
+ exports.resolveSkyCodeChannel = resolveFirstPartyHarnessChannel;
166
+ /** @deprecated Use `firstPartyHarnessChannelPrefix`. */
167
+ exports.skyCodeChannelPrefix = firstPartyHarnessChannelPrefix;
168
+ /** @deprecated Use `firstPartyHarnessDefaultFeedBase()`. Names the legacy feed. */
169
+ exports.DEFAULT_SKY_CODE_FEED_BASE = (0, firstPartyHarness_js_1.firstPartyHarnessDefaultFeedBase)(firstPartyHarness_js_1.FIRST_PARTY_HARNESS.wireProvider);
170
+ /** @deprecated Read the feed with `firstPartyHarnessFeedBase`; the variable has a neutral name now. */
171
+ exports.SKY_CODE_FEED_BASE_ENV = firstPartyHarness_js_1.FIRST_PARTY_HARNESS_ENV.updateFeedBase.legacy[0];
172
+ /**
173
+ * @deprecated Use `firstPartyHarnessFeedCandidates`. Keeps the LEGACY feed as its
174
+ * default, because that is the only feed a caller compiled against this name knows
175
+ * how to read.
176
+ */
177
+ function skyCodeFeedBase(env = process.env) {
178
+ const override = (0, firstPartyHarness_js_1.readFirstPartyHarnessEnv)(env, 'updateFeedBase').value;
179
+ return (override || (0, firstPartyHarness_js_1.firstPartyHarnessDefaultFeedBase)(firstPartyHarness_js_1.FIRST_PARTY_HARNESS.wireProvider)).replace(/\/+$/, '');
180
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Where the first-party harness lives on disk, and the one-time move that keeps
3
+ * that true across a rename.
4
+ *
5
+ * ── Why one implementation ────────────────────────────────────────────────
6
+ *
7
+ * Three processes put an engine under the skrr root and read it back: the daemon
8
+ * installer, the CLI (`skrr code`), and the desktop app's bundled-engine
9
+ * install. Each used to build `<root>/sky-code/bin/sky-code` itself, and they
10
+ * already disagreed once about which root that was (see `configRoot.ts`). A
11
+ * rename that every one of them had to perform independently would disagree
12
+ * again, so the paths AND the move live here.
13
+ *
14
+ * ── The move ──────────────────────────────────────────────────────────────
15
+ *
16
+ * Idempotent, lossless, and safe to run concurrently from all three:
17
+ *
18
+ * 1. The HOME follows the engine's namespace. If `<root>/<appName>` does not
19
+ * exist and `<root>/<legacyAppName>` is a real directory, rename it and
20
+ * leave the legacy name as a relative symlink. (No legacy app name exists
21
+ * yet; this step is what an engine-namespace rename will use.)
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.
26
+ *
27
+ * The two follow different names on purpose: the home is shared with the
28
+ * engine, which reads its config and managed `AGENTS.md` from it, so it can only
29
+ * move when the engine's namespace does (`firstPartyHarnessHomeDirname`). The
30
+ * symlinks are the compatibility story, not an afterthought: every CLI and
31
+ * daemon already installed reads the legacy binary path, and a symlink keeps
32
+ * them working with no coordination.
33
+ *
34
+ * It never overwrites, never deletes, and never renames THROUGH a symlink, so
35
+ * running it twice, or after a downgrade, changes nothing. Any error leaves the
36
+ * tree exactly as it was and is reported rather than thrown: a failed move must
37
+ * degrade to reading the old path, not to an engine that cannot start.
38
+ *
39
+ * Skipped on Windows, where creating a symlink needs a privilege a normal user
40
+ * does not hold, and where no engine artifact is published anyway.
41
+ */
42
+ import { type Stats } from 'node:fs';
43
+ /** Suffix of the rollback copy the installer keeps beside the binary. */
44
+ export declare const FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = ".previous";
45
+ /**
46
+ * Suffix of the provenance record beside the binary. The `oversky` in it is a
47
+ * persisted file name that already exists on machines, not the product name.
48
+ */
49
+ export declare const FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX = ".oversky-provenance.json";
50
+ /** `<root>/<engine appName>` — the engine home, shared by the platform and the engine. */
51
+ export declare function firstPartyHarnessHomeDir(env?: NodeJS.ProcessEnv): string;
52
+ /** `<home>/bin`, where the platform installs the engine binary. */
53
+ export declare function firstPartyHarnessBinDir(env?: NodeJS.ProcessEnv): string;
54
+ /** The installed binary path for a spelling (canonical by default) — lookup order is canonical first. */
55
+ export declare function firstPartyHarnessInstalledBinaryPath(env?: NodeJS.ProcessEnv, provider?: string, platform?: string): string;
56
+ export interface FirstPartyHarnessHomeMigration {
57
+ /** Human-readable record of each change made, in order. Empty when nothing moved. */
58
+ readonly moved: readonly string[];
59
+ /** Why a step was not taken when it looked applicable. Never thrown. */
60
+ readonly errors: readonly string[];
61
+ /** Set when the whole migration was skipped by design. */
62
+ readonly skipped?: 'unsupported-platform';
63
+ }
64
+ /** The minimal filesystem surface the move needs; injectable for tests. */
65
+ export interface FirstPartyHarnessHomeFs {
66
+ lstat(p: string): Stats | null;
67
+ /** The raw link text of a symlink, or null when `p` is not one. */
68
+ readlink(p: string): string | null;
69
+ rename(from: string, to: string): void;
70
+ symlink(target: string, at: string, type: 'dir' | 'file'): void;
71
+ }
72
+ /**
73
+ * Move a legacy engine home and binary onto the canonical names. See the file
74
+ * header for the exact contract. Returns what it did; never throws.
75
+ *
76
+ * Inside `<home>/bin` it resolves the three layouts a rename can leave:
77
+ *
78
+ * - canonical ABSENT, an alias is a real file — the ordinary legacy install.
79
+ * The alias moves to canonical and a link is left at its name.
80
+ * - canonical is a LINK to an alias that is a real file — what an alias link
81
+ * left DURING the expand phase becomes once the flip swaps which spelling is
82
+ * canonical. Leaving it means the next install writes its provenance record
83
+ * THROUGH the link onto the old binary's record, and the legacy name then
84
+ * verifies as `tampered`. The real file is adopted onto the canonical name
85
+ * (a rename replaces the link itself, never what it points at).
86
+ * - canonical and an alias are BOTH real files and the alias is newer — a
87
+ * client that predates the flip installed an update under the old name. The
88
+ * newer engine is what every reader should run, so it is promoted: the older
89
+ * canonical binary becomes the rollback copy, and the alias becomes a link.
90
+ *
91
+ * Any other layout is left exactly as it is.
92
+ */
93
+ export declare function migrateFirstPartyHarnessHome(options?: {
94
+ env?: NodeJS.ProcessEnv;
95
+ platform?: string;
96
+ fs?: FirstPartyHarnessHomeFs;
97
+ }): FirstPartyHarnessHomeMigration;
98
+ /**
99
+ * Leave a relative symlink at every alias name — binary and provenance sidecar —
100
+ * that does not exist yet, pointing at the canonical file.
101
+ *
102
+ * Call after installing the canonical binary. A platform CLI or daemon installed
103
+ * 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.
106
+ */
107
+ export declare function linkFirstPartyHarnessAliases(options?: {
108
+ env?: NodeJS.ProcessEnv;
109
+ platform?: string;
110
+ fs?: FirstPartyHarnessHomeFs;
111
+ }): FirstPartyHarnessHomeMigration;