@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
@@ -25,25 +25,40 @@
25
25
  * phase, once every peer accepts the new one;
26
26
  * 4. regenerate `first-party-harness.json`
27
27
  * (`node --import tsx scripts/emit-first-party-harness-json.mjs`);
28
- * 5. ship the data migration, which is generic over `aliases`.
28
+ * 5. ship the data migration, which is generic over `aliases` and
29
+ * `formerSpellings`;
30
+ * 6. at that rename's contract, move the spelling from `aliases` to the front
31
+ * of `formerSpellings`.
29
32
  *
30
33
  * No identifier, file, env var name or workflow input moves.
31
34
  *
32
- * ── Three spellings, kept apart on purpose ────────────────────────────────
35
+ * ── Four fields, kept apart on purpose ────────────────────────────────────
33
36
  *
34
- * `provider` CANONICAL. What the platform stores, returns and renders.
35
- * `aliases` ACCEPTED on every ingress until contract, and normalised to
36
- * canonical at the edge. Nothing new is ever written with one.
37
- * `wireProvider` What the server and a daemon say to EACH OTHER — the
38
- * capability a daemon advertises, `daemon:<p>:request|response`,
39
- * the `provider` of a session RPC. It moves after the flip and
40
- * BEFORE contract, because a daemon on a user's machine may be
41
- * any version and the server cannot tell which one sent a
42
- * request: only a released daemon that already advertises the
43
- * canonical capability lets the old one age out of the fleet.
37
+ * `provider` CANONICAL. What the platform stores, returns and renders.
38
+ * `aliases` ACCEPTED on every ingress during a rename's window, and
39
+ * normalised to canonical at the edge. Nothing new is ever
40
+ * written with one. Empty between renames.
41
+ * `formerSpellings` RETIRED. Accepted on NO ingress: no HTTP body, enum, Mongo
42
+ * filter, capability, wire event, CLI flag or trust table
43
+ * admits one. Recognised only where an ARTIFACT that predates
44
+ * the rename is read — a binary already installed, a checkout
45
+ * directory already on disk, a release already published
46
+ * under its prefix, a row or config key already written.
47
+ * Those exist whatever the platform accepts, and dropping a
48
+ * spelling from this list does not make them go away. Read it
49
+ * through `firstPartyHarnessArtifactSpellings`, never as an
50
+ * ingress check.
51
+ * `wireProvider` What the server and a daemon say to EACH OTHER — the
52
+ * capability a daemon advertises,
53
+ * `daemon:<p>:request|response`, the `provider` of a session
54
+ * RPC. It moves after the flip and BEFORE contract, because a
55
+ * daemon on a user's machine may be any version and the
56
+ * server cannot tell which one sent a request: only a
57
+ * released daemon that already advertises the canonical
58
+ * capability lets the old one age out of the fleet.
44
59
  *
45
- * The old code treated those three as one string, which is why no single
46
- * change could rename it safely. See
60
+ * The old code treated these as one string, which is why no single change could
61
+ * rename it safely. See
47
62
  * `docs/architecture/skrr-code-identifier-rename-2026-09-13.md`.
48
63
  *
49
64
  * ── Constraints on this file ──────────────────────────────────────────────
@@ -56,8 +71,10 @@
56
71
  Object.defineProperty(exports, "__esModule", { value: true });
57
72
  exports.FIRST_PARTY_HARNESS_ENGINE = exports.FIRST_PARTY_HARNESS_ENV = exports.FIRST_PARTY_HARNESS_FEED_ORIGIN = exports.FIRST_PARTY_HARNESS_PROVIDER = exports.FIRST_PARTY_HARNESS = void 0;
58
73
  exports.firstPartyHarnessSpellings = firstPartyHarnessSpellings;
74
+ exports.firstPartyHarnessArtifactSpellings = firstPartyHarnessArtifactSpellings;
59
75
  exports.isFirstPartyHarnessProvider = isFirstPartyHarnessProvider;
60
76
  exports.canonicalHarnessProvider = canonicalHarnessProvider;
77
+ exports.canonicalArtifactHarnessProvider = canonicalArtifactHarnessProvider;
61
78
  exports.harnessProviderSpellings = harnessProviderSpellings;
62
79
  exports.wireHarnessProvider = wireHarnessProvider;
63
80
  exports.isSameHarnessProvider = isSameHarnessProvider;
@@ -79,44 +96,53 @@ exports.firstPartyHarnessFeedPrefix = firstPartyHarnessFeedPrefix;
79
96
  exports.firstPartyHarnessFeedPrefixes = firstPartyHarnessFeedPrefixes;
80
97
  exports.firstPartyHarnessStagingPrefix = firstPartyHarnessStagingPrefix;
81
98
  exports.firstPartyHarnessDefaultFeedBase = firstPartyHarnessDefaultFeedBase;
82
- exports.firstPartyHarnessEnvNames = firstPartyHarnessEnvNames;
83
99
  exports.readFirstPartyHarnessEnv = readFirstPartyHarnessEnv;
100
+ exports.firstPartyHarnessEngineEnvNames = firstPartyHarnessEngineEnvNames;
101
+ exports.readFirstPartyHarnessEngineEnv = readFirstPartyHarnessEngineEnv;
102
+ exports.firstPartyHarnessEngineEnvEntries = firstPartyHarnessEngineEnvEntries;
103
+ exports.firstPartyHarnessEngineProjectDirectories = firstPartyHarnessEngineProjectDirectories;
104
+ exports.firstPartyHarnessEngineHomeDirnames = firstPartyHarnessEngineHomeDirnames;
84
105
  /** A slug usable as a directory name, a capability prefix and an event segment. */
85
106
  const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
86
107
  function defineIdentity(identity) {
87
108
  const spellings = [identity.provider, ...identity.aliases];
88
- for (const spelling of spellings) {
109
+ const every = [...spellings, ...identity.formerSpellings];
110
+ for (const spelling of every) {
89
111
  if (!SLUG.test(spelling)) {
90
112
  throw new Error(`first-party harness spelling ${JSON.stringify(spelling)} is not a slug; it becomes a ` +
91
113
  `directory name, a capability prefix and a wire event segment`);
92
114
  }
93
115
  }
94
- if (new Set(spellings).size !== spellings.length) {
95
- throw new Error('first-party harness provider and aliases must be distinct');
116
+ if (new Set(every).size !== every.length) {
117
+ throw new Error('first-party harness provider, aliases and formerSpellings must be distinct — a spelling ' +
118
+ 'is accepted or retired, never both');
96
119
  }
97
120
  if (!spellings.includes(identity.wireProvider)) {
98
121
  throw new Error(`first-party harness wireProvider ${JSON.stringify(identity.wireProvider)} must be the ` +
99
122
  'provider or one of its aliases — a peer can only accept a spelling it knows');
100
123
  }
101
- return Object.freeze({ ...identity, aliases: Object.freeze([...identity.aliases]) });
124
+ return Object.freeze({
125
+ ...identity,
126
+ aliases: Object.freeze([...identity.aliases]),
127
+ formerSpellings: Object.freeze([...identity.formerSpellings]),
128
+ });
102
129
  }
103
130
  /** The canonical slug. Written once, and read by every field that names the current spelling. */
104
131
  const PROVIDER = 'skrr-code';
105
132
  /**
106
133
  * THE identity — the only literal product name in the codebase.
107
134
  *
108
- * Phase: WIRE MOVED. `skrr-code` is canonical — stored, returned, rendered — and
109
- * it is now also what the server and a daemon say to each other. `sky-code` is
110
- * still accepted on every ingress, so a daemon released before this change
111
- * (which advertises and answers on `sky-code`) keeps working against a server
112
- * that speaks `skrr-code`, and the reverse. Contract (OSK-8663) empties
113
- * `aliases` once no connected daemon still advertises the old capability.
135
+ * Phase: CONTRACTED (OSK-8663). `skrr-code` is the only spelling any ingress
136
+ * accepts and the only one on the wire. `sky-code` is retired: nothing admits
137
+ * it, and it is still recognised where a binary, a checkout, a published
138
+ * release or a stored value written before the rename is looked for.
114
139
  */
115
140
  const IDENTITY = {
116
141
  displayName: 'skrr Code',
117
142
  command: 'skrr code',
118
143
  provider: PROVIDER,
119
- aliases: ['sky-code'],
144
+ aliases: [],
145
+ formerSpellings: ['sky-code'],
120
146
  wireProvider: PROVIDER,
121
147
  };
122
148
  exports.FIRST_PARTY_HARNESS = defineIdentity(IDENTITY);
@@ -127,6 +153,11 @@ const SPELLINGS = Object.freeze([
127
153
  ...exports.FIRST_PARTY_HARNESS.aliases,
128
154
  ]);
129
155
  const SPELLING_SET = new Set(SPELLINGS);
156
+ const ARTIFACT_SPELLINGS = Object.freeze([
157
+ ...SPELLINGS,
158
+ ...exports.FIRST_PARTY_HARNESS.formerSpellings,
159
+ ]);
160
+ const ARTIFACT_SPELLING_SET = new Set(ARTIFACT_SPELLINGS);
130
161
  const SESSION_SUFFIX = '_session';
131
162
  const ENVIRONMENT_PREFIX = 'local-';
132
163
  function asSlug(value) {
@@ -135,10 +166,30 @@ function asSlug(value) {
135
166
  const slug = value.trim().toLowerCase();
136
167
  return slug ? slug : null;
137
168
  }
138
- /** Every spelling, canonical first. The order is the resolution order. */
169
+ /**
170
+ * Every ACCEPTED spelling, canonical first — what an ingress admits. The order
171
+ * is the resolution order.
172
+ */
139
173
  function firstPartyHarnessSpellings() {
140
174
  return SPELLINGS;
141
175
  }
176
+ /**
177
+ * Every spelling an ARTIFACT that already exists may carry, canonical first:
178
+ * the accepted spellings, then every former one.
179
+ *
180
+ * For code that LOOKS FOR what an earlier release left behind — an installed
181
+ * binary, a checkout directory, an immutable published release, a stored value
182
+ * or config key — and for the tooling that must keep recognising those
183
+ * (ignore lists, the identity ratchet). A machine that never ran a post-rename
184
+ * client still has its engine under the old name, and it must still be found.
185
+ *
186
+ * Never an ingress check: a former spelling is not accepted anywhere, and a
187
+ * reader that admitted one here would silently reopen what contract closed. Use
188
+ * `firstPartyHarnessSpellings` / `isFirstPartyHarnessProvider` for that.
189
+ */
190
+ function firstPartyHarnessArtifactSpellings() {
191
+ return ARTIFACT_SPELLINGS;
192
+ }
142
193
  /** True for the canonical provider or any alias (case- and whitespace-insensitive). */
143
194
  function isFirstPartyHarnessProvider(value) {
144
195
  const slug = asSlug(value);
@@ -152,6 +203,27 @@ function isFirstPartyHarnessProvider(value) {
152
203
  function canonicalHarnessProvider(value) {
153
204
  return isFirstPartyHarnessProvider(value) ? exports.FIRST_PARTY_HARNESS.provider : value;
154
205
  }
206
+ /**
207
+ * The canonical provider for a value read back from an ARTIFACT that may predate
208
+ * a rename — any artifact spelling (canonical, alias or FORMER) becomes the
209
+ * provider; every other value is returned unchanged.
210
+ *
211
+ * For values nobody migrates and nobody can refuse: a harness row's stored
212
+ * display NAME (the operator migration rewrites `provider`, never `name`), a
213
+ * daemon's config file, a harness session binding on a user's machine. Read raw
214
+ * after contract, each would name an unknown provider — a label that renders the
215
+ * retired name again, a local session silently running another backend, a live
216
+ * resume context reset as new — with no error anywhere.
217
+ *
218
+ * NEVER for anything a peer, a client or an operator SENDS, and never for a
219
+ * trust, dispatch or admission decision: a former spelling is accepted on no
220
+ * ingress, and canonicalising one here would reopen what contract closed. Use
221
+ * `canonicalHarnessProvider` for those.
222
+ */
223
+ function canonicalArtifactHarnessProvider(value) {
224
+ const slug = asSlug(value);
225
+ return slug !== null && ARTIFACT_SPELLING_SET.has(slug) ? exports.FIRST_PARTY_HARNESS.provider : value;
226
+ }
155
227
  /**
156
228
  * Every spelling a stored row for this provider may carry, canonical first —
157
229
  * for a Mongo `$in` filter while rows written before a flip still exist. Any
@@ -231,9 +303,18 @@ function canonicalExecutionEnvironment(value) {
231
303
  function firstPartyHarnessBinaryName(provider = exports.FIRST_PARTY_HARNESS.provider, platform) {
232
304
  return platform === 'win32' ? `${provider}.exe` : provider;
233
305
  }
234
- /** Every binary file name, canonical first — the lookup order. */
306
+ /**
307
+ * Every binary file name an install may carry, canonical first — the lookup
308
+ * order, and the names an install links to the canonical binary.
309
+ *
310
+ * Built from the ARTIFACT spellings, former ones included: binaries are
311
+ * artifacts. An engine installed before a rename is still on disk under its old
312
+ * name, and a platform CLI or daemon built before it still looks for that name,
313
+ * so resolvers try it and installers leave a link at it. Neither makes the
314
+ * spelling acceptable on any ingress.
315
+ */
235
316
  function firstPartyHarnessBinaryNames(platform) {
236
- return SPELLINGS.map((spelling) => firstPartyHarnessBinaryName(spelling, platform));
317
+ return ARTIFACT_SPELLINGS.map((spelling) => firstPartyHarnessBinaryName(spelling, platform));
237
318
  }
238
319
  /**
239
320
  * The engine home directory name under the skrr config root.
@@ -244,7 +325,8 @@ function firstPartyHarnessBinaryNames(platform) {
244
325
  * the managed `AGENTS.md` from `<home>`. Deriving it from the provider would, on
245
326
  * a fresh install after a provider rename, create a second directory the engine
246
327
  * never reads — managed instructions would stop applying with no error. It moves
247
- * when the engine's namespace moves, and not before.
328
+ * when the engine's namespace moves, and not before; it did in OSK-8674, and
329
+ * `migrateFirstPartyHarnessHome` moves a home under a legacy name onto it.
248
330
  */
249
331
  function firstPartyHarnessHomeDirname() {
250
332
  return exports.FIRST_PARTY_HARNESS_ENGINE.appName;
@@ -262,7 +344,13 @@ exports.FIRST_PARTY_HARNESS_FEED_ORIGIN = 'https://updates.oversky.ai';
262
344
  function firstPartyHarnessFeedPrefix(provider = exports.FIRST_PARTY_HARNESS.provider) {
263
345
  return `${provider}/`;
264
346
  }
265
- /** Every feed prefix, canonical first. Publishing writes all of them. */
347
+ /**
348
+ * Every feed prefix a publish WRITES, canonical first: the accepted spellings.
349
+ *
350
+ * A former spelling's prefix still holds every release published under it, and
351
+ * those stay served — their artifact URLs are inside signed manifests — but
352
+ * nothing new is written there, so its channel pointers stop moving at contract.
353
+ */
266
354
  function firstPartyHarnessFeedPrefixes() {
267
355
  return SPELLINGS.map((spelling) => firstPartyHarnessFeedPrefix(spelling));
268
356
  }
@@ -274,97 +362,195 @@ function firstPartyHarnessStagingPrefix(provider = exports.FIRST_PARTY_HARNESS.p
274
362
  function firstPartyHarnessDefaultFeedBase(provider = exports.FIRST_PARTY_HARNESS.provider) {
275
363
  return `${exports.FIRST_PARTY_HARNESS_FEED_ORIGIN}/${provider}`;
276
364
  }
277
- function envName(name, legacy) {
278
- return Object.freeze({ name, legacy: Object.freeze([...legacy]) });
365
+ function envName(name) {
366
+ return Object.freeze({ name });
279
367
  }
280
368
  /**
281
- * Every environment variable the platform reads for the first-party harness,
282
- * by purpose. The neutral name wins; a legacy name is read only when the neutral
283
- * one is unset or blank, because a variable set in an ECS task definition, a
284
- * user's shell or a LaunchAgent does not change when the code does.
369
+ * Every environment variable the PLATFORM reads for the first-party harness, by
370
+ * purpose, under one brand-neutral name each — so a product rename never renames
371
+ * one.
372
+ *
373
+ * The product-named variables these replaced were read second until contract
374
+ * (OSK-8663) and are not read at all now. The ENGINE's own variables are a
375
+ * different namespace (`FIRST_PARTY_HARNESS_ENGINE.env`): the fork reads them,
376
+ * so a platform reader that must agree with the engine consults the engine's
377
+ * names explicitly (`readFirstPartyHarnessEngineEnv`) — the engine home is the
378
+ * one case (`home` below).
285
379
  */
286
380
  exports.FIRST_PARTY_HARNESS_ENV = Object.freeze({
287
381
  /** Absolute path to an engine binary, overriding resolution. */
288
- path: envName('SKRR_FIRST_PARTY_HARNESS_PATH', ['OVERSKY_SKY_CODE_PATH']),
382
+ path: envName('SKRR_FIRST_PARTY_HARNESS_PATH'),
289
383
  /** Forces the CLI's managed-inference mode. */
290
- managed: envName('SKRR_FIRST_PARTY_HARNESS_MANAGED', ['OVERSKY_SKY_CODE_MANAGED']),
384
+ managed: envName('SKRR_FIRST_PARTY_HARNESS_MANAGED'),
291
385
  /** Update channel. */
292
- channel: envName('SKRR_FIRST_PARTY_HARNESS_CHANNEL', ['OVERSKY_SKY_CODE_CHANNEL']),
386
+ channel: envName('SKRR_FIRST_PARTY_HARNESS_CHANNEL'),
293
387
  /** Update-feed base URL override. */
294
- updateFeedBase: envName('SKRR_FIRST_PARTY_HARNESS_UPDATE_FEED_BASE', [
295
- 'OVERSKY_SKY_CODE_UPDATE_FEED_BASE',
296
- ]),
388
+ updateFeedBase: envName('SKRR_FIRST_PARTY_HARNESS_UPDATE_FEED_BASE'),
297
389
  /** Path to a file of development release keys. */
298
- devKeys: envName('SKRR_FIRST_PARTY_HARNESS_DEV_KEYS', ['OVERSKY_SKY_CODE_DEV_KEYS']),
390
+ devKeys: envName('SKRR_FIRST_PARTY_HARNESS_DEV_KEYS'),
299
391
  /** Engine version the desktop build bundles. */
300
- bundleVersion: envName('SKRR_FIRST_PARTY_HARNESS_BUNDLE_VERSION', ['OVERSKY_BUNDLE_SKY_CODE']),
301
- /** Engine home override (the engine reads its own name too — see ENGINE env below). */
302
- home: envName('SKRR_FIRST_PARTY_HARNESS_HOME', ['SKY_CODE_HOME']),
392
+ bundleVersion: envName('SKRR_FIRST_PARTY_HARNESS_BUNDLE_VERSION'),
393
+ /**
394
+ * Engine home override. The engine reads only its own variable, under every
395
+ * name it has had (`readFirstPartyHarnessEngineEnv(env, 'home')`), so a reader
396
+ * that resolves the home the engine will use reads those second, and a
397
+ * launcher hands the engine the resolved path under every one of them
398
+ * (`firstPartyHarnessEngineEnvEntries`).
399
+ */
400
+ home: envName('SKRR_FIRST_PARTY_HARNESS_HOME'),
303
401
  /** Engine version baked into the cloud-coding image. */
304
- cloudCodingVersion: envName('CLOUD_CODING_FIRST_PARTY_HARNESS_VERSION', [
305
- 'CLOUD_CODING_SKY_CODE_VERSION',
306
- ]),
402
+ cloudCodingVersion: envName('CLOUD_CODING_FIRST_PARTY_HARNESS_VERSION'),
307
403
  /** Engine version baked into the Dedicated Runtime AMI. */
308
- dedicatedRuntimeVersion: envName('SKRR_DEDICATED_RUNTIME_FIRST_PARTY_HARNESS_VERSION', [
309
- 'SKRR_DEDICATED_RUNTIME_SKY_CODE_VERSION',
310
- ]),
404
+ dedicatedRuntimeVersion: envName('SKRR_DEDICATED_RUNTIME_FIRST_PARTY_HARNESS_VERSION'),
311
405
  /** Engine version the desktop release workflow bundles. */
312
- desktopBundledVersion: envName('DESKTOP_BUNDLED_FIRST_PARTY_HARNESS_VERSION', [
313
- 'DESKTOP_BUNDLED_SKY_CODE_VERSION',
314
- ]),
406
+ desktopBundledVersion: envName('DESKTOP_BUNDLED_FIRST_PARTY_HARNESS_VERSION'),
315
407
  /** Update-feed bucket used by publish and promote. */
316
- updatesBucket: envName('FIRST_PARTY_HARNESS_UPDATES_BUCKET', ['SKY_CODE_UPDATES_BUCKET']),
408
+ updatesBucket: envName('FIRST_PARTY_HARNESS_UPDATES_BUCKET'),
317
409
  /** Release signing key (CI secret). */
318
- signingKey: envName('FIRST_PARTY_HARNESS_SIGNING_KEY', ['SKY_CODE_SIGNING_KEY']),
410
+ signingKey: envName('FIRST_PARTY_HARNESS_SIGNING_KEY'),
319
411
  /** Release signing key id (CI secret). */
320
- signingKeyId: envName('FIRST_PARTY_HARNESS_SIGNING_KEY_ID', ['SKY_CODE_SIGNING_KEY_ID']),
412
+ signingKeyId: envName('FIRST_PARTY_HARNESS_SIGNING_KEY_ID'),
321
413
  });
322
- /** Every name for one purpose, neutral first — for spawn-env forwarding and sanitisers. */
323
- function firstPartyHarnessEnvNames(key) {
324
- const entry = exports.FIRST_PARTY_HARNESS_ENV[key];
325
- return [entry.name, ...entry.legacy];
326
- }
327
- /** Read one purpose's variable: neutral name first, legacy names after. */
414
+ /** Read one purpose's variable. A blank value counts as unset. */
328
415
  function readFirstPartyHarnessEnv(env, key) {
329
- const entry = exports.FIRST_PARTY_HARNESS_ENV[key];
330
- for (const name of [entry.name, ...entry.legacy]) {
331
- const raw = env[name];
332
- if (typeof raw === 'string' && raw.trim() !== '') {
333
- return { value: raw.trim(), name, legacy: name !== entry.name };
334
- }
416
+ const { name } = exports.FIRST_PARTY_HARNESS_ENV[key];
417
+ const raw = env[name];
418
+ if (typeof raw === 'string' && raw.trim() !== '') {
419
+ return { value: raw.trim(), name };
335
420
  }
336
- return { value: undefined, name: undefined, legacy: false };
421
+ return { value: undefined, name: undefined };
337
422
  }
338
423
  /* ── The ENGINE's own namespace (defined inside the fork) ───────────────── */
424
+ /**
425
+ * The engine's state namespace today. Moved from `sky-code` by OSK-8674: XDG
426
+ * dirs, project directory, config basename, the engine home under the skrr
427
+ * root, and every variable the engine reads all derive from it.
428
+ */
429
+ const ENGINE_APP_NAME = 'skrr-code';
430
+ /**
431
+ * Earlier engine namespaces, newest first. Still READ on both sides of the
432
+ * daemon↔engine boundary: an engine binary, a daemon, a CLI and a desktop app
433
+ * each ship on their own schedule, and a machine holds state written under
434
+ * every name it has run.
435
+ */
436
+ const ENGINE_LEGACY_APP_NAMES = ['sky-code'];
437
+ /**
438
+ * Every variable the engine reads, by purpose, as the suffix after the app
439
+ * name's prefix (`skrr-code` → `SKRR_CODE_<suffix>`). The fork names all of its
440
+ * own variables this way, so the names follow the namespace rather than being
441
+ * written out once per spelling.
442
+ */
443
+ const ENGINE_ENV_SUFFIXES = Object.freeze({
444
+ serverPassword: 'SERVER_PASSWORD',
445
+ serverUsername: 'SERVER_USERNAME',
446
+ home: 'HOME',
447
+ config: 'CONFIG',
448
+ appName: 'APP_NAME',
449
+ scriptName: 'SCRIPT_NAME',
450
+ allowUpstreamEgress: 'ALLOW_UPSTREAM_EGRESS',
451
+ compatOpencode: 'COMPAT_OPENCODE',
452
+ noCompatPrompts: 'NO_COMPAT_PROMPTS',
453
+ });
454
+ function engineEnvName(appName, key) {
455
+ return `${appName.toUpperCase().replace(/-/g, '_')}_${ENGINE_ENV_SUFFIXES[key]}`;
456
+ }
457
+ function engineEnvTable(appName) {
458
+ return Object.freeze(Object.fromEntries(Object.keys(ENGINE_ENV_SUFFIXES).map((key) => [
459
+ key,
460
+ engineEnvName(appName, key),
461
+ ])));
462
+ }
463
+ function defineEngineNamespace(appName, legacyAppNames) {
464
+ const every = [appName, ...legacyAppNames];
465
+ for (const name of every) {
466
+ if (!SLUG.test(name)) {
467
+ throw new Error(`first-party harness engine app name ${JSON.stringify(name)} is not a slug; it becomes a ` +
468
+ 'directory name and a variable prefix');
469
+ }
470
+ }
471
+ if (new Set(every).size !== every.length) {
472
+ throw new Error('first-party harness engine appName and legacyAppNames must be distinct');
473
+ }
474
+ return Object.freeze({
475
+ /**
476
+ * The engine's state namespace: XDG dirs, project marker, config basename, and
477
+ * the engine home under the skrr root (`firstPartyHarnessHomeDirname`).
478
+ */
479
+ appName,
480
+ /**
481
+ * Earlier `appName`s, newest first. `migrateFirstPartyHarnessHome` moves a home
482
+ * under one of them onto the current name and leaves a symlink; readers of
483
+ * engine state look under them after the current name.
484
+ */
485
+ legacyAppNames: Object.freeze([...legacyAppNames]),
486
+ /** Project-local configuration directory inside a user's repository. */
487
+ projectDirectory: `.${appName}`,
488
+ /**
489
+ * Earlier project directories, newest first. The engine still reads them and
490
+ * the platform never rewrites a user's repository to move one, so a reader
491
+ * looks here after `projectDirectory`, and a writer adopts an existing file
492
+ * here rather than creating a second one beside it.
493
+ */
494
+ legacyProjectDirectories: Object.freeze(legacyAppNames.map((name) => `.${name}`)),
495
+ /**
496
+ * Environment variables the engine reads, under the CURRENT namespace. The
497
+ * engine also reads each one under every legacy namespace, second — use
498
+ * `readFirstPartyHarnessEngineEnv` to read one the way the engine does and
499
+ * `firstPartyHarnessEngineEnvEntries` to set one every engine build honours.
500
+ */
501
+ env: engineEnvTable(appName),
502
+ });
503
+ }
339
504
  /**
340
505
  * Names the ENGINE itself defines and reads. The platform sets or reads them
341
506
  * across the daemon↔engine boundary, so they are recorded here as values rather
342
- * than scattered as literals — but they are the fork's to change, and moving
343
- * one is a two-sided change (contract §6). Not part of this identity's rename.
507
+ * than scattered as literals. They are the fork's names, and moving one is a
508
+ * two-sided change: the engine reads the new name first and every legacy name
509
+ * second, and while engine and platform releases can arrive in either order the
510
+ * platform SETS every name and READS new-then-legacy (contract §6, OSK-8674).
344
511
  */
345
- exports.FIRST_PARTY_HARNESS_ENGINE = Object.freeze({
346
- /**
347
- * The engine's state namespace: XDG dirs, project marker, config basename, and
348
- * the engine home under the skrr root (`firstPartyHarnessHomeDirname`).
349
- */
350
- appName: 'sky-code',
351
- /**
352
- * Earlier `appName`s whose home directory `migrateFirstPartyHarnessHome` moves
353
- * onto the current one. Empty until the engine's namespace is renamed.
354
- */
355
- legacyAppNames: Object.freeze([]),
356
- /** Project-local configuration directory inside a user's repository. */
357
- projectDirectory: '.sky-code',
358
- /** Environment variables the engine reads. */
359
- env: Object.freeze({
360
- serverPassword: 'SKY_CODE_SERVER_PASSWORD',
361
- serverUsername: 'SKY_CODE_SERVER_USERNAME',
362
- home: 'SKY_CODE_HOME',
363
- config: 'SKY_CODE_CONFIG',
364
- appName: 'SKY_CODE_APP_NAME',
365
- scriptName: 'SKY_CODE_SCRIPT_NAME',
366
- allowUpstreamEgress: 'SKY_CODE_ALLOW_UPSTREAM_EGRESS',
367
- compatOpencode: 'SKY_CODE_COMPAT_OPENCODE',
368
- noCompatPrompts: 'SKY_CODE_NO_COMPAT_PROMPTS',
369
- }),
370
- });
512
+ exports.FIRST_PARTY_HARNESS_ENGINE = defineEngineNamespace(ENGINE_APP_NAME, ENGINE_LEGACY_APP_NAMES);
513
+ /**
514
+ * Every name the engine reads for one purpose, current namespace first — the
515
+ * engine's own lookup order.
516
+ */
517
+ function firstPartyHarnessEngineEnvNames(key) {
518
+ return [exports.FIRST_PARTY_HARNESS_ENGINE.appName, ...exports.FIRST_PARTY_HARNESS_ENGINE.legacyAppNames].map((appName) => engineEnvName(appName, key));
519
+ }
520
+ /**
521
+ * Read one engine variable the way the engine reads it: the current name, then
522
+ * every legacy name. A blank value counts as unset.
523
+ */
524
+ function readFirstPartyHarnessEngineEnv(env, key) {
525
+ for (const name of firstPartyHarnessEngineEnvNames(key)) {
526
+ const raw = env[name];
527
+ if (typeof raw === 'string' && raw.trim() !== '') {
528
+ return { value: raw.trim(), name };
529
+ }
530
+ }
531
+ return { value: undefined, name: undefined };
532
+ }
533
+ /**
534
+ * One engine variable set under EVERY name the engine has read, for a process
535
+ * that spawns the engine. An engine binary is installed on its own schedule, so
536
+ * a launcher cannot know whether the one it starts reads the current name or a
537
+ * legacy one; setting both means either build receives the value.
538
+ */
539
+ function firstPartyHarnessEngineEnvEntries(key, value) {
540
+ return Object.fromEntries(firstPartyHarnessEngineEnvNames(key).map((name) => [name, value]));
541
+ }
542
+ /** Every engine project directory, current first — the engine's read order. */
543
+ function firstPartyHarnessEngineProjectDirectories() {
544
+ return [
545
+ exports.FIRST_PARTY_HARNESS_ENGINE.projectDirectory,
546
+ ...exports.FIRST_PARTY_HARNESS_ENGINE.legacyProjectDirectories,
547
+ ];
548
+ }
549
+ /**
550
+ * Every engine home directory name under a config root, current first: where an
551
+ * engine home may exist on disk. `firstPartyHarnessHomeDirname` is the one the
552
+ * platform writes.
553
+ */
554
+ function firstPartyHarnessEngineHomeDirnames() {
555
+ return [exports.FIRST_PARTY_HARNESS_ENGINE.appName, ...exports.FIRST_PARTY_HARNESS_ENGINE.legacyAppNames];
556
+ }
@@ -108,40 +108,28 @@ export interface FirstPartyHarnessFeedCandidate {
108
108
  * Every place a manifest may be published, in the order an installer should
109
109
  * try them: the canonical spelling first, then each alias.
110
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.
111
+ * Why a list: publishing writes every accepted spelling, but an installer can
112
+ * ship before the first release that does, and a machine can point at a feed
113
+ * published by an older pipeline. An installer that asked only for the canonical
114
+ * path would then find NOTHING and report "no release" on a feed that has one.
115
+ * Trying the aliases on not-found closes that window in both directions without
116
+ * anyone having to deploy in a particular order.
117
+ *
118
+ * WHICH spellings depends on what is being read, and the difference is the
119
+ * point:
120
+ *
121
+ * - a CHANNEL pointer (`pinnedRelease` false) is current state. Only the
122
+ * accepted spellings are tried: a former spelling's pointer stopped moving
123
+ * when publishing stopped writing it, so falling back to one would install
124
+ * whatever that channel named on the day of contract — a silent downgrade.
125
+ * - a PINNED release (`pinnedRelease` true) is an immutable artifact. Former
126
+ * spellings are tried too: a release published before the canonical prefix
127
+ * existed lives only under the old one, and its signed bytes — artifact URLs
128
+ * included — are the same wherever they are found.
117
129
  *
118
130
  * An operator override keeps its base and still tries each manifest filename,
119
131
  * since a dev feed staged by an older script names the file the old way.
120
132
  */
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;
133
+ export declare function firstPartyHarnessFeedCandidates(env?: NodeJS.ProcessEnv, options?: {
134
+ pinnedRelease?: boolean;
135
+ }): readonly FirstPartyHarnessFeedCandidate[];