@sema-agent/settings-schema 15.2.0 → 17.0.0
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.
- package/CHANGELOG.md +183 -0
- package/README.md +71 -46
- package/conformance/VERSION +1 -1
- package/conformance/cases/c16-deep-nesting-205/expected.json +2 -2
- package/conformance/cases/c16-deep-nesting-205/meta.json +1 -1
- package/conformance/cases/c50-s784-lane-host-legal/expected.json +6 -2
- package/conformance/cases/c50-s784-lane-host-legal/input.json +3 -1
- package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +6 -2
- package/conformance/cases/c51-s784-lane-e2b-legal/input.json +3 -1
- package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +6 -2
- package/conformance/cases/c52-s784-lane-k8s-legal/input.json +3 -1
- package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +6 -2
- package/conformance/cases/c53-s784-lane-ssh-legal/input.json +3 -1
- package/conformance/cases/c54-s784-lane-adb-legal/expected.json +6 -2
- package/conformance/cases/c54-s784-lane-adb-legal/input.json +3 -1
- package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +6 -2
- package/conformance/cases/c55-s784-lane-local-docker-legal/input.json +3 -1
- package/conformance/cases/c56-s784-lane-device-legal/expected.json +8 -2
- package/conformance/cases/c56-s784-lane-device-legal/input.json +5 -2
- package/conformance/cases/c57-s784-g1-enabled-no-lane/meta.json +1 -1
- package/conformance/cases/c58-s784-g2-multitenant-host/input.json +3 -1
- package/conformance/cases/c59-s784-g3-k8s-missing-image/input.json +3 -1
- package/conformance/cases/c60-s784-g3-e2b-missing-key/input.json +3 -1
- package/conformance/cases/c61-s784-g3-other-lane-key/input.json +3 -1
- package/conformance/cases/c62-s784-g7a-session-tidb/input.json +3 -1
- package/conformance/cases/c63-s784-host-two-replicas/input.json +3 -1
- package/conformance/cases/c64-s784-multitenant-local-store/input.json +3 -1
- package/conformance/cases/c65-s784-pg-without-db/input.json +3 -1
- package/conformance/cases/c66-s784-memory-backend-mismatch/input.json +3 -1
- package/conformance/cases/c66-s784-memory-backend-mismatch/meta.json +1 -1
- package/conformance/cases/c68-s784-device-local-store/input.json +6 -1
- package/conformance/cases/cli04-env-value-with-newline/expected.json +6 -2
- package/conformance/cases/cli04-env-value-with-newline/input.json +3 -1
- package/dist/atomic-write.d.ts +4 -0
- package/dist/atomic-write.js +20 -0
- package/dist/bundle.d.ts +0 -18
- package/dist/bundle.js +1 -54
- package/dist/cli.d.ts +1 -1
- package/dist/config-d-file.d.ts +13 -12
- package/dist/config-d-file.js +72 -27
- package/dist/config-document-refusal.d.ts +9 -7
- package/dist/config-document-refusal.js +2 -2
- package/dist/device-relay.d.ts +21 -0
- package/dist/device-relay.js +23 -0
- package/dist/env-model-table.d.ts +2 -9
- package/dist/env-model-table.js +6 -14
- package/dist/execution-lane-index.d.ts +19 -7
- package/dist/execution-lane-index.js +26 -8
- package/dist/execution-lanes.d.ts +9 -4
- package/dist/execution-lanes.js +18 -6
- package/dist/file-store.d.ts +31 -4
- package/dist/file-store.js +78 -11
- package/dist/fleet.d.ts +6 -8
- package/dist/fleet.js +5 -7
- package/dist/hooks.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/local-load.d.ts +12 -4
- package/dist/local-load.js +20 -31
- package/dist/migration-receipt.d.ts +1 -1
- package/dist/migration-receipt.js +1 -1
- package/dist/one-time-migrations.d.ts +1 -1
- package/dist/one-time-migrations.js +166 -45
- package/dist/remote-exec.js +11 -4
- package/dist/types.d.ts +15 -11
- package/dist/types.js +99 -48
- package/dist/url-shape.d.ts +18 -0
- package/dist/url-shape.js +22 -0
- package/docs/API-SURFACE.md +12 -8
- package/docs/CONFORMANCE.md +1 -7
- package/docs/CONSUMER-INTEGRATION.md +3 -3
- package/llms-full.txt +25 -29
- package/llms.txt +9 -8
- package/package.json +1 -1
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
*/
|
|
37
37
|
import { z } from "zod";
|
|
38
38
|
import { outgoingPath, redactSecrets } from "./config-fns.js";
|
|
39
|
-
import { LANE_ENV_OWNER, STORE_ENV_KEYS, isLaneEnvKey } from "./execution-lane-index.js";
|
|
40
|
-
import { EXECUTION_LANE_WORDS,
|
|
39
|
+
import { LANE_ENV_OWNER, LATE_LANE_ENV_KEYS, STORE_ENV_KEYS, isLaneEnvKey } from "./execution-lane-index.js";
|
|
40
|
+
import { EXECUTION_LANE_WORDS, STORE_BACKENDS } from "./execution-lanes.js";
|
|
41
41
|
import { INTEGER } from "./integer.js";
|
|
42
42
|
import { NOT_PLAIN_DATA, UNINSPECTABLE, UninspectableError, captureJson, dataDescriptor, emptyDocument, inspect, ownEntriesOf, ownOrThrow, readDeclaredKey, sentenceOfMinted } from "./own-key-read.js";
|
|
43
43
|
import { isJsonArray, isPlainJsonObject, jsonShapeOf } from "./plain-json-object.js";
|
|
@@ -102,9 +102,10 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
102
102
|
releasedOn: "2026-10-10",
|
|
103
103
|
removableAfter: "2026-11-09",
|
|
104
104
|
rewrites: "workers: a 6.x row (env.REMOTE_EXEC + lane keys, runtime.sessionBackend / memoryBackend / db, coords.tidb*, secretRefs.tidbPasswordEnv, store " +
|
|
105
|
-
"keys in env) is rewritten into runtime.execution + runtime.store
|
|
106
|
-
"
|
|
107
|
-
"rewrite (SS-
|
|
105
|
+
"keys in env) is rewritten into runtime.execution + runtime.store — since 16.0.0 straight into the 16.0.0 form: the lane lands as " +
|
|
106
|
+
"runtime.execution.lanes [<lane>], and env MEMORY_ENGINE_BACKEND \"tidb\" lands as store.memory.backend \"mysql\"; a row the step would have to guess for, or that still " +
|
|
107
|
+
"does not read after the rewrite, is refused and kept verbatim (readWorkersRows holds it on read). Runs last, in one workers-row step with SS-M12 (SS-M12's " +
|
|
108
|
+
"rewrite first, then this one's): the row is judged once, after both and after every other step's rewrite (SS-M7's fragment strip included)",
|
|
108
109
|
removal: "the next major after the window: delete the SS-M4 step; the 6.x tombstone fields of WorkerSpec and the reserved-env signpost stay — their SS-M4 clause goes",
|
|
109
110
|
afterRemoval: "the 6.x tombstone fields of WorkerSpec and the reserved-env refusal (permanent): a residual 6.x row is held by readWorkersRows, never rewritten",
|
|
110
111
|
},
|
|
@@ -204,6 +205,25 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
204
205
|
"refuse on it today. Where the engine refuses, the way out is to write config.env-models.json = {\"envModels\":\"replaced\"} in the " +
|
|
205
206
|
"config root; no signpost in this package",
|
|
206
207
|
},
|
|
208
|
+
{
|
|
209
|
+
id: "SS-M12",
|
|
210
|
+
oldForm: "refused",
|
|
211
|
+
introducedIn: "16.0.0",
|
|
212
|
+
releasedOn: "2026-10-30",
|
|
213
|
+
removableAfter: "2026-11-29",
|
|
214
|
+
rewrites: "workers: a 7.0–15.x row is rewritten into the 16.0.0 form, field by field — runtime.execution.lane L ⇒ runtime.execution.lanes [L] (moved); " +
|
|
215
|
+
"runtime.store.memory.backend \"tidb\" ⇒ \"mysql\" (rewritten: the same MySQL-family store, now one word with store.backend); DEVICE_RELAY_URL in env " +
|
|
216
|
+
"(the 15.x write gate took it as a plain env key) ⇒ runtime.execution.coords.DEVICE_RELAY_URL when the row declares the device lane (moved), dropped when " +
|
|
217
|
+
"it does not (dropped-inert: the engine reads it only on the device lane). Refused and kept verbatim (readWorkersRows holds it on read): a row carrying both " +
|
|
218
|
+
"lane and lanes, a DEVICE_RELAY_URL on a row that declares the device lane but sets it from a host secret (secretEnv: the value cannot be read) or writes it " +
|
|
219
|
+
"both in env and in coords, and a row that still does not read after the rewrite — a device row with no DEVICE_RELAY_URL among them (a required coordinate " +
|
|
220
|
+
"since 16.0.0; the step does not guess between a URL and \"none\"). Runs in one workers-row step with SS-M4, after every pure-rewrite step: this step's " +
|
|
221
|
+
"rewrite first, then SS-M4's, then one judgement of the row (a refusal of that judgement is filed under the newest migration that rewrote the row)",
|
|
222
|
+
removal: "the next major after the window: delete the SS-M12 step; the runtime.execution.lane tombstone field, the memory-backend refusal for \"tidb\" and the " +
|
|
223
|
+
"reserved-env signpost for DEVICE_RELAY_URL stay — their SS-M12 clause goes",
|
|
224
|
+
afterRemoval: "the runtime.execution.lane tombstone field, the memory-backend refusal for \"tidb\" and the reserved-env refusal for DEVICE_RELAY_URL (permanent): a residual " +
|
|
225
|
+
"15.x row is refused on write and held by readWorkersRows on read, never rewritten",
|
|
226
|
+
},
|
|
207
227
|
{
|
|
208
228
|
// 一条 wire 位的删码登记,不是数据迁移(id 在 WIRE_RETIREMENT_IDS,不在收据闭集)。
|
|
209
229
|
id: "SS-W1",
|
|
@@ -261,8 +281,10 @@ export function migrateStoredDocuments(docs, opts) {
|
|
|
261
281
|
for (const [k, v] of entries.value)
|
|
262
282
|
Object.defineProperty(out, k, dataDescriptor(v));
|
|
263
283
|
const receipts = [];
|
|
264
|
-
// 步序的律:纯改写步在前,拿新 schema
|
|
265
|
-
// fragment)不会先被它拒掉(否则一跑 refused、二跑才过:既不是一次完成,也不幂等)
|
|
284
|
+
// 步序的律:纯改写步在前,拿新 schema 终判改写结果的 workers 行步最后 —— 它判的是**全部**改写之后的行,别的步能修好的形(例:SS-M7 剥掉的
|
|
285
|
+
// fragment)不会先被它拒掉(否则一跑 refused、二跑才过:既不是一次完成,也不幂等)。同一条律落到行内:一行可以同时带 SS-M12 认的形(7.0+ 的单值车道)
|
|
286
|
+
// 与 SS-M4 认的形(6.x 的店字段),所以两只迁移在**一步**里按行做 —— SS-M12 先改(7.0+ 行 env 里的晚登记车道键 `DEVICE_RELAY_URL` 在 SS-M4 眼里是
|
|
287
|
+
// 「新旧两形并存」,先由 SS-M12 归位),SS-M4 再改,改完**判一次**;各判各的会互相卡死(一只判「另一只的形还在」而拒)。
|
|
266
288
|
try {
|
|
267
289
|
stepRuntimeDomain(out, opts.coverage, receipts);
|
|
268
290
|
stepHostsBounds(out, receipts);
|
|
@@ -272,7 +294,7 @@ export function migrateStoredDocuments(docs, opts) {
|
|
|
272
294
|
stepModelsDefault(out, receipts);
|
|
273
295
|
stepPromptsAxes(out, receipts);
|
|
274
296
|
stepUrlFragments(out, receipts);
|
|
275
|
-
|
|
297
|
+
stepWorkersRows(out, receipts);
|
|
276
298
|
}
|
|
277
299
|
catch (e) {
|
|
278
300
|
const sentence = sentenceOfMinted(e);
|
|
@@ -482,16 +504,21 @@ const MEMORY_ENGINE_OFF_WORDS = ["off", "false", "0", "no", "disabled"];
|
|
|
482
504
|
const MYSQL_DEFAULT_PORT = 3306;
|
|
483
505
|
const isLaneWord = (v) => typeof v === "string" && EXECUTION_LANE_WORDS.some((w) => w === v);
|
|
484
506
|
const isStoreWord = (v) => typeof v === "string" && STORE_BACKENDS.some((w) => w === v);
|
|
485
|
-
|
|
507
|
+
/** 6.x 引擎收的 `MEMORY_ENGINE_BACKEND` 词 → 今天的词(16.0.0 起 MySQL 族那一只叫 `mysql`,与店词同字)。键 = 6.x 引擎的闭集(它见 `mysql` 拒启)。 */
|
|
508
|
+
const SIX_X_MEMORY_BACKEND = new Map([
|
|
509
|
+
["file", "file"],
|
|
510
|
+
["pg", "pg"],
|
|
511
|
+
["tidb", "mysql"],
|
|
512
|
+
]);
|
|
486
513
|
/** 拒因里只点名形如词的值(短、小写);其余一律不回显(值可能是粘错位置的凭据)。 */
|
|
487
514
|
const describeWord = (v) => (typeof v === "string" && /^[a-z0-9][a-z0-9-]{0,31}$/.test(v) ? `"${v}"` : "(value not shown)");
|
|
488
515
|
const UNCHANGED = { kind: "unchanged" };
|
|
489
516
|
/**
|
|
490
|
-
* 一行的 6.x 改写。规则(每个旧键恰好一种去处):车道 —— `env.REMOTE_EXEC` 是车道词 ⇒ `runtime.execution.
|
|
517
|
+
* 一行的 6.x 改写。规则(每个旧键恰好一种去处):车道 —— `env.REMOTE_EXEC` 是车道词 ⇒ `runtime.execution.lanes`(一元表),同车道的 env 键 ⇒ `coords`、
|
|
491
518
|
* 同车道的 `secretEnv` 密钥 ⇒ `secretRefs`;别车道的键与 `REQUIRE_EXECUTION_LANE` ⇒ 丢(引擎只读被点名车道那一臂);enabled 且无车道 ⇒ 拒
|
|
492
519
|
* (不替运维猜车道)。店 —— `sessionBackend` ⇒ `store.session`(`tidb` ⇒ `mysql`);`memoryBackend` ⇒ 丢(从未到达引擎);`coords.tidb*` +
|
|
493
520
|
* `runtime.db` ⇒ `store.db`(缺端口 ⇒ 3306),`tidbPasswordEnv` ⇒ `dbPasswordEnv`;无 host ⇒ `store.backend = local`;env 里的
|
|
494
|
-
* `DB_BACKEND` / `MEMORY_ENGINE` / `MEMORY_ENGINE_BACKEND` ⇒ 搬进 `store
|
|
521
|
+
* `DB_BACKEND` / `MEMORY_ENGINE` / `MEMORY_ENGINE_BACKEND` ⇒ 搬进 `store`(方言 `tidb` ⇒ `mysql`)。表达不了 ⇒ 拒:值写错了栏、不认识的词、新旧两形并存、
|
|
495
522
|
* `DB_BACKEND=pg` 配 MySQL 族坐标。不是 6.x 形的行(没有任何旧键,该有的块都在)⇒ 不动 —— 它读不读得进是 `readWorkersRows` 的事。
|
|
496
523
|
*/
|
|
497
524
|
function migrateWorkerRow(row) {
|
|
@@ -538,7 +565,7 @@ function migrateWorkerRow(row) {
|
|
|
538
565
|
if (secretStoreKey !== undefined)
|
|
539
566
|
return refuse(["secretEnv", secretStoreKey], `${secretStoreKey} is set from a host secret — the migration cannot read it; declare runtime.store by hand`);
|
|
540
567
|
if (has(rawSecretEnv, "REMOTE_EXEC"))
|
|
541
|
-
return refuse(["secretEnv", "REMOTE_EXEC"], "the execution lane is set from a host secret — the migration cannot read it; declare runtime.execution.
|
|
568
|
+
return refuse(["secretEnv", "REMOTE_EXEC"], "the execution lane is set from a host secret — the migration cannot read it; declare runtime.execution.lanes by hand");
|
|
542
569
|
const needLane = !hasExecution && (enabled || laneTouched);
|
|
543
570
|
const needStore = !hasStore && (enabled || storeTouched);
|
|
544
571
|
// 收尾要问「原来的 env / secretEnv 有没有键」:在拷之前、浅读里问好
|
|
@@ -567,7 +594,7 @@ function migrateWorkerRow(row) {
|
|
|
567
594
|
if (rx === undefined || rx === "") {
|
|
568
595
|
if (enabled) {
|
|
569
596
|
return refuse(["env", "REMOTE_EXEC"], `an enabled worker names no execution lane (env.REMOTE_EXEC is unset) — it boots with no hands and still reports tasks completed. ` +
|
|
570
|
-
`The migration does not guess a lane: set runtime.execution.
|
|
597
|
+
`The migration does not guess a lane: set runtime.execution.lanes (${EXECUTION_LANE_WORDS.join(" | ")}) by hand, or disable the worker`);
|
|
571
598
|
}
|
|
572
599
|
for (const k of envLaneKeys)
|
|
573
600
|
drop(env, "env", k); // 停用且没点名车道 ⇒ 每个车道键都是惰性的
|
|
@@ -576,7 +603,7 @@ function migrateWorkerRow(row) {
|
|
|
576
603
|
}
|
|
577
604
|
else {
|
|
578
605
|
if (!isLaneWord(rx)) {
|
|
579
|
-
return refuse(["env", "REMOTE_EXEC"], `env.REMOTE_EXEC ${describeWord(rx)} is not an execution lane (${EXECUTION_LANE_WORDS.join(" | ")}) — the engine refuses to start with it; set runtime.execution.
|
|
606
|
+
return refuse(["env", "REMOTE_EXEC"], `env.REMOTE_EXEC ${describeWord(rx)} is not an execution lane (${EXECUTION_LANE_WORDS.join(" | ")}) — the engine refuses to start with it; set runtime.execution.lanes by hand`);
|
|
580
607
|
}
|
|
581
608
|
const lane = rx;
|
|
582
609
|
const laneCoords = emptyDocument();
|
|
@@ -615,7 +642,7 @@ function migrateWorkerRow(row) {
|
|
|
615
642
|
laneSecretRefs[k] = v;
|
|
616
643
|
delete secretEnv[k];
|
|
617
644
|
}
|
|
618
|
-
runtime.execution = { lane, coords: laneCoords, secretRefs: laneSecretRefs };
|
|
645
|
+
runtime.execution = { lanes: [lane], coords: laneCoords, secretRefs: laneSecretRefs };
|
|
619
646
|
}
|
|
620
647
|
}
|
|
621
648
|
// ── 店
|
|
@@ -709,10 +736,11 @@ function migrateWorkerRow(row) {
|
|
|
709
736
|
}
|
|
710
737
|
const memoryBackendRaw = env.MEMORY_ENGINE_BACKEND;
|
|
711
738
|
if (memoryBackendRaw !== undefined) {
|
|
712
|
-
|
|
713
|
-
|
|
739
|
+
const today = typeof memoryBackendRaw === "string" ? SIX_X_MEMORY_BACKEND.get(memoryBackendRaw) : undefined;
|
|
740
|
+
if (today === undefined) {
|
|
741
|
+
return refuse(["env", "MEMORY_ENGINE_BACKEND"], `env.MEMORY_ENGINE_BACKEND ${describeWord(memoryBackendRaw)} is not a 6.x memory backend (${[...SIX_X_MEMORY_BACKEND.keys()].join(" | ")}) — the engine refused to start with it`);
|
|
714
742
|
}
|
|
715
|
-
memory.backend =
|
|
743
|
+
memory.backend = today;
|
|
716
744
|
delete env.MEMORY_ENGINE_BACKEND;
|
|
717
745
|
}
|
|
718
746
|
if (Object.keys(memory).length > 0)
|
|
@@ -725,16 +753,95 @@ function migrateWorkerRow(row) {
|
|
|
725
753
|
delete w.env;
|
|
726
754
|
if (isPlainJsonObject(w.secretEnv) && Object.keys(secretEnv).length === 0 && secretEnvHadKeys)
|
|
727
755
|
delete w.secretEnv;
|
|
728
|
-
//
|
|
729
|
-
|
|
730
|
-
if (verdict !== undefined)
|
|
731
|
-
return refuse(verdict.field, verdict.reason);
|
|
732
|
-
const out = captureJson(w, "ordinary"); // 交出去的是寻常对象(隔离原型的深副本只是工作副本)
|
|
733
|
-
if (!out.ok)
|
|
734
|
-
throw new UninspectableError(out.sentence);
|
|
735
|
-
return { kind: "rewritten", row: out.value, dropped };
|
|
756
|
+
// 改写后的行还没判:终判在 workers 行步(两只迁移的改写都做完之后判一次,见 `stepWorkersRows`)。
|
|
757
|
+
return { kind: "rewritten", row: w, dropped };
|
|
736
758
|
}
|
|
737
|
-
|
|
759
|
+
/** SS-M12 认的晚登记车道键(16.0.0 那一批;表在 `execution-lane-index.ts`,这里只取归 SS-M12 的那几个)。 */
|
|
760
|
+
const SS_M12_ENV_KEYS = [...LATE_LANE_ENV_KEYS].filter(([, m]) => m.migration === "SS-M12").map(([k]) => k);
|
|
761
|
+
/**
|
|
762
|
+
* 一行的 15.x 改写(规则见登记表 SS-M12 行)。只认 7.0+ 行上的三种形 —— `runtime.execution.lane`、`runtime.store.memory.backend: "tidb"`、带 `runtime.execution` 的行
|
|
763
|
+
* 逃生口里的晚登记车道键 —— 逐字段改,每改一处一张收据。先浅读判要不要迁(没有这三种形的行一个字段都不动、不拷);要迁才拍快照改副本。
|
|
764
|
+
* 改完的行不在这里判:SS-M4 可能还要改同一行,读侧那一只(`workerRowVerdict`)在 workers 行步里两只都改完之后判一次(`stepWorkersRows`)。
|
|
765
|
+
*/
|
|
766
|
+
function migrateWorkerRow15(row) {
|
|
767
|
+
const refuse = (field, reason) => ({ kind: "refused", field, reason });
|
|
768
|
+
const peek = (o, k) => (isPlainJsonObject(o) ? ownOrThrow(o, k) : undefined);
|
|
769
|
+
const rawRuntime = peek(row, "runtime");
|
|
770
|
+
const rawExecution = peek(rawRuntime, "execution");
|
|
771
|
+
const laneOld = has(rawExecution, "lane");
|
|
772
|
+
const memoryTidb = peek(peek(peek(rawRuntime, "store"), "memory"), "backend") === "tidb";
|
|
773
|
+
const lateKeysAt = (slot) => (isPlainJsonObject(rawExecution) ? SS_M12_ENV_KEYS.filter((k) => has(peek(row, slot), k)) : []);
|
|
774
|
+
const late = { env: lateKeysAt("env"), secretEnv: lateKeysAt("secretEnv") };
|
|
775
|
+
if (!laneOld && !memoryTidb && late.env.length + late.secretEnv.length === 0)
|
|
776
|
+
return { kind: "unchanged" };
|
|
777
|
+
if (laneOld && has(rawExecution, "lanes")) {
|
|
778
|
+
return refuse(["runtime", "execution", "lane"], "declares runtime.execution.lane (the 15.x single lane) AND runtime.execution.lanes (the 16.0.0 lane set) — keep one form; the migration does not pick a winner");
|
|
779
|
+
}
|
|
780
|
+
const working = captureJson(row);
|
|
781
|
+
if (!working.ok)
|
|
782
|
+
throw new UninspectableError(working.sentence);
|
|
783
|
+
const w = working.value;
|
|
784
|
+
const runtime = w.runtime;
|
|
785
|
+
const changes = [];
|
|
786
|
+
if (laneOld) {
|
|
787
|
+
// 换名不换位:`lanes` 落在 `lane` 原来的键序位置上,值是只含旧车道的一元表
|
|
788
|
+
const before = runtime.execution;
|
|
789
|
+
const next = emptyDocument();
|
|
790
|
+
for (const [k, v] of Object.entries(before))
|
|
791
|
+
Object.defineProperty(next, k === "lane" ? "lanes" : k, dataDescriptor(k === "lane" ? [v] : v));
|
|
792
|
+
runtime.execution = next;
|
|
793
|
+
changes.push({ appliedAs: "moved", path: ["runtime", "execution", "lane"], to: ["runtime", "execution", "lanes"] });
|
|
794
|
+
}
|
|
795
|
+
if (memoryTidb) {
|
|
796
|
+
runtime.store.memory.backend = "mysql";
|
|
797
|
+
changes.push({ appliedAs: "rewritten", path: ["runtime", "store", "memory", "backend"] });
|
|
798
|
+
}
|
|
799
|
+
const execution = runtime.execution;
|
|
800
|
+
if (isPlainJsonObject(execution)) {
|
|
801
|
+
const lanes = isJsonArray(execution.lanes) ? execution.lanes : [];
|
|
802
|
+
for (const slot of ["env", "secretEnv"]) {
|
|
803
|
+
if (late[slot].length === 0)
|
|
804
|
+
continue;
|
|
805
|
+
const rec = w[slot];
|
|
806
|
+
for (const key of late[slot]) {
|
|
807
|
+
const owner = LANE_ENV_OWNER.get(key);
|
|
808
|
+
if (owner === undefined)
|
|
809
|
+
throw new Error(`SS-M12: ${key} has no lane in the execution lane table (package invariant broken)`);
|
|
810
|
+
if (!lanes.includes(owner.lane)) {
|
|
811
|
+
delete rec[key]; // 这台 worker 不声明那条车道:引擎只在那条车道的臂里读它,从没生效
|
|
812
|
+
changes.push({ appliedAs: "dropped-inert", path: [slot, key] });
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (owner.secret)
|
|
816
|
+
return refuse([slot, key], `${key} is a secret of lane "${owner.lane}" — declare it by hand in runtime.execution.secretRefs.${key} (the host env-NAME holding it)`);
|
|
817
|
+
if (slot === "secretEnv")
|
|
818
|
+
return refuse([slot, key], `${key} is a non-secret coordinate of lane "${owner.lane}" but is set from a host secret — the migration cannot read the value; write it in runtime.execution.coords.${key} by hand`);
|
|
819
|
+
const coords = isPlainJsonObject(execution.coords) ? execution.coords : emptyDocument();
|
|
820
|
+
if (Object.hasOwn(coords, key))
|
|
821
|
+
return refuse([slot, key], `${key} is written both in env and in runtime.execution.coords — keep one (the coordinate); the migration does not pick a winner`);
|
|
822
|
+
const value = rec[key];
|
|
823
|
+
if (typeof value !== "string")
|
|
824
|
+
return refuse([slot, key], `env.${key} is not a string`);
|
|
825
|
+
coords[key] = value;
|
|
826
|
+
execution.coords = coords;
|
|
827
|
+
delete rec[key];
|
|
828
|
+
changes.push({ appliedAs: "moved", path: [slot, key], to: ["runtime", "execution", "coords", key] });
|
|
829
|
+
}
|
|
830
|
+
if (Object.keys(rec).length === 0)
|
|
831
|
+
delete w[slot]; // 被搬空的逃生口整键去掉(可选字段;留一个 `{}` 只是噪声)
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
// 改写后的行还没判:SS-M4 可能还要改它(6.x 的店字段),终判在 workers 行步(见 `stepWorkersRows`)。
|
|
835
|
+
return { kind: "rewritten", row: w, changes };
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* workers 行步:SS-M12 与 SS-M4 在**一步**里按行做 —— SS-M12 改写(7.0+ 的形)→ SS-M4 改写(6.x 的形)→ 用读侧那一只(`workerRowVerdict`)**判一次**。
|
|
839
|
+
* 一行两只都可能要改(7.0+ 的单值车道 + 6.x 的店字段);各判各的会互相卡死:先判的那只看见另一只的旧形还在而拒,重跑同样。
|
|
840
|
+
* · 形上就改不了(两形并存、值写错了栏……)⇒ 那一只的拒收据,另一只的改写不留(行原样,收据与交回的文档一致);
|
|
841
|
+
* · 改完读不进 ⇒ 一张拒收据,记在**改了这一行的最新那只迁移**名下(这一行要抬到的就是它的形),原文留盘(读口 `readWorkersRows` 扣下它);
|
|
842
|
+
* · 读得进 ⇒ 写回寻常对象(工作副本隔离原型,只是副本),收据按迁移序:SS-M12 的逐字段收据,再 SS-M4 的 `rewritten` + `dropped-inert`。
|
|
843
|
+
*/
|
|
844
|
+
function stepWorkersRows(out, receipts) {
|
|
738
845
|
const doc = ownOrThrow(out, "workers");
|
|
739
846
|
if (!isPlainJsonObject(doc))
|
|
740
847
|
return;
|
|
@@ -746,24 +853,38 @@ function stepWorkers6x(out, receipts) {
|
|
|
746
853
|
rows.forEach((row, index) => {
|
|
747
854
|
if (!isPlainJsonObject(row) || typeof ownOrThrow(row, "name") !== "string")
|
|
748
855
|
return; // 认不出名字:整域读侧判
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
856
|
+
const at = (path) => ["workers", index, ...path];
|
|
857
|
+
const r12 = migrateWorkerRow15(row);
|
|
858
|
+
if (r12.kind === "refused") {
|
|
859
|
+
receipts.push(refused("SS-M12", "workers", at(r12.field), r12.reason));
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
const r4 = migrateWorkerRow(r12.kind === "rewritten" ? r12.row : row);
|
|
863
|
+
if (r4.kind === "refused") {
|
|
864
|
+
receipts.push(refused("SS-M4", "workers", at(r4.field), r4.reason));
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (r12.kind === "unchanged" && r4.kind === "unchanged")
|
|
868
|
+
return;
|
|
869
|
+
const final = r4.kind === "rewritten" ? r4.row : r12.row;
|
|
870
|
+
const verdict = workerRowVerdict(final, "migrated");
|
|
871
|
+
if (verdict !== undefined) {
|
|
872
|
+
receipts.push(refused(r12.kind === "rewritten" ? "SS-M12" : "SS-M4", "workers", at(verdict.field), verdict.reason));
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
const written = captureJson(final, "ordinary"); // 交出去的是寻常对象(隔离原型的深副本只是工作副本)
|
|
876
|
+
if (!written.ok)
|
|
877
|
+
throw new UninspectableError(written.sentence);
|
|
878
|
+
next ??= [...rows];
|
|
879
|
+
next[index] = written.value;
|
|
880
|
+
if (r12.kind === "rewritten") {
|
|
881
|
+
for (const c of r12.changes)
|
|
882
|
+
receipts.push(c.appliedAs === "moved" ? moved("SS-M12", "workers", at(c.path), { document: "workers", path: at(c.to) }) : receipt("SS-M12", "workers", at(c.path), c.appliedAs));
|
|
883
|
+
}
|
|
884
|
+
if (r4.kind === "rewritten") {
|
|
885
|
+
receipts.push(receipt("SS-M4", "workers", ["workers", index], "rewritten"));
|
|
886
|
+
for (const field of r4.dropped)
|
|
887
|
+
receipts.push(receipt("SS-M4", "workers", at(field), "dropped-inert"));
|
|
767
888
|
}
|
|
768
889
|
});
|
|
769
890
|
if (next !== undefined)
|
package/dist/remote-exec.js
CHANGED
|
@@ -30,15 +30,22 @@ import { z } from "zod";
|
|
|
30
30
|
import { INTEGER } from "./integer.js";
|
|
31
31
|
import { buildQuotedIssue } from "./quoted-issue.js";
|
|
32
32
|
import { ENV_NAME, K8S_QUANTITY, NON_SECRET_URL, TCP_PORT, containsSecretToken, hasNul } from "./types.js";
|
|
33
|
-
import { retiredLaneWordRefusal } from "./execution-lanes.js";
|
|
33
|
+
import { isBlankCoordinate, retiredLaneWordRefusal } from "./execution-lanes.js";
|
|
34
34
|
import { ownValueOf } from "./own-key-read.js";
|
|
35
35
|
import { isPlainJsonObject } from "./plain-json-object.js";
|
|
36
36
|
/** The name of this contract's document in a `config.d` directory (`config.d/remote-exec.json`) —— the one spelling: the local reader /
|
|
37
37
|
* writer, the directory migration and the command line name the document by it. It is not a config domain (not in `DOMAIN_SCHEMAS`). */
|
|
38
38
|
export const REMOTE_EXEC_DOCUMENT = "remote-exec";
|
|
39
|
-
/** A non-
|
|
40
|
-
*
|
|
41
|
-
|
|
39
|
+
/** A non-blank exec-bound string (an image / path / shell / arg the adapter feeds to spawn()/fs) — THE one grammar of every string field in this file that
|
|
40
|
+
* is not an env-NAME / quantity / URL. Blank = empty or whitespace-only, asked of THE lane-coordinate blank judge (`isBlankCoordinate`, the one the
|
|
41
|
+
* write gate asks of every required coordinate): the same deployment statement ("which image", "which namespace") is refused under one reading on
|
|
42
|
+
* both surfaces, and an empty string and a whitespace-only one are one refusal with one sentence (no second reading of "empty"). The judge only
|
|
43
|
+
* judges — an accepted value is carried verbatim, never trimmed. Rejects a NUL byte uniformly — a NUL validates here but makes Node throw at
|
|
44
|
+
* spawn/boot (fail-OPEN → runtime crash). R20. */
|
|
45
|
+
const execStr = z
|
|
46
|
+
.string()
|
|
47
|
+
.refine((s) => !isBlankCoordinate(s), buildQuotedIssue `must not be empty or whitespace-only — give it a real value (an optional key can simply be left out)`)
|
|
48
|
+
.refine((s) => !hasNul(s), buildQuotedIssue `must not contain a NUL byte`);
|
|
42
49
|
/** Container knobs of the `local-docker` lane. `network` defaults to `none` at the adapter (the safe posture for
|
|
43
50
|
* untrusted code — see the dual-mode design note). */
|
|
44
51
|
const DOCKER_FIELDS = {
|
package/dist/types.d.ts
CHANGED
|
@@ -506,11 +506,11 @@ export type SystemsConfig = z.infer<typeof SystemsConfig>;
|
|
|
506
506
|
export declare const SystemStatusEntry: z.ZodObject<{
|
|
507
507
|
name: z.ZodString;
|
|
508
508
|
phase: z.ZodEnum<{
|
|
509
|
+
refused: "refused";
|
|
509
510
|
bootstrap: "bootstrap";
|
|
510
511
|
steady: "steady";
|
|
511
512
|
rotating: "rotating";
|
|
512
513
|
grace: "grace";
|
|
513
|
-
refused: "refused";
|
|
514
514
|
"lkg-hold": "lkg-hold";
|
|
515
515
|
}>;
|
|
516
516
|
fp: z.ZodString;
|
|
@@ -1427,7 +1427,8 @@ export declare const WorkerSpec: z.ZodObject<{
|
|
|
1427
1427
|
}, z.core.$strip>>;
|
|
1428
1428
|
runtime: z.ZodObject<{
|
|
1429
1429
|
execution: z.ZodOptional<z.ZodObject<{
|
|
1430
|
-
lane: z.
|
|
1430
|
+
lane: z.ZodOptional<z.ZodNever>;
|
|
1431
|
+
lanes: z.ZodArray<z.ZodEnum<{
|
|
1431
1432
|
host: "host";
|
|
1432
1433
|
e2b: "e2b";
|
|
1433
1434
|
k8s: "k8s";
|
|
@@ -1435,7 +1436,7 @@ export declare const WorkerSpec: z.ZodObject<{
|
|
|
1435
1436
|
adb: "adb";
|
|
1436
1437
|
"local-docker": "local-docker";
|
|
1437
1438
|
device: "device";
|
|
1438
|
-
}
|
|
1439
|
+
}>>;
|
|
1439
1440
|
coords: z.ZodPrefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1440
1441
|
secretRefs: z.ZodPrefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1441
1442
|
}, z.core.$strip>>;
|
|
@@ -1458,8 +1459,8 @@ export declare const WorkerSpec: z.ZodObject<{
|
|
|
1458
1459
|
}>>;
|
|
1459
1460
|
backend: z.ZodDefault<z.ZodEnum<{
|
|
1460
1461
|
file: "file";
|
|
1462
|
+
mysql: "mysql";
|
|
1461
1463
|
pg: "pg";
|
|
1462
|
-
tidb: "tidb";
|
|
1463
1464
|
}>>;
|
|
1464
1465
|
}, z.core.$strip>>;
|
|
1465
1466
|
db: z.ZodOptional<z.ZodObject<{
|
|
@@ -1595,7 +1596,8 @@ export declare const WorkersConfig: z.ZodObject<{
|
|
|
1595
1596
|
}, z.core.$strip>>;
|
|
1596
1597
|
runtime: z.ZodObject<{
|
|
1597
1598
|
execution: z.ZodOptional<z.ZodObject<{
|
|
1598
|
-
lane: z.
|
|
1599
|
+
lane: z.ZodOptional<z.ZodNever>;
|
|
1600
|
+
lanes: z.ZodArray<z.ZodEnum<{
|
|
1599
1601
|
host: "host";
|
|
1600
1602
|
e2b: "e2b";
|
|
1601
1603
|
k8s: "k8s";
|
|
@@ -1603,7 +1605,7 @@ export declare const WorkersConfig: z.ZodObject<{
|
|
|
1603
1605
|
adb: "adb";
|
|
1604
1606
|
"local-docker": "local-docker";
|
|
1605
1607
|
device: "device";
|
|
1606
|
-
}
|
|
1608
|
+
}>>;
|
|
1607
1609
|
coords: z.ZodPrefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1608
1610
|
secretRefs: z.ZodPrefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1609
1611
|
}, z.core.$strip>>;
|
|
@@ -1626,8 +1628,8 @@ export declare const WorkersConfig: z.ZodObject<{
|
|
|
1626
1628
|
}>>;
|
|
1627
1629
|
backend: z.ZodDefault<z.ZodEnum<{
|
|
1628
1630
|
file: "file";
|
|
1631
|
+
mysql: "mysql";
|
|
1629
1632
|
pg: "pg";
|
|
1630
|
-
tidb: "tidb";
|
|
1631
1633
|
}>>;
|
|
1632
1634
|
}, z.core.$strip>>;
|
|
1633
1635
|
db: z.ZodOptional<z.ZodObject<{
|
|
@@ -1787,7 +1789,8 @@ export declare const HostStatus: z.ZodObject<{
|
|
|
1787
1789
|
}, z.core.$strip>>;
|
|
1788
1790
|
}, z.core.$strip>>;
|
|
1789
1791
|
}, z.core.$strip>;
|
|
1790
|
-
ts: z.
|
|
1792
|
+
ts: z.ZodISODateTime;
|
|
1793
|
+
heartbeatMs: z.ZodOptional<z.ZodNumber>;
|
|
1791
1794
|
}, z.core.$strip>;
|
|
1792
1795
|
export type HostStatus = z.infer<typeof HostStatus>;
|
|
1793
1796
|
export declare const SkillSpec: z.ZodObject<{
|
|
@@ -2673,7 +2676,8 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
2673
2676
|
}, z.core.$strip>>;
|
|
2674
2677
|
runtime: z.ZodObject<{
|
|
2675
2678
|
execution: z.ZodOptional<z.ZodObject<{
|
|
2676
|
-
lane: z.
|
|
2679
|
+
lane: z.ZodOptional<z.ZodNever>;
|
|
2680
|
+
lanes: z.ZodArray<z.ZodEnum<{
|
|
2677
2681
|
host: "host";
|
|
2678
2682
|
e2b: "e2b";
|
|
2679
2683
|
k8s: "k8s";
|
|
@@ -2681,7 +2685,7 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
2681
2685
|
adb: "adb";
|
|
2682
2686
|
"local-docker": "local-docker";
|
|
2683
2687
|
device: "device";
|
|
2684
|
-
}
|
|
2688
|
+
}>>;
|
|
2685
2689
|
coords: z.ZodPrefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
2686
2690
|
secretRefs: z.ZodPrefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
2687
2691
|
}, z.core.$strip>>;
|
|
@@ -2704,8 +2708,8 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
2704
2708
|
}>>;
|
|
2705
2709
|
backend: z.ZodDefault<z.ZodEnum<{
|
|
2706
2710
|
file: "file";
|
|
2711
|
+
mysql: "mysql";
|
|
2707
2712
|
pg: "pg";
|
|
2708
|
-
tidb: "tidb";
|
|
2709
2713
|
}>>;
|
|
2710
2714
|
}, z.core.$strip>>;
|
|
2711
2715
|
db: z.ZodOptional<z.ZodObject<{
|