@sema-agent/settings-schema 6.4.0 → 7.0.1

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 (95) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/README.md +53 -2
  3. package/bin/settings-schema.mjs +22 -0
  4. package/conformance/VERSION +1 -1
  5. package/conformance/cases/c50-s784-lane-host-legal/expected.json +102 -0
  6. package/conformance/cases/c50-s784-lane-host-legal/input.json +29 -0
  7. package/conformance/cases/c50-s784-lane-host-legal/meta.json +5 -0
  8. package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +104 -0
  9. package/conformance/cases/c51-s784-lane-e2b-legal/input.json +32 -0
  10. package/conformance/cases/c51-s784-lane-e2b-legal/meta.json +5 -0
  11. package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +106 -0
  12. package/conformance/cases/c52-s784-lane-k8s-legal/input.json +34 -0
  13. package/conformance/cases/c52-s784-lane-k8s-legal/meta.json +5 -0
  14. package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +106 -0
  15. package/conformance/cases/c53-s784-lane-ssh-legal/input.json +34 -0
  16. package/conformance/cases/c53-s784-lane-ssh-legal/meta.json +5 -0
  17. package/conformance/cases/c54-s784-lane-adb-legal/expected.json +102 -0
  18. package/conformance/cases/c54-s784-lane-adb-legal/input.json +29 -0
  19. package/conformance/cases/c54-s784-lane-adb-legal/meta.json +5 -0
  20. package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +103 -0
  21. package/conformance/cases/c55-s784-lane-local-docker-legal/input.json +30 -0
  22. package/conformance/cases/c55-s784-lane-local-docker-legal/meta.json +5 -0
  23. package/conformance/cases/c56-s784-lane-device-legal/expected.json +109 -0
  24. package/conformance/cases/c56-s784-lane-device-legal/input.json +36 -0
  25. package/conformance/cases/c56-s784-lane-device-legal/meta.json +5 -0
  26. package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +83 -0
  27. package/conformance/cases/c57-s784-g1-enabled-no-lane/input.json +23 -0
  28. package/conformance/cases/c57-s784-g1-enabled-no-lane/meta.json +5 -0
  29. package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +83 -0
  30. package/conformance/cases/c58-s784-g2-multitenant-host/input.json +34 -0
  31. package/conformance/cases/c58-s784-g2-multitenant-host/meta.json +5 -0
  32. package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +83 -0
  33. package/conformance/cases/c59-s784-g3-k8s-missing-image/input.json +26 -0
  34. package/conformance/cases/c59-s784-g3-k8s-missing-image/meta.json +5 -0
  35. package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +83 -0
  36. package/conformance/cases/c60-s784-g3-e2b-missing-key/input.json +26 -0
  37. package/conformance/cases/c60-s784-g3-e2b-missing-key/meta.json +5 -0
  38. package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +83 -0
  39. package/conformance/cases/c61-s784-g3-other-lane-key/input.json +30 -0
  40. package/conformance/cases/c61-s784-g3-other-lane-key/meta.json +5 -0
  41. package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +83 -0
  42. package/conformance/cases/c62-s784-g7a-session-tidb/input.json +27 -0
  43. package/conformance/cases/c62-s784-g7a-session-tidb/meta.json +5 -0
  44. package/conformance/cases/c63-s784-host-two-replicas/expected.json +83 -0
  45. package/conformance/cases/c63-s784-host-two-replicas/input.json +27 -0
  46. package/conformance/cases/c63-s784-host-two-replicas/meta.json +5 -0
  47. package/conformance/cases/c64-s784-multitenant-local-store/expected.json +83 -0
  48. package/conformance/cases/c64-s784-multitenant-local-store/input.json +30 -0
  49. package/conformance/cases/c64-s784-multitenant-local-store/meta.json +5 -0
  50. package/conformance/cases/c65-s784-pg-without-db/expected.json +83 -0
  51. package/conformance/cases/c65-s784-pg-without-db/input.json +27 -0
  52. package/conformance/cases/c65-s784-pg-without-db/meta.json +5 -0
  53. package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +83 -0
  54. package/conformance/cases/c66-s784-memory-backend-mismatch/input.json +36 -0
  55. package/conformance/cases/c66-s784-memory-backend-mismatch/meta.json +5 -0
  56. package/conformance/cases/c67-s784-retired-field-loud/expected.json +83 -0
  57. package/conformance/cases/c67-s784-retired-field-loud/input.json +22 -0
  58. package/conformance/cases/c67-s784-retired-field-loud/meta.json +5 -0
  59. package/conformance/cases/c68-s784-device-local-store/expected.json +83 -0
  60. package/conformance/cases/c68-s784-device-local-store/input.json +26 -0
  61. package/conformance/cases/c68-s784-device-local-store/meta.json +5 -0
  62. package/conformance/cases/cli04-env-value-with-newline/expected.json +13 -2
  63. package/conformance/cases/cli04-env-value-with-newline/input.json +8 -1
  64. package/conformance/cases/cli04-env-value-with-newline/meta.json +1 -1
  65. package/conformance/migrate-workers/m01-g5-k8s-env-lifted/expected.json +49 -0
  66. package/conformance/migrate-workers/m01-g5-k8s-env-lifted/input.json +26 -0
  67. package/conformance/migrate-workers/m01-g5-k8s-env-lifted/meta.json +4 -0
  68. package/conformance/migrate-workers/m02-g5-idempotent/expected.json +38 -0
  69. package/conformance/migrate-workers/m02-g5-idempotent/input.json +33 -0
  70. package/conformance/migrate-workers/m02-g5-idempotent/meta.json +5 -0
  71. package/conformance/migrate-workers/m03-g5-refused-no-lane/expected.json +71 -0
  72. package/conformance/migrate-workers/m03-g5-refused-no-lane/input.json +42 -0
  73. package/conformance/migrate-workers/m03-g5-refused-no-lane/meta.json +4 -0
  74. package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/expected.json +52 -0
  75. package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/input.json +30 -0
  76. package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/meta.json +4 -0
  77. package/dist/cli.d.ts +8 -0
  78. package/dist/cli.js +110 -0
  79. package/dist/execution-lane-index.d.ts +38 -0
  80. package/dist/execution-lane-index.js +46 -0
  81. package/dist/execution-lanes.d.ts +64 -0
  82. package/dist/execution-lanes.js +152 -0
  83. package/dist/file-edit.js +26 -21
  84. package/dist/hooks.d.ts +4 -4
  85. package/dist/index.d.ts +2 -0
  86. package/dist/index.js +2 -0
  87. package/dist/migrate-workers.d.ts +63 -0
  88. package/dist/migrate-workers.js +327 -0
  89. package/dist/remote-exec.d.ts +23 -64
  90. package/dist/remote-exec.js +20 -71
  91. package/dist/secret-refs.d.ts +7 -1
  92. package/dist/secret-refs.js +9 -3
  93. package/dist/types.d.ts +1356 -455
  94. package/dist/types.js +267 -63
  95. package/package.json +9 -1
package/dist/cli.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * `settings-schema` 命令行的核心(7.0.0)—— 子命令只有一个:`migrate-workers <workers.json> [--check]`,即
3
+ * {@link migrateWorkersDomain} 的文件形(文件后端 / 离线用;中心在它的 workers 读口直接调同一只函数)。
4
+ *
5
+ * 本模块不碰 `node:fs`:读写文件由入口 `bin/settings-schema.mjs` 注入(那是本包唯一用 fs 写文件的地方),所以测试可以直接驱动它。
6
+ * 不进主桶 —— 它是 bin 的实现,不是契约面。
7
+ *
8
+ * 退出码:0 = 已写回 / 无事可做;1 = 有 worker 迁不了(refused)或迁出的文档整域读不进 7.0.0,**一字不写**;2 = 用法 / 读文件 /
9
+ * JSON 错(此时 stdout 为空);3 = `--check` 且会有改动(不写)。
10
+ *
11
+ * ## 报告形(7.0.1:让「没写」在机读上不可能被读成「写了」)
12
+ * 退出码 0 / 1 / 3 时 stdout 恰两行:
13
+ * · 第 1 行 = **唯一结论行** `outcome=<refused|applied|noop|pending> written=<true|false>`;
14
+ * · 第 2 行 = JSON 报告(`event: "workers_domain_migrated"`,只点名字段、不带值),同带 `outcome` / `written`。
15
+ * `written` 不是独立字段:由 `outcome` 派生({@link writtenOf};只有 `applied` 写盘)。**键名带时态**:`written=true` 才有 `changed` /
16
+ * `dropped`;`written=false`(refused / noop / pending)时是 `wouldChange` / `wouldDrop` —— 读旧字段 `changed` 的脚本在没写的时候
17
+ * 读不到值,而不是读到一份「已迁」名单。结论行在**写盘成功之后**才出(写盘抛错 ⇒ 没有 `outcome=applied`)。
18
+ */
19
+ import { formatConfigPath, sanitizeZodError } from "./config-fns.js";
20
+ import { MIGRATE_WORKERS_COMMAND } from "./execution-lane-index.js";
21
+ import { migrateWorkersDomain } from "./migrate-workers.js";
22
+ import { DOMAIN_SCHEMAS } from "./types.js";
23
+ const USAGE = [
24
+ `usage: ${MIGRATE_WORKERS_COMMAND} <workers.json> [--check]`,
25
+ " One-time 7.0.0 migration of a workers domain document ({ \"workers\": [...] }): env.REMOTE_EXEC + lane keys →",
26
+ " runtime.execution, sessionBackend / coords.tidb* / runtime.db / tidbPasswordEnv → runtime.store. Writes the file in",
27
+ " place only when every worker migrates; --check only reports (exit 3 if the file would change).",
28
+ " stdout (exit 0 / 1 / 3): line 1 = outcome=<refused|applied|noop|pending> written=<true|false>, line 2 = the JSON report",
29
+ " (changed / dropped only when written=true; otherwise wouldChange / wouldDrop).",
30
+ ].join("\n");
31
+ /** 这个结论写没写盘 —— 只有 `applied` 写。`written` 从这里派生,不是报告里一个可以和 `outcome` 各说各话的独立字段。 */
32
+ function writtenOf(outcome) {
33
+ switch (outcome) {
34
+ case "applied":
35
+ return true;
36
+ case "refused":
37
+ case "noop":
38
+ case "pending":
39
+ return false;
40
+ default: {
41
+ const unhandled = outcome;
42
+ throw new Error(`settings-schema: unhandled outcome ${String(unhandled)}`);
43
+ }
44
+ }
45
+ }
46
+ /** 一次发出报告:结论行 + JSON 行(一次 `stdout` 调用)。键名带时态:写了 ⇒ `changed` / `dropped`,没写 ⇒ `wouldChange` / `wouldDrop`。 */
47
+ function reportOf(outcome, file, m) {
48
+ const written = writtenOf(outcome);
49
+ const dropped = m.dropped.map((d) => ({ name: d.name, field: formatConfigPath(d.field) }));
50
+ const json = JSON.stringify({
51
+ event: "workers_domain_migrated",
52
+ outcome,
53
+ written,
54
+ file,
55
+ ...(written ? { changed: m.changed, dropped } : { wouldChange: m.changed, wouldDrop: dropped }),
56
+ refused: m.refused.map((r) => ({ name: r.name, field: formatConfigPath(r.field), reason: r.reason })),
57
+ });
58
+ return `outcome=${outcome} written=${written}\n${json}\n`;
59
+ }
60
+ export async function runSettingsSchemaCli(argv, io) {
61
+ const [command, ...rest] = argv;
62
+ const check = rest.includes("--check");
63
+ const files = rest.filter((a) => a !== "--check");
64
+ const file = files[0];
65
+ if (command !== "migrate-workers" || files.length !== 1 || file === undefined || file.startsWith("-")) {
66
+ io.stderr(`${USAGE}\n`);
67
+ return 2;
68
+ }
69
+ let text;
70
+ try {
71
+ text = await io.readFile(file);
72
+ }
73
+ catch (err) {
74
+ io.stderr(`settings-schema: cannot read ${file}: ${err instanceof Error ? err.message : String(err)}\n`);
75
+ return 2;
76
+ }
77
+ let doc;
78
+ try {
79
+ doc = JSON.parse(text);
80
+ }
81
+ catch {
82
+ io.stderr(`settings-schema: ${file} is not valid JSON\n`);
83
+ return 2;
84
+ }
85
+ const m = migrateWorkersDomain(doc);
86
+ if (m.refused.length > 0) {
87
+ io.stdout(reportOf("refused", file, m));
88
+ io.stderr(`settings-schema: ${m.refused.length} worker(s) cannot be migrated (see "refused") — fix them by hand and re-run; nothing was written\n`);
89
+ return 1;
90
+ }
91
+ if (m.changed.length === 0) {
92
+ io.stdout(reportOf("noop", file, m));
93
+ return 0;
94
+ }
95
+ if (check) {
96
+ io.stdout(reportOf("pending", file, m));
97
+ return 3;
98
+ }
99
+ // 整域再判一次(域层规则:名字 / 路由唯一 …)—— 读不进 7.0.0 的文档不落盘。
100
+ const whole = DOMAIN_SCHEMAS.workers.safeParse(m.doc);
101
+ if (!whole.success) {
102
+ const issues = sanitizeZodError(whole.error).issues.map((i) => `${formatConfigPath(i.path)}: ${i.message}`);
103
+ io.stdout(reportOf("refused", file, m));
104
+ io.stderr(`settings-schema: the migrated document does not read as a 7.0.0 workers domain — ${issues.join("; ")}; nothing was written\n`);
105
+ return 1;
106
+ }
107
+ await io.writeFileAtomic(file, `${JSON.stringify(m.doc, null, 2)}\n`);
108
+ io.stdout(reportOf("applied", file, m)); // 写盘成功之后才报 applied
109
+ return 0;
110
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 包内私有:车道表({@link EXECUTION_LANES})的两只**派生索引** + 「迁移能搬哪些键」的**一份**键集与指路句,
3
+ * 供 `types.ts`(写闸)与 `migrate-workers.ts`(一次性迁移)同读。
4
+ * 不进主桶、不进 `package.json` 的 `exports`(与 `secret-scan.ts` 同一姿势)—— 消费方只拿表本身,索引是本包的实现细节。
5
+ * 索引只从表派生,不另存任何词:表改了,这里跟着变。
6
+ *
7
+ * ## 迁移指路(7.0.1)
8
+ * 拒因要不要带迁移指路只有一条规则:**这个键「旧形 ⇒ 迁移能搬」就带,搬不了的保留名(`PORT` / `MODEL_*` …)不带**。
9
+ * 「迁移能搬」= 迁移自己识别的键集(车道键 {@link isLaneEnvKey} ∪ 店键 {@link STORE_ENV_KEYS}),写闸读的是**同一只谓词**
10
+ * ({@link isMigratableEnvKey}),不是第二张手抄的表 —— 迁移多认一个键而写闸没跟,或反过来,不会静默分叉
11
+ * (`test/migration-signpost-7.0.1.test.ts` 用真跑迁移的读数逐个保留名对拍)。
12
+ */
13
+ import { type ExecutionLane, type ExecutionLaneRow } from "./execution-lanes.js";
14
+ /** 一条车道的表行。词表与表行由 `execution-lanes.ts` 的 `satisfies Record<ExecutionLane, …>` 一一对应 ——
15
+ * 找不到 = 本包自身的不变量坏了(不是输入错),所以抛。 */
16
+ export declare function laneRowOf(lane: ExecutionLane): ExecutionLaneRow;
17
+ /** 一个车道 env 名归谁:哪条车道、密钥栏还是坐标栏、必需还是可选。 */
18
+ export interface LaneEnvOwner {
19
+ readonly lane: ExecutionLane;
20
+ readonly secret: boolean;
21
+ readonly required: boolean;
22
+ }
23
+ /** 车道 env 名 → 归属。四栏两两不交、跨车道不交(`test/execution-lanes-7.0.0.test.ts` 钉),所以每个名恰好一个归属。 */
24
+ export declare const LANE_ENV_OWNER: ReadonlyMap<string, LaneEnvOwner>;
25
+ /** 一个车道 env 名在 WorkerSpec 里的**唯一写位**(保留键的拒因指路用它)。 */
26
+ export declare function laneEnvSeat(name: string, owner: LaneEnvOwner): string;
27
+ /** 车道块的两个特殊键(不在车道表里,但归车道块:迁移把 `REMOTE_EXEC` 搬成 `runtime.execution.lane`、`REQUIRE_EXECUTION_LANE` 丢弃)。 */
28
+ export declare const LANE_WORD_KEYS: readonly ["REMOTE_EXEC", "REQUIRE_EXECUTION_LANE"];
29
+ /** 渲染器以前不渲、运维只能写在 env 逃生口里的店键(7.0.0 起归 `runtime.store`;迁移搬进 `store`)。 */
30
+ export declare const STORE_ENV_KEYS: readonly ["DB_BACKEND", "MEMORY_ENGINE", "MEMORY_ENGINE_BACKEND"];
31
+ /** 一个名字是不是车道块的键:车道词两键,或车道表里的任一车道 env 名。 */
32
+ export declare function isLaneEnvKey(name: string): boolean;
33
+ /** 一个名字是不是迁移会处理的 6.x env 键(搬进结构块 / 丢弃 / 点名它拒):车道块的键 ∪ 店键。写闸拒因带不带迁移指路只看它。 */
34
+ export declare function isMigratableEnvKey(name: string): boolean;
35
+ /** 迁移命令的唯一拼写(design/327 §3.4 原句):`npx` 全名形 —— 没装本包的运维粘贴即可跑;裸 bin 名 `settings-schema …` 在没装本包的机器上是 `command not found`。 */
36
+ export declare const MIGRATE_WORKERS_COMMAND: "npx @sema-agent/settings-schema migrate-workers";
37
+ /** 拒因里的迁移指路 —— **一句话、一个常量**:保留名闸(`env` / `secretEnv` 里的 {@link isMigratableEnvKey} 键)与删除项预处理(七个被删旧键)引用同一个。 */
38
+ export declare const MIGRATION_SIGNPOST: string;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 包内私有:车道表({@link EXECUTION_LANES})的两只**派生索引** + 「迁移能搬哪些键」的**一份**键集与指路句,
3
+ * 供 `types.ts`(写闸)与 `migrate-workers.ts`(一次性迁移)同读。
4
+ * 不进主桶、不进 `package.json` 的 `exports`(与 `secret-scan.ts` 同一姿势)—— 消费方只拿表本身,索引是本包的实现细节。
5
+ * 索引只从表派生,不另存任何词:表改了,这里跟着变。
6
+ *
7
+ * ## 迁移指路(7.0.1)
8
+ * 拒因要不要带迁移指路只有一条规则:**这个键「旧形 ⇒ 迁移能搬」就带,搬不了的保留名(`PORT` / `MODEL_*` …)不带**。
9
+ * 「迁移能搬」= 迁移自己识别的键集(车道键 {@link isLaneEnvKey} ∪ 店键 {@link STORE_ENV_KEYS}),写闸读的是**同一只谓词**
10
+ * ({@link isMigratableEnvKey}),不是第二张手抄的表 —— 迁移多认一个键而写闸没跟,或反过来,不会静默分叉
11
+ * (`test/migration-signpost-7.0.1.test.ts` 用真跑迁移的读数逐个保留名对拍)。
12
+ */
13
+ import { EXECUTION_LANES } from "./execution-lanes.js";
14
+ /** 一条车道的表行。词表与表行由 `execution-lanes.ts` 的 `satisfies Record<ExecutionLane, …>` 一一对应 ——
15
+ * 找不到 = 本包自身的不变量坏了(不是输入错),所以抛。 */
16
+ export function laneRowOf(lane) {
17
+ const row = EXECUTION_LANES.find((r) => r.lane === lane);
18
+ if (row === undefined)
19
+ throw new Error(`execution lane table has no row for "${lane}" (package invariant broken)`);
20
+ return row;
21
+ }
22
+ /** 车道 env 名 → 归属。四栏两两不交、跨车道不交(`test/execution-lanes-7.0.0.test.ts` 钉),所以每个名恰好一个归属。 */
23
+ export const LANE_ENV_OWNER = new Map(EXECUTION_LANES.flatMap((r) => {
24
+ const own = (names, secret, required) => names.map((k) => [k, { lane: r.lane, secret, required }]);
25
+ return [...own(r.requiredEnv, false, true), ...own(r.optionalEnv, false, false), ...own(r.requiredSecretEnv, true, true), ...own(r.optionalSecretEnv, true, false)];
26
+ }));
27
+ /** 一个车道 env 名在 WorkerSpec 里的**唯一写位**(保留键的拒因指路用它)。 */
28
+ export function laneEnvSeat(name, owner) {
29
+ return `runtime.execution.${owner.secret ? "secretRefs" : "coords"}.${name}`;
30
+ }
31
+ /** 车道块的两个特殊键(不在车道表里,但归车道块:迁移把 `REMOTE_EXEC` 搬成 `runtime.execution.lane`、`REQUIRE_EXECUTION_LANE` 丢弃)。 */
32
+ export const LANE_WORD_KEYS = ["REMOTE_EXEC", "REQUIRE_EXECUTION_LANE"];
33
+ /** 渲染器以前不渲、运维只能写在 env 逃生口里的店键(7.0.0 起归 `runtime.store`;迁移搬进 `store`)。 */
34
+ export const STORE_ENV_KEYS = ["DB_BACKEND", "MEMORY_ENGINE", "MEMORY_ENGINE_BACKEND"];
35
+ /** 一个名字是不是车道块的键:车道词两键,或车道表里的任一车道 env 名。 */
36
+ export function isLaneEnvKey(name) {
37
+ return LANE_WORD_KEYS.some((k) => k === name) || LANE_ENV_OWNER.has(name);
38
+ }
39
+ /** 一个名字是不是迁移会处理的 6.x env 键(搬进结构块 / 丢弃 / 点名它拒):车道块的键 ∪ 店键。写闸拒因带不带迁移指路只看它。 */
40
+ export function isMigratableEnvKey(name) {
41
+ return isLaneEnvKey(name) || STORE_ENV_KEYS.some((k) => k === name);
42
+ }
43
+ /** 迁移命令的唯一拼写(design/327 §3.4 原句):`npx` 全名形 —— 没装本包的运维粘贴即可跑;裸 bin 名 `settings-schema …` 在没装本包的机器上是 `command not found`。 */
44
+ export const MIGRATE_WORKERS_COMMAND = "npx @sema-agent/settings-schema migrate-workers";
45
+ /** 拒因里的迁移指路 —— **一句话、一个常量**:保留名闸(`env` / `secretEnv` 里的 {@link isMigratableEnvKey} 键)与删除项预处理(七个被删旧键)引用同一个。 */
46
+ export const MIGRATION_SIGNPOST = `lift a stored 6.x spec once with migrateWorkersDomain(doc) (on the raw document, ahead of parseDomain) or \`${MIGRATE_WORKERS_COMMAND} <file>\``;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * 执行车道 + 存储后端的**词表正本**(7.0.0,M-R2 WorkerSpec 结构块)。纯数据常量,零函数、零依赖(叶模块:
3
+ * 本文件不 import 任何东西,`types.ts` 反过来引它)。
4
+ *
5
+ * ## 为什么在这里
6
+ * 车道词、每条车道读哪些 env、哪条车道分租户、存储后端词 —— 这几张表以前在四处各抄一份(引擎的能力表、
7
+ * 部署面的车道清单、中心表单、本包 `RemoteExecSpec`),任何一处改了别处都不红。从本版起**只有这一份**:
8
+ * · 中心写闸 = `WorkerSpec.safeParse`(本包 schema 读这张表判 `runtime.execution` / `runtime.store`);
9
+ * · 中心渲染器按这张表把结构块渲成 env(唯一写者);
10
+ * · 引擎提货时从这张表派生自己的车道词表 / 必需键表 / 租户分离位(引擎只留运行期事实,按导入联合类型穷举);
11
+ * · 部署面车道清单由这张表生成。
12
+ * 写闸与起服读同一张表 ⇒ 不会分叉。
13
+ *
14
+ * ## 行内容的出处(逐行抄自引擎真码,坐标 = server 7.112.0 树)
15
+ * · 车道词与次序:`src/execution-lane-caps.ts` `EXECUTION_LANE_WORDS`(:335)去掉 `in-process` =
16
+ * `REMOTE_EXEC_PROVIDER_WORDS`(:366)。次序照抄,于是引擎改派生之后,拒启句里的 `valid:` 列表与出路列表逐字不变。
17
+ * · `tenantSeparation`:同文件 `executionLaneCaps` 每臂的 `tenantSeparation` 位(:209 host / :230 e2b / :249 k8s /
18
+ * :269 ssh / :288 adb / :308 local-docker / :327 device)。
19
+ * · `requiredEnv ∪ requiredSecretEnv`:`src/config.ts` `REQUIRED_ENVS_BY_LANE`(:1054)逐字;按 `src/config-catalog.ts`
20
+ * 该键那一行的值类型分栏 —— 类型 `secret` / `opaque`(目录自己「绝不回显值」的两类)进 `*SecretEnv`,其余进 `*Env`。
21
+ * · `optionalEnv ∪ optionalSecretEnv`:`src/config.ts` `parseOrchestrationDomain` 的 `REMOTE_EXEC` 三元链里**该车道那一臂**
22
+ * 读的 env 名(:3925 e2b / :3957 k8s / :3991 ssh / :4006 adb / :4014 host / :4026 local-docker / :4055 device;
23
+ * 臂内调用的解析器 `parseSshHostKeyEnv` :1521、`parseDevicePreAuthPerRemoteEnv` :871、`parseDeviceEnrollTokenTtlEnv` :857
24
+ * 读的名也算),去掉必需键,同样按目录值类型分栏。**不收**别的域也读的名:k8s 臂里的 `MINIO_ENDPOINT` /
25
+ * `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY` / `MINIO_REGION` 同时是存储域对象存储的键(`src/config.ts` :1848 / :1872),
26
+ * 收进车道栏会把它们保留成「k8s 专属」,别的车道就再也设不了快照对象存储。也**不收**臂外的适配器调参键
27
+ * (`HOST_BG_*` / `E2B_BG_*` / `K8S_BG_*` / `HOST_SHELL_PATH` …):它们不是车道坐标,照旧走 `env` 逃生口。
28
+ *
29
+ * ## 不在表里的词
30
+ * · `in-process`:引擎内部「没有 remoteExec」这个状态的名字,不是可声明的车道 —— 显式「无手」的 worker 在模型要用工具时
31
+ * 仍回 completed + 零工具,就是那个病换了个合法名字。要无手 worker 先另立设计、定用户面行为再给词。
32
+ * · `remote-docker`:引擎有一只没接线的适配器工厂,但没有任何读点能把这个词变成一条在跑的车道
33
+ * (`REMOTE_EXEC=remote-docker` 起服即拒;远端 docker 守护进程今天的真形 = `local-docker` + `DOCKER_HOST`)。
34
+ */
35
+ /** 可声明的执行车道闭集(= 引擎 `REMOTE_EXEC` 收的词,逐字同序)。 */
36
+ export declare const EXECUTION_LANE_WORDS: readonly ["host", "e2b", "k8s", "ssh", "adb", "local-docker", "device"];
37
+ export type ExecutionLane = (typeof EXECUTION_LANE_WORDS)[number];
38
+ /** 车道分不分租户(引擎能力表的 `tenantSeparation` 位,两档,没有「不表态」)。
39
+ * `supported` = 每个租户的命令落在各自的目标上;`single-user-only` = 所有任务落同一个共享目标 ⇒ 只许单用户 worker。 */
40
+ export declare const TENANT_SEPARATION_WORDS: readonly ["supported", "single-user-only"];
41
+ export type TenantSeparation = (typeof TENANT_SEPARATION_WORDS)[number];
42
+ /** 一条车道的表行。四栏 env 名两两不交、跨车道也不交(`test/execution-lanes-7.0.0.test.ts` 钉)。
43
+ * - `requiredEnv` / `optionalEnv`:非密坐标 —— 写在 `runtime.execution.coords`(字面值);
44
+ * - `requiredSecretEnv` / `optionalSecretEnv`:密钥 —— 写在 `runtime.execution.secretRefs`(服务变量名 → 宿主 env-NAME,绝不写值)。 */
45
+ export interface ExecutionLaneRow {
46
+ readonly lane: ExecutionLane;
47
+ readonly tenantSeparation: TenantSeparation;
48
+ readonly requiredEnv: readonly string[];
49
+ readonly requiredSecretEnv: readonly string[];
50
+ readonly optionalEnv: readonly string[];
51
+ readonly optionalSecretEnv: readonly string[];
52
+ }
53
+ /** 车道表(次序 = {@link EXECUTION_LANE_WORDS})。整张表深冻结 —— 它是唯一正本,消费方拿到的不许被改。 */
54
+ export declare const EXECUTION_LANES: readonly ExecutionLaneRow[];
55
+ /** 存储后端(→ `DB_BACKEND`)。= server `src/config-types.ts` `DB_BACKEND_WORDS`(:67)逐字同序。 */
56
+ export declare const STORE_BACKENDS: readonly ["mysql", "pg", "local", "memory"];
57
+ export type StoreBackend = (typeof STORE_BACKENDS)[number];
58
+ /** session 店(→ `SESSION_BACKEND`)。= server `src/config.ts` :1760 `enumEnv("SESSION_BACKEND", …)` 的词表逐字同序
59
+ * (`tidb` 不在集内:引擎见之拒启)。 */
60
+ export declare const SESSION_BACKENDS: readonly ["memory", "mysql", "auto"];
61
+ export type SessionBackend = (typeof SESSION_BACKENDS)[number];
62
+ /** 记忆持久面方言(→ `MEMORY_ENGINE_BACKEND`)。= server `src/config.ts` :3436 闭集 / 目录 :468 `enumValues` 逐字同序。 */
63
+ export declare const MEMORY_ENGINE_BACKENDS: readonly ["file", "pg", "tidb"];
64
+ export type MemoryEngineBackend = (typeof MEMORY_ENGINE_BACKENDS)[number];
@@ -0,0 +1,152 @@
1
+ /**
2
+ * 执行车道 + 存储后端的**词表正本**(7.0.0,M-R2 WorkerSpec 结构块)。纯数据常量,零函数、零依赖(叶模块:
3
+ * 本文件不 import 任何东西,`types.ts` 反过来引它)。
4
+ *
5
+ * ## 为什么在这里
6
+ * 车道词、每条车道读哪些 env、哪条车道分租户、存储后端词 —— 这几张表以前在四处各抄一份(引擎的能力表、
7
+ * 部署面的车道清单、中心表单、本包 `RemoteExecSpec`),任何一处改了别处都不红。从本版起**只有这一份**:
8
+ * · 中心写闸 = `WorkerSpec.safeParse`(本包 schema 读这张表判 `runtime.execution` / `runtime.store`);
9
+ * · 中心渲染器按这张表把结构块渲成 env(唯一写者);
10
+ * · 引擎提货时从这张表派生自己的车道词表 / 必需键表 / 租户分离位(引擎只留运行期事实,按导入联合类型穷举);
11
+ * · 部署面车道清单由这张表生成。
12
+ * 写闸与起服读同一张表 ⇒ 不会分叉。
13
+ *
14
+ * ## 行内容的出处(逐行抄自引擎真码,坐标 = server 7.112.0 树)
15
+ * · 车道词与次序:`src/execution-lane-caps.ts` `EXECUTION_LANE_WORDS`(:335)去掉 `in-process` =
16
+ * `REMOTE_EXEC_PROVIDER_WORDS`(:366)。次序照抄,于是引擎改派生之后,拒启句里的 `valid:` 列表与出路列表逐字不变。
17
+ * · `tenantSeparation`:同文件 `executionLaneCaps` 每臂的 `tenantSeparation` 位(:209 host / :230 e2b / :249 k8s /
18
+ * :269 ssh / :288 adb / :308 local-docker / :327 device)。
19
+ * · `requiredEnv ∪ requiredSecretEnv`:`src/config.ts` `REQUIRED_ENVS_BY_LANE`(:1054)逐字;按 `src/config-catalog.ts`
20
+ * 该键那一行的值类型分栏 —— 类型 `secret` / `opaque`(目录自己「绝不回显值」的两类)进 `*SecretEnv`,其余进 `*Env`。
21
+ * · `optionalEnv ∪ optionalSecretEnv`:`src/config.ts` `parseOrchestrationDomain` 的 `REMOTE_EXEC` 三元链里**该车道那一臂**
22
+ * 读的 env 名(:3925 e2b / :3957 k8s / :3991 ssh / :4006 adb / :4014 host / :4026 local-docker / :4055 device;
23
+ * 臂内调用的解析器 `parseSshHostKeyEnv` :1521、`parseDevicePreAuthPerRemoteEnv` :871、`parseDeviceEnrollTokenTtlEnv` :857
24
+ * 读的名也算),去掉必需键,同样按目录值类型分栏。**不收**别的域也读的名:k8s 臂里的 `MINIO_ENDPOINT` /
25
+ * `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY` / `MINIO_REGION` 同时是存储域对象存储的键(`src/config.ts` :1848 / :1872),
26
+ * 收进车道栏会把它们保留成「k8s 专属」,别的车道就再也设不了快照对象存储。也**不收**臂外的适配器调参键
27
+ * (`HOST_BG_*` / `E2B_BG_*` / `K8S_BG_*` / `HOST_SHELL_PATH` …):它们不是车道坐标,照旧走 `env` 逃生口。
28
+ *
29
+ * ## 不在表里的词
30
+ * · `in-process`:引擎内部「没有 remoteExec」这个状态的名字,不是可声明的车道 —— 显式「无手」的 worker 在模型要用工具时
31
+ * 仍回 completed + 零工具,就是那个病换了个合法名字。要无手 worker 先另立设计、定用户面行为再给词。
32
+ * · `remote-docker`:引擎有一只没接线的适配器工厂,但没有任何读点能把这个词变成一条在跑的车道
33
+ * (`REMOTE_EXEC=remote-docker` 起服即拒;远端 docker 守护进程今天的真形 = `local-docker` + `DOCKER_HOST`)。
34
+ */
35
+ /** 可声明的执行车道闭集(= 引擎 `REMOTE_EXEC` 收的词,逐字同序)。 */
36
+ export const EXECUTION_LANE_WORDS = ["host", "e2b", "k8s", "ssh", "adb", "local-docker", "device"];
37
+ /** 车道分不分租户(引擎能力表的 `tenantSeparation` 位,两档,没有「不表态」)。
38
+ * `supported` = 每个租户的命令落在各自的目标上;`single-user-only` = 所有任务落同一个共享目标 ⇒ 只许单用户 worker。 */
39
+ export const TENANT_SEPARATION_WORDS = ["supported", "single-user-only"];
40
+ /** 逐车道一行。`satisfies Record<ExecutionLane, …>` = 词表加词而这里不加行 ⇒ 编译红。 */
41
+ const LANE_ROWS = {
42
+ /** server `config.ts` :4014 臂(无必需键);`execution-lane-caps.ts` :209 `single-user-only`(所有任务落 worker 宿主)。 */
43
+ host: {
44
+ tenantSeparation: "single-user-only",
45
+ requiredEnv: [],
46
+ requiredSecretEnv: [],
47
+ optionalEnv: ["HOST_WORKSPACE_BASE", "HOST_COMMAND_TIMEOUT_MS"],
48
+ optionalSecretEnv: [],
49
+ },
50
+ /** server `config.ts` :3925 臂;`E2B_API_KEY` 目录 :351 类型 `secret`、`E2B_SANDBOX_ENV` :359 类型 `secret`;
51
+ * `execution-lane-caps.ts` :230 `supported`(每任务一只 microVM)。 */
52
+ e2b: {
53
+ tenantSeparation: "supported",
54
+ requiredEnv: [],
55
+ requiredSecretEnv: ["E2B_API_KEY"],
56
+ optionalEnv: ["E2B_TEMPLATE", "E2B_TIMEOUT_MS", "E2B_LIVENESS_MS", "E2B_ALLOW_NET", "E2B_MOUNT_PATH", "E2B_ORPHAN_RECLAIM"],
57
+ optionalSecretEnv: ["E2B_SANDBOX_ENV"],
58
+ },
59
+ /** server `config.ts` :3957 臂;`K8S_TOKEN` 目录 :412 类型 `secret`、`K8S_CA_CERT_B64` :403 类型 `opaque`;
60
+ * `execution-lane-caps.ts` :249 `supported`(每任务一只 pod)。`MINIO_*` 刻意不收(见文件头)。 */
61
+ k8s: {
62
+ tenantSeparation: "supported",
63
+ requiredEnv: ["K8S_SANDBOX_IMAGE"],
64
+ requiredSecretEnv: [],
65
+ optionalEnv: [
66
+ "K8S_API_URL",
67
+ "K8S_INSECURE_TLS",
68
+ "K8S_NAMESPACE",
69
+ "K8S_RUNTIME_CLASS",
70
+ "K8S_MOUNT_PATH",
71
+ "K8S_TIMEOUT_MS",
72
+ "K8S_MEMORY",
73
+ "K8S_CPU",
74
+ "WORKSPACE_SNAPSHOT_BUCKET",
75
+ "WORKSPACE_SNAPSHOT_PREFIX",
76
+ "WORKSPACE_SNAPSHOT_TTL_SEC",
77
+ ],
78
+ optionalSecretEnv: ["K8S_TOKEN", "K8S_CA_CERT_B64"],
79
+ },
80
+ /** server `config.ts` :3991 臂 + `parseSshHostKeyEnv` :1521;`SSH_PRIVATE_KEY` 目录 :687 类型 `secret`;
81
+ * `execution-lane-caps.ts` :269 `single-user-only`(一台跨任务共享的常驻真机)。 */
82
+ ssh: {
83
+ tenantSeparation: "single-user-only",
84
+ requiredEnv: ["SSH_HOST", "SSH_USER"],
85
+ requiredSecretEnv: ["SSH_PRIVATE_KEY"],
86
+ optionalEnv: ["SSH_PORT", "SSH_MOUNT_PATH", "SSH_HOST_FINGERPRINT", "SSH_KNOWN_HOSTS"],
87
+ optionalSecretEnv: [],
88
+ },
89
+ /** server `config.ts` :4006 臂;`execution-lane-caps.ts` :288 `single-user-only`(一台跨任务共享的设备)。 */
90
+ adb: {
91
+ tenantSeparation: "single-user-only",
92
+ requiredEnv: ["ADB_SERIAL"],
93
+ requiredSecretEnv: [],
94
+ optionalEnv: ["ADB_PATH", "ADB_MOUNT_PATH"],
95
+ optionalSecretEnv: [],
96
+ },
97
+ /** server `config.ts` :4026 臂;`DOCKER_SANDBOX_ENV` 目录 :345 类型 `secret`;`execution-lane-caps.ts` :308 `supported`
98
+ * (每任务一只容器)。`DOCKER_HOST` 在本行:远端 docker 守护进程 = 本车道 + `coords.DOCKER_HOST`。 */
99
+ "local-docker": {
100
+ tenantSeparation: "supported",
101
+ requiredEnv: ["DOCKER_IMAGE"],
102
+ requiredSecretEnv: [],
103
+ optionalEnv: [
104
+ "DOCKER_MOUNT_PATH",
105
+ "DOCKER_PATH",
106
+ "DOCKER_HOST",
107
+ "DOCKER_MEMORY",
108
+ "DOCKER_CPUS",
109
+ "DOCKER_PIDS_LIMIT",
110
+ "DOCKER_DROP_CAPS",
111
+ "DOCKER_NETWORK",
112
+ "DOCKER_COMMAND_TIMEOUT_MS",
113
+ ],
114
+ optionalSecretEnv: ["DOCKER_SANDBOX_ENV"],
115
+ },
116
+ /** server `config.ts` :4055 臂 + `parseDevicePreAuthPerRemoteEnv` :871 / `parseDeviceEnrollTokenTtlEnv` :857(无必需键:设备是运行期
117
+ * 连进来的);`execution-lane-caps.ts` :327 `supported`(发起者自己的设备)。 */
118
+ device: {
119
+ tenantSeparation: "supported",
120
+ requiredEnv: [],
121
+ requiredSecretEnv: [],
122
+ optionalEnv: [
123
+ "DEVICE_DELIVERY_TIMEOUT_MS",
124
+ "DEVICE_RECONNECT_GRACE_MS",
125
+ "DEVICE_EXEC_TIMEOUT_MS",
126
+ "DEVICE_MAX_INFLIGHT_PER_DEVICE",
127
+ "DEVICE_MAX_PREAUTH_PER_REMOTE",
128
+ "DEVICE_ENROLL_TOKEN_TTL_SEC",
129
+ "DEVICE_SUPERSEDE",
130
+ ],
131
+ optionalSecretEnv: [],
132
+ },
133
+ };
134
+ /** 车道表(次序 = {@link EXECUTION_LANE_WORDS})。整张表深冻结 —— 它是唯一正本,消费方拿到的不许被改。 */
135
+ export const EXECUTION_LANES = Object.freeze(EXECUTION_LANE_WORDS.map((lane) => {
136
+ const row = LANE_ROWS[lane];
137
+ return Object.freeze({
138
+ lane,
139
+ tenantSeparation: row.tenantSeparation,
140
+ requiredEnv: Object.freeze([...row.requiredEnv]),
141
+ requiredSecretEnv: Object.freeze([...row.requiredSecretEnv]),
142
+ optionalEnv: Object.freeze([...row.optionalEnv]),
143
+ optionalSecretEnv: Object.freeze([...row.optionalSecretEnv]),
144
+ });
145
+ }));
146
+ /** 存储后端(→ `DB_BACKEND`)。= server `src/config-types.ts` `DB_BACKEND_WORDS`(:67)逐字同序。 */
147
+ export const STORE_BACKENDS = ["mysql", "pg", "local", "memory"];
148
+ /** session 店(→ `SESSION_BACKEND`)。= server `src/config.ts` :1760 `enumEnv("SESSION_BACKEND", …)` 的词表逐字同序
149
+ * (`tidb` 不在集内:引擎见之拒启)。 */
150
+ export const SESSION_BACKENDS = ["memory", "mysql", "auto"];
151
+ /** 记忆持久面方言(→ `MEMORY_ENGINE_BACKEND`)。= server `src/config.ts` :3436 闭集 / 目录 :468 `enumValues` 逐字同序。 */
152
+ export const MEMORY_ENGINE_BACKENDS = ["file", "pg", "tidb"];
package/dist/file-edit.js CHANGED
@@ -95,29 +95,34 @@ export function listRequiredEnvNames(eff, remoteExec, resolvedEnv) {
95
95
  add(r.envName, use.domain, use.detail);
96
96
  if (remoteExec) {
97
97
  const re = remoteExec;
98
- if (re.provider === "e2b") {
99
- add(re.apiKeyEnv, "remoteExec", "e2b.apiKeyEnv");
100
- for (const e of re.sandboxEnv ?? [])
101
- add(e, "remoteExec", "e2b.sandboxEnv[]");
102
- }
103
- else if (re.provider === "k8s") {
104
- add(re.tokenEnv, "remoteExec", "k8s.tokenEnv");
105
- add(re.caCertEnv, "remoteExec", "k8s.caCertEnv");
106
- if (re.s3Snapshot) {
107
- add(re.s3Snapshot.accessKeyEnv, "remoteExec", "k8s.s3Snapshot.accessKeyEnv");
108
- add(re.s3Snapshot.secretKeyEnv, "remoteExec", "k8s.s3Snapshot.secretKeyEnv");
98
+ // Exhaustive over the provider words (7.0.0: ⊆ EXECUTION_LANE_WORDS) — a new provider that carries an env-NAME
99
+ // ref must say so here, or this switch is a compile error (no silent "carries nothing" default).
100
+ switch (re.provider) {
101
+ case "e2b":
102
+ add(re.apiKeyEnv, "remoteExec", "e2b.apiKeyEnv");
103
+ for (const e of re.sandboxEnv ?? [])
104
+ add(e, "remoteExec", "e2b.sandboxEnv[]");
105
+ break;
106
+ case "k8s":
107
+ add(re.tokenEnv, "remoteExec", "k8s.tokenEnv");
108
+ add(re.caCertEnv, "remoteExec", "k8s.caCertEnv");
109
+ if (re.s3Snapshot) {
110
+ add(re.s3Snapshot.accessKeyEnv, "remoteExec", "k8s.s3Snapshot.accessKeyEnv");
111
+ add(re.s3Snapshot.secretKeyEnv, "remoteExec", "k8s.s3Snapshot.secretKeyEnv");
112
+ }
113
+ break;
114
+ case "ssh":
115
+ add(re.privateKeyEnv, "remoteExec", "ssh.privateKeyEnv");
116
+ break;
117
+ case "adb":
118
+ case "host":
119
+ case "local-docker":
120
+ break; // these carry no secret env-NAME refs
121
+ default: {
122
+ const unhandled = re;
123
+ throw new Error(`listRequiredEnvNames: unhandled remoteExec provider ${JSON.stringify(unhandled)}`);
109
124
  }
110
125
  }
111
- else if (re.provider === "ssh") {
112
- add(re.privateKeyEnv, "remoteExec", "ssh.privateKeyEnv");
113
- }
114
- else if (re.provider === "remote-docker") {
115
- add(re.tlsCertEnv, "remoteExec", "remote-docker.tlsCertEnv");
116
- add(re.tlsKeyEnv, "remoteExec", "remote-docker.tlsKeyEnv");
117
- add(re.tlsCaEnv, "remoteExec", "remote-docker.tlsCaEnv");
118
- add(re.sshKeyEnv, "remoteExec", "remote-docker.sshKeyEnv");
119
- }
120
- // adb / host / local-docker carry no secret env-NAME refs.
121
126
  }
122
127
  return [...map.entries()]
123
128
  .map(([envName, refs]) => ({ envName, refs, present: resolvedEnv[envName] !== undefined }))
package/dist/hooks.d.ts CHANGED
@@ -1732,7 +1732,7 @@ export declare const PostModelSwitchHookInput: z.ZodObject<{
1732
1732
  pricing: z.ZodEnum<["configured", "catalog", "default"]>;
1733
1733
  hook_event_name: z.ZodLiteral<"PostModelSwitch">;
1734
1734
  }, "strip", z.ZodTypeAny, {
1735
- source: "command" | "auto" | "resume" | "picker" | "sdk";
1735
+ source: "auto" | "command" | "resume" | "picker" | "sdk";
1736
1736
  to_model: string;
1737
1737
  session_id: string;
1738
1738
  transcript_path: string;
@@ -1749,7 +1749,7 @@ export declare const PostModelSwitchHookInput: z.ZodObject<{
1749
1749
  permission_mode?: string | undefined;
1750
1750
  agent_id?: string | undefined;
1751
1751
  }, {
1752
- source: "command" | "auto" | "resume" | "picker" | "sdk";
1752
+ source: "auto" | "command" | "resume" | "picker" | "sdk";
1753
1753
  to_model: string;
1754
1754
  session_id: string;
1755
1755
  transcript_path: string;
@@ -3173,7 +3173,7 @@ export declare const HookInput: z.ZodDiscriminatedUnion<"hook_event_name", [z.Zo
3173
3173
  pricing: z.ZodEnum<["configured", "catalog", "default"]>;
3174
3174
  hook_event_name: z.ZodLiteral<"PostModelSwitch">;
3175
3175
  }, "strip", z.ZodTypeAny, {
3176
- source: "command" | "auto" | "resume" | "picker" | "sdk";
3176
+ source: "auto" | "command" | "resume" | "picker" | "sdk";
3177
3177
  to_model: string;
3178
3178
  session_id: string;
3179
3179
  transcript_path: string;
@@ -3190,7 +3190,7 @@ export declare const HookInput: z.ZodDiscriminatedUnion<"hook_event_name", [z.Zo
3190
3190
  permission_mode?: string | undefined;
3191
3191
  agent_id?: string | undefined;
3192
3192
  }, {
3193
- source: "command" | "auto" | "resume" | "picker" | "sdk";
3193
+ source: "auto" | "command" | "resume" | "picker" | "sdk";
3194
3194
  to_model: string;
3195
3195
  session_id: string;
3196
3196
  transcript_path: string;
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  * the config center, the worker service, and the TOC desktop/CLI.
4
4
  */
5
5
  export * from "./types.js";
6
+ export * from "./execution-lanes.js";
6
7
  export * from "./hash.js";
7
8
  export * from "./sha256.js";
8
9
  export * from "./skills-manifest.js";
@@ -17,6 +18,7 @@ export * from "./scheduler-store.js";
17
18
  export * from "./cross-session.js";
18
19
  export * from "./hooks.js";
19
20
  export * from "./fleet.js";
21
+ export * from "./migrate-workers.js";
20
22
  export * from "./migrate.js";
21
23
  export * from "./api/auth.js";
22
24
  export * from "./api/auth-bridge.js";
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  * the config center, the worker service, and the TOC desktop/CLI.
4
4
  */
5
5
  export * from "./types.js";
6
+ export * from "./execution-lanes.js"; // 7.0.0 M-R2: the ONE lane / store vocabulary (EXECUTION_LANE_WORDS / EXECUTION_LANES / STORE_BACKENDS / SESSION_BACKENDS / MEMORY_ENGINE_BACKENDS) — pure data, browser-safe
6
7
  export * from "./hash.js";
7
8
  export * from "./sha256.js";
8
9
  export * from "./skills-manifest.js";
@@ -17,6 +18,7 @@ export * from "./scheduler-store.js"; // R7 self-wake scheduler store CONTRACT (
17
18
  export * from "./cross-session.js"; // CC-parity `settings.json` cross-session keys (crossSessionInbound / dialogExpiry) — pure/browser-safe
18
19
  export * from "./hooks.js"; // CC-parity hooks contract (31-event set + owner map + per-event input/output shapes + HooksConfig) & stop attribution (stoppedBy) — pure/browser-safe
19
20
  export * from "./fleet.js"; // scheduling/fleet contract (drain protocol · instance discovery · dispatch policy · quota lease · usage report · quota reset event) — CONTROL-PLANE-SCHEDULING-DESIGN, pure/browser-safe
21
+ export * from "./migrate-workers.js"; // 7.0.0 one-time workers-domain migration (env.REMOTE_EXEC / sessionBackend / tidb* → runtime.execution / runtime.store), pure — run it on the RAW document before parseDomain
20
22
  export * from "./migrate.js"; // 0.5→0.6 migration tools (hosts grandfathering + worker.model→catalog migration PLAN — report/draft only, E1; EXPERT-REDESIGN §2-2/§3), pure/browser-safe
21
23
  export * from "./api/auth.js"; // /api/v1/auth/* wire contract (RFC 8628 device flow + rotating refresh + logout + approve) — REGISTRY-REBUILD-DESIGN §2, M1 shipped shapes frozen, pure/browser-safe
22
24
  export * from "./api/auth-bridge.js"; // 鉴权桥 contract: worker verifies registry user JWT (RS256-only, JWKS kid-miss refresh + throttle, offline grace fail-closed) → VerifiedIdentity — design §5, the service verifier's spec
@@ -0,0 +1,63 @@
1
+ /**
2
+ * workers 域的**一次性迁移**(7.0.0,M-R2 WorkerSpec 结构块)—— 持久面唯一允许的过渡,窗口关了就整文件删除
3
+ * (连同 `types.ts` 里「删除项读到即拒」那一只预处理)。纯函数、无 I/O、不改入参;文件形的入口是
4
+ * `npx @sema-agent/settings-schema migrate-workers <file>`(`bin/settings-schema.mjs`,同一只函数;命令拼写的唯一正本 = `MIGRATE_WORKERS_COMMAND`)。
5
+ *
6
+ * ## 谁跑、在哪
7
+ * 读前抬升:在 `parseDomain("workers", raw)` / `DOMAIN_SCHEMAS.workers.safeParse` **之前**对**原始 JSON** 跑 —— workers 是
8
+ * 「读即抛」的 gate 域,6.x 存下来的行在 7.0.0 schema 上整域读不出来(删除项响亮拒、enabled 的 worker 缺两块)。中心在它唯一的
9
+ * workers 读口跑一次,结果写回;CLI 给文件后端 / 离线用。
10
+ *
11
+ * ## 规则(每个旧键恰好一种去处)
12
+ * · **车道**:`env.REMOTE_EXEC` 是车道词 ⇒ `runtime.execution.lane`;同车道的 env 键 ⇒ `coords`,同车道的 `secretEnv` 密钥 ⇒
13
+ * `secretRefs`(都按导出表 `EXECUTION_LANES` 的栏判)。别车道的键、`REQUIRE_EXECUTION_LANE` ⇒ **dropped**(引擎只读被点名车道
14
+ * 那一臂;声明了车道就恒渲 true)—— 今天就是惰性的。**enabled 且无 `REMOTE_EXEC` ⇒ refused**:那是「无手照回 completed」的
15
+ * 病例本身,迁移不替运维猜车道。停用且无 `REMOTE_EXEC` ⇒ 不写车道,残留车道键 dropped。
16
+ * · **店**:`sessionBackend` ⇒ `store.session`(`tidb` ⇒ `mysql`,且 `store.backend` 由此为 `mysql`);`memoryBackend` ⇒ **dropped**
17
+ * (它从未到达引擎);`coords.tidbHost` 在 ⇒ `store.backend = mysql`,`coords.tidb*` + `runtime.db` ⇒ `store.db`(缺端口 ⇒ 引擎
18
+ * `MYSQL_PORT` 的缺省 3306),`secretRefs.tidbPasswordEnv` ⇒ `secretRefs.dbPasswordEnv`;没有 host ⇒ `store.backend = local`
19
+ * (设计稿 §3.4:推一次、写死),零散坐标惰性 ⇒ dropped。`env` 逃生口里的 `DB_BACKEND` / `MEMORY_ENGINE` /
20
+ * `MEMORY_ENGINE_BACKEND`(渲染器以前不渲,运维只能写在那里)⇒ 搬进 `store`,显式的 `DB_BACKEND` 优先。停用且旧形没点名任何店
21
+ * (无 host、无 `DB_BACKEND`、无 `tidb` 词)⇒ 不写店,旧字段 dropped。
22
+ * · **表达不了 ⇒ refused**:值写错了栏(密钥以字面值写在 env、坐标来自宿主密钥)、不认识的词、新旧两形并存、`DB_BACKEND=pg`
23
+ * 配 MySQL 族坐标 —— 迁移不搬值进密钥位、不读宿主密钥、不替运维挑赢家、不换库。
24
+ * · **终判 = 7.0.0 schema**:迁出来的每只 worker 都过 `WorkerSpec.safeParse`;不过 ⇒ refused,拒因是 schema 自己的 issue
25
+ * (例:多租户 + host 车道、点了车道却缺必需坐标)。迁移不复述任何一条写闸规则。
26
+ *
27
+ * refused 的 worker **不进**输出 `doc`(中心不加载它、在列表标红点名 `field`;CLI 有 refused 就一字不写),但随 `refused[].original`
28
+ * 逐字带回:`doc` ∪ `refused[].original` 覆盖输入的全部 worker,中心写回前按 `index` 放回(或另存),运维的 spec 不会随迁移消失。
29
+ * 拒因 / dropped 只点名字段,不回显值。
30
+ *
31
+ * ## 幂等
32
+ * 已迁形(没有任何旧键、该有的块都在)⇒ 原样(同一引用)返回、`changed = []`。所以再跑一次 ⇒ 零变化、`doc` 字节相等。
33
+ */
34
+ import { type ConfigPath } from "./config-fns.js";
35
+ /** 一个被丢掉的旧字段(引擎本就不读它 —— 通知一行,不是错误)。`field` 相对该 worker 条目;`index` = 它在输入 `workers` 里的下标。 */
36
+ export interface WorkersMigrationDrop {
37
+ readonly name: string;
38
+ readonly index: number;
39
+ readonly field: ConfigPath;
40
+ }
41
+ /** 一只迁不了的 worker(不进输出 `doc`)。`field` 指到要人改的那一格(相对该 worker 条目;终判失败时是 7.0.0 形里的路径),
42
+ * `reason` 只点名字段、不回显值。`original` = 输入里那一行**逐字**(同一个值,未拷贝未改写)——
43
+ * 🔴 **写回 `doc` 之前必须把每个 `refused[].original` 按 `index` 放回(或另存)**,否则这只 worker 的 spec 随写回从存储里消失,
44
+ * 运维看到的是「worker 不见了」而不是「worker 被标红」。它是调用方自己的数据,原样带回;要记日志,按调用方记那一行的规矩记。 */
45
+ export interface WorkersMigrationRefusal {
46
+ readonly name: string;
47
+ readonly index: number;
48
+ readonly field: ConfigPath;
49
+ readonly reason: string;
50
+ readonly original: unknown;
51
+ }
52
+ /** `migrateWorkersDomain` 的结果。`changed` = 被改写的 worker 名(含只丢了旧字段的)。 */
53
+ export interface WorkersDomainMigration {
54
+ readonly doc: unknown;
55
+ readonly changed: readonly string[];
56
+ readonly dropped: readonly WorkersMigrationDrop[];
57
+ readonly refused: readonly WorkersMigrationRefusal[];
58
+ }
59
+ /**
60
+ * 迁移一份 workers 域**原始**文档(`{ workers: [...] }`)。不是这个形 ⇒ 原样返回(判形是 `parseDomain` 的事,迁移不抛)。
61
+ * 规则、幂等与 refused 的处置见文件头。
62
+ */
63
+ export declare function migrateWorkersDomain(doc: unknown): WorkersDomainMigration;