@sema-agent/settings-schema 6.3.0 → 7.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 +207 -0
- package/README.md +40 -5
- package/bin/settings-schema.mjs +22 -0
- package/conformance/VERSION +1 -1
- package/conformance/cases/c48-s935-model-headers-transported/expected.json +115 -0
- package/conformance/cases/c48-s935-model-headers-transported/input.json +37 -0
- package/conformance/cases/c48-s935-model-headers-transported/meta.json +6 -0
- package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +98 -0
- package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/input.json +28 -0
- package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/meta.json +9 -0
- package/conformance/cases/c50-s784-lane-host-legal/expected.json +102 -0
- package/conformance/cases/c50-s784-lane-host-legal/input.json +29 -0
- package/conformance/cases/c50-s784-lane-host-legal/meta.json +5 -0
- package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +104 -0
- package/conformance/cases/c51-s784-lane-e2b-legal/input.json +32 -0
- package/conformance/cases/c51-s784-lane-e2b-legal/meta.json +5 -0
- package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +106 -0
- package/conformance/cases/c52-s784-lane-k8s-legal/input.json +34 -0
- package/conformance/cases/c52-s784-lane-k8s-legal/meta.json +5 -0
- package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +106 -0
- package/conformance/cases/c53-s784-lane-ssh-legal/input.json +34 -0
- package/conformance/cases/c53-s784-lane-ssh-legal/meta.json +5 -0
- package/conformance/cases/c54-s784-lane-adb-legal/expected.json +102 -0
- package/conformance/cases/c54-s784-lane-adb-legal/input.json +29 -0
- package/conformance/cases/c54-s784-lane-adb-legal/meta.json +5 -0
- package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +103 -0
- package/conformance/cases/c55-s784-lane-local-docker-legal/input.json +30 -0
- package/conformance/cases/c55-s784-lane-local-docker-legal/meta.json +5 -0
- package/conformance/cases/c56-s784-lane-device-legal/expected.json +109 -0
- package/conformance/cases/c56-s784-lane-device-legal/input.json +36 -0
- package/conformance/cases/c56-s784-lane-device-legal/meta.json +5 -0
- package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +83 -0
- package/conformance/cases/c57-s784-g1-enabled-no-lane/input.json +23 -0
- package/conformance/cases/c57-s784-g1-enabled-no-lane/meta.json +5 -0
- package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +83 -0
- package/conformance/cases/c58-s784-g2-multitenant-host/input.json +34 -0
- package/conformance/cases/c58-s784-g2-multitenant-host/meta.json +5 -0
- package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +83 -0
- package/conformance/cases/c59-s784-g3-k8s-missing-image/input.json +26 -0
- package/conformance/cases/c59-s784-g3-k8s-missing-image/meta.json +5 -0
- package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +83 -0
- package/conformance/cases/c60-s784-g3-e2b-missing-key/input.json +26 -0
- package/conformance/cases/c60-s784-g3-e2b-missing-key/meta.json +5 -0
- package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +83 -0
- package/conformance/cases/c61-s784-g3-other-lane-key/input.json +30 -0
- package/conformance/cases/c61-s784-g3-other-lane-key/meta.json +5 -0
- package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +83 -0
- package/conformance/cases/c62-s784-g7a-session-tidb/input.json +27 -0
- package/conformance/cases/c62-s784-g7a-session-tidb/meta.json +5 -0
- package/conformance/cases/c63-s784-host-two-replicas/expected.json +83 -0
- package/conformance/cases/c63-s784-host-two-replicas/input.json +27 -0
- package/conformance/cases/c63-s784-host-two-replicas/meta.json +5 -0
- package/conformance/cases/c64-s784-multitenant-local-store/expected.json +83 -0
- package/conformance/cases/c64-s784-multitenant-local-store/input.json +30 -0
- package/conformance/cases/c64-s784-multitenant-local-store/meta.json +5 -0
- package/conformance/cases/c65-s784-pg-without-db/expected.json +83 -0
- package/conformance/cases/c65-s784-pg-without-db/input.json +27 -0
- package/conformance/cases/c65-s784-pg-without-db/meta.json +5 -0
- package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +83 -0
- package/conformance/cases/c66-s784-memory-backend-mismatch/input.json +36 -0
- package/conformance/cases/c66-s784-memory-backend-mismatch/meta.json +5 -0
- package/conformance/cases/c67-s784-retired-field-loud/expected.json +83 -0
- package/conformance/cases/c67-s784-retired-field-loud/input.json +22 -0
- package/conformance/cases/c67-s784-retired-field-loud/meta.json +5 -0
- package/conformance/cases/c68-s784-device-local-store/expected.json +83 -0
- package/conformance/cases/c68-s784-device-local-store/input.json +26 -0
- package/conformance/cases/c68-s784-device-local-store/meta.json +5 -0
- package/conformance/cases/cli04-env-value-with-newline/expected.json +13 -2
- package/conformance/cases/cli04-env-value-with-newline/input.json +8 -1
- package/conformance/cases/cli04-env-value-with-newline/meta.json +1 -1
- package/conformance/migrate-workers/m01-g5-k8s-env-lifted/expected.json +49 -0
- package/conformance/migrate-workers/m01-g5-k8s-env-lifted/input.json +26 -0
- package/conformance/migrate-workers/m01-g5-k8s-env-lifted/meta.json +4 -0
- package/conformance/migrate-workers/m02-g5-idempotent/expected.json +38 -0
- package/conformance/migrate-workers/m02-g5-idempotent/input.json +33 -0
- package/conformance/migrate-workers/m02-g5-idempotent/meta.json +5 -0
- package/conformance/migrate-workers/m03-g5-refused-no-lane/expected.json +71 -0
- package/conformance/migrate-workers/m03-g5-refused-no-lane/input.json +42 -0
- package/conformance/migrate-workers/m03-g5-refused-no-lane/meta.json +4 -0
- package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/expected.json +52 -0
- package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/input.json +30 -0
- package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/meta.json +4 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +70 -0
- package/dist/execution-lane-index.d.ts +19 -0
- package/dist/execution-lane-index.js +23 -0
- package/dist/execution-lanes.d.ts +64 -0
- package/dist/execution-lanes.js +152 -0
- package/dist/file-edit.js +26 -21
- package/dist/hash.d.ts +3 -3
- package/dist/hash.js +3 -3
- package/dist/hooks.d.ts +12 -12
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/migrate-workers.d.ts +63 -0
- package/dist/migrate-workers.js +331 -0
- package/dist/remote-exec.d.ts +23 -64
- package/dist/remote-exec.js +20 -71
- package/dist/secret-refs.d.ts +7 -1
- package/dist/secret-refs.js +9 -3
- package/dist/types.d.ts +1382 -430
- package/dist/types.js +298 -76
- package/package.json +9 -1
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
3193
|
+
source: "auto" | "command" | "resume" | "picker" | "sdk";
|
|
3194
3194
|
to_model: string;
|
|
3195
3195
|
session_id: string;
|
|
3196
3196
|
transcript_path: string;
|
|
@@ -5545,20 +5545,20 @@ export declare const HttpHookEntry: z.ZodObject<{
|
|
|
5545
5545
|
}, "strip", z.ZodTypeAny, {
|
|
5546
5546
|
type: "http";
|
|
5547
5547
|
url: string;
|
|
5548
|
+
headers?: Record<string, string> | undefined;
|
|
5548
5549
|
if?: string | undefined;
|
|
5549
5550
|
timeout?: number | undefined;
|
|
5550
5551
|
statusMessage?: string | undefined;
|
|
5551
5552
|
once?: boolean | undefined;
|
|
5552
|
-
headers?: Record<string, string> | undefined;
|
|
5553
5553
|
allowedEnvVars?: string[] | undefined;
|
|
5554
5554
|
}, {
|
|
5555
5555
|
type: "http";
|
|
5556
5556
|
url: string;
|
|
5557
|
+
headers?: Record<string, string> | undefined;
|
|
5557
5558
|
if?: string | undefined;
|
|
5558
5559
|
timeout?: number | undefined;
|
|
5559
5560
|
statusMessage?: string | undefined;
|
|
5560
5561
|
once?: boolean | undefined;
|
|
5561
|
-
headers?: Record<string, string> | undefined;
|
|
5562
5562
|
allowedEnvVars?: string[] | undefined;
|
|
5563
5563
|
}>;
|
|
5564
5564
|
/** One configured hook (CC settings shape + shell superset fields; discriminated on `type`). */
|
|
@@ -5655,20 +5655,20 @@ export declare const HookEntry: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
|
5655
5655
|
}, "strip", z.ZodTypeAny, {
|
|
5656
5656
|
type: "http";
|
|
5657
5657
|
url: string;
|
|
5658
|
+
headers?: Record<string, string> | undefined;
|
|
5658
5659
|
if?: string | undefined;
|
|
5659
5660
|
timeout?: number | undefined;
|
|
5660
5661
|
statusMessage?: string | undefined;
|
|
5661
5662
|
once?: boolean | undefined;
|
|
5662
|
-
headers?: Record<string, string> | undefined;
|
|
5663
5663
|
allowedEnvVars?: string[] | undefined;
|
|
5664
5664
|
}, {
|
|
5665
5665
|
type: "http";
|
|
5666
5666
|
url: string;
|
|
5667
|
+
headers?: Record<string, string> | undefined;
|
|
5667
5668
|
if?: string | undefined;
|
|
5668
5669
|
timeout?: number | undefined;
|
|
5669
5670
|
statusMessage?: string | undefined;
|
|
5670
5671
|
once?: boolean | undefined;
|
|
5671
|
-
headers?: Record<string, string> | undefined;
|
|
5672
5672
|
allowedEnvVars?: string[] | undefined;
|
|
5673
5673
|
}>]>;
|
|
5674
5674
|
export type HookEntry = z.infer<typeof HookEntry>;
|
|
@@ -5773,20 +5773,20 @@ export declare const HookMatcherEntry: z.ZodObject<{
|
|
|
5773
5773
|
}, "strip", z.ZodTypeAny, {
|
|
5774
5774
|
type: "http";
|
|
5775
5775
|
url: string;
|
|
5776
|
+
headers?: Record<string, string> | undefined;
|
|
5776
5777
|
if?: string | undefined;
|
|
5777
5778
|
timeout?: number | undefined;
|
|
5778
5779
|
statusMessage?: string | undefined;
|
|
5779
5780
|
once?: boolean | undefined;
|
|
5780
|
-
headers?: Record<string, string> | undefined;
|
|
5781
5781
|
allowedEnvVars?: string[] | undefined;
|
|
5782
5782
|
}, {
|
|
5783
5783
|
type: "http";
|
|
5784
5784
|
url: string;
|
|
5785
|
+
headers?: Record<string, string> | undefined;
|
|
5785
5786
|
if?: string | undefined;
|
|
5786
5787
|
timeout?: number | undefined;
|
|
5787
5788
|
statusMessage?: string | undefined;
|
|
5788
5789
|
once?: boolean | undefined;
|
|
5789
|
-
headers?: Record<string, string> | undefined;
|
|
5790
5790
|
allowedEnvVars?: string[] | undefined;
|
|
5791
5791
|
}>]>, "many">;
|
|
5792
5792
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -5819,11 +5819,11 @@ export declare const HookMatcherEntry: z.ZodObject<{
|
|
|
5819
5819
|
} | {
|
|
5820
5820
|
type: "http";
|
|
5821
5821
|
url: string;
|
|
5822
|
+
headers?: Record<string, string> | undefined;
|
|
5822
5823
|
if?: string | undefined;
|
|
5823
5824
|
timeout?: number | undefined;
|
|
5824
5825
|
statusMessage?: string | undefined;
|
|
5825
5826
|
once?: boolean | undefined;
|
|
5826
|
-
headers?: Record<string, string> | undefined;
|
|
5827
5827
|
allowedEnvVars?: string[] | undefined;
|
|
5828
5828
|
})[];
|
|
5829
5829
|
matcher?: string | undefined;
|
|
@@ -5857,11 +5857,11 @@ export declare const HookMatcherEntry: z.ZodObject<{
|
|
|
5857
5857
|
} | {
|
|
5858
5858
|
type: "http";
|
|
5859
5859
|
url: string;
|
|
5860
|
+
headers?: Record<string, string> | undefined;
|
|
5860
5861
|
if?: string | undefined;
|
|
5861
5862
|
timeout?: number | undefined;
|
|
5862
5863
|
statusMessage?: string | undefined;
|
|
5863
5864
|
once?: boolean | undefined;
|
|
5864
|
-
headers?: Record<string, string> | undefined;
|
|
5865
5865
|
allowedEnvVars?: string[] | undefined;
|
|
5866
5866
|
})[];
|
|
5867
5867
|
matcher?: string | undefined;
|
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
|
+
* `settings-schema migrate-workers <file>`(`bin/settings-schema.mjs`,同一只函数)。
|
|
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;
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workers 域的**一次性迁移**(7.0.0,M-R2 WorkerSpec 结构块)—— 持久面唯一允许的过渡,窗口关了就整文件删除
|
|
3
|
+
* (连同 `types.ts` 里「删除项读到即拒」那一只预处理)。纯函数、无 I/O、不改入参;文件形的入口是
|
|
4
|
+
* `settings-schema migrate-workers <file>`(`bin/settings-schema.mjs`,同一只函数)。
|
|
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 { formatConfigPath, redactSecrets } from "./config-fns.js";
|
|
35
|
+
import { LANE_ENV_OWNER } from "./execution-lane-index.js";
|
|
36
|
+
import { EXECUTION_LANE_WORDS, MEMORY_ENGINE_BACKENDS, STORE_BACKENDS } from "./execution-lanes.js";
|
|
37
|
+
import { WorkerSpec } from "./types.js";
|
|
38
|
+
function isRec(v) {
|
|
39
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
40
|
+
}
|
|
41
|
+
/** 车道块的两个特殊键(不在车道表里,但归车道块)。 */
|
|
42
|
+
const LANE_WORD_KEYS = ["REMOTE_EXEC", "REQUIRE_EXECUTION_LANE"];
|
|
43
|
+
/** 渲染器以前不渲、运维只能写在 env 逃生口里的店键(7.0.0 起归 `runtime.store`)。 */
|
|
44
|
+
const STORE_ENV_KEYS = ["DB_BACKEND", "MEMORY_ENGINE", "MEMORY_ENGINE_BACKEND"];
|
|
45
|
+
/** 6.x 删除项:`[容器, 键]`。 */
|
|
46
|
+
const SIX_X_STORE_FIELDS = [
|
|
47
|
+
["runtime", "sessionBackend"],
|
|
48
|
+
["runtime", "memoryBackend"],
|
|
49
|
+
["runtime", "db"],
|
|
50
|
+
["coords", "tidbHost"],
|
|
51
|
+
["coords", "tidbPort"],
|
|
52
|
+
["coords", "tidbUser"],
|
|
53
|
+
["secretRefs", "tidbPasswordEnv"],
|
|
54
|
+
];
|
|
55
|
+
/** 引擎 `MEMORY_ENGINE` 的两族词(server `src/config.ts` :3011–3012,比较前 trim + 小写)。 */
|
|
56
|
+
const MEMORY_ENGINE_ON_WORDS = ["on", "true", "1", "yes", "enabled"];
|
|
57
|
+
const MEMORY_ENGINE_OFF_WORDS = ["off", "false", "0", "no", "disabled"];
|
|
58
|
+
/** 引擎 `MYSQL_PORT` 的缺省(server `src/config.ts` :1818)—— 旧形没写端口时引擎连的就是它。 */
|
|
59
|
+
const MYSQL_DEFAULT_PORT = 3306;
|
|
60
|
+
function isLaneWord(v) {
|
|
61
|
+
return typeof v === "string" && EXECUTION_LANE_WORDS.some((w) => w === v);
|
|
62
|
+
}
|
|
63
|
+
function isStoreWord(v) {
|
|
64
|
+
return typeof v === "string" && STORE_BACKENDS.some((w) => w === v);
|
|
65
|
+
}
|
|
66
|
+
function isMemoryBackendWord(v) {
|
|
67
|
+
return typeof v === "string" && MEMORY_ENGINE_BACKENDS.some((w) => w === v);
|
|
68
|
+
}
|
|
69
|
+
/** 拒因里只点名形如词的值(短、小写);其余一律不回显(值可能是粘错位置的凭据)。 */
|
|
70
|
+
function describeWord(v) {
|
|
71
|
+
return typeof v === "string" && /^[a-z0-9][a-z0-9-]{0,31}$/.test(v) ? `"${v}"` : "(value not shown)";
|
|
72
|
+
}
|
|
73
|
+
const UNCHANGED = { kind: "unchanged" };
|
|
74
|
+
function migrateEntry(raw, index) {
|
|
75
|
+
if (!isRec(raw) || typeof raw.name !== "string")
|
|
76
|
+
return UNCHANGED; // 不是一只可辨认的 worker:判形是 parseDomain 的事
|
|
77
|
+
const name = raw.name;
|
|
78
|
+
const refuse = (field, reason) => ({ kind: "refused", refusal: { name, index, field, reason: redactSecrets(reason), original: raw } });
|
|
79
|
+
const enabled = raw.enabled !== false;
|
|
80
|
+
const has = (container, key) => isRec(container) && Object.hasOwn(container, key);
|
|
81
|
+
const laneKeysOf = (rec) => isRec(rec) ? Object.keys(rec).filter((k) => LANE_WORD_KEYS.some((w) => w === k) || LANE_ENV_OWNER.has(k)) : [];
|
|
82
|
+
const storeKeysOf = (rec) => (isRec(rec) ? STORE_ENV_KEYS.filter((k) => Object.hasOwn(rec, k)) : []);
|
|
83
|
+
const envLaneKeys = laneKeysOf(raw.env);
|
|
84
|
+
const secretLaneKeys = laneKeysOf(raw.secretEnv);
|
|
85
|
+
const envStoreKeys = storeKeysOf(raw.env);
|
|
86
|
+
const secretStoreKeys = storeKeysOf(raw.secretEnv);
|
|
87
|
+
const oldStoreFields = SIX_X_STORE_FIELDS.filter(([c, k]) => has(raw[c], k));
|
|
88
|
+
const hasExecution = has(raw.runtime, "execution");
|
|
89
|
+
const hasStore = has(raw.runtime, "store");
|
|
90
|
+
const laneTouched = envLaneKeys.length + secretLaneKeys.length > 0;
|
|
91
|
+
const storeTouched = oldStoreFields.length + envStoreKeys.length + secretStoreKeys.length > 0;
|
|
92
|
+
if (hasExecution && laneTouched) {
|
|
93
|
+
return refuse(["runtime", "execution"], `declares runtime.execution AND execution-lane keys in env / secretEnv (${[...envLaneKeys, ...secretLaneKeys].join(", ")}) — keep one form; the migration does not pick a winner`);
|
|
94
|
+
}
|
|
95
|
+
// 新形的店字段有两处:`runtime.store` 与 `secretRefs.dbPasswordEnv`(gpt-6 腿 F1:只看前者时,手填过新口令引用、又留着旧
|
|
96
|
+
// `tidbPasswordEnv` 的行会被旧引用静默覆盖)。任一在场 ∧ 还有旧店字段 ⇒ 两形并存,不挑赢家。
|
|
97
|
+
const newStoreFields = [...(hasStore ? ["runtime.store"] : []), ...(has(raw.secretRefs, "dbPasswordEnv") ? ["secretRefs.dbPasswordEnv"] : [])];
|
|
98
|
+
if (newStoreFields.length > 0 && storeTouched) {
|
|
99
|
+
const old = [...oldStoreFields.map(([c, k]) => `${c}.${k}`), ...envStoreKeys.map((k) => `env.${k}`), ...secretStoreKeys.map((k) => `secretEnv.${k}`)];
|
|
100
|
+
return refuse(["runtime", "store"], `declares the 7.0.0 store fields (${newStoreFields.join(", ")}) AND 6.x store fields (${old.join(", ")}) — keep one form; the migration does not pick a winner`);
|
|
101
|
+
}
|
|
102
|
+
const secretStoreKey = secretStoreKeys[0];
|
|
103
|
+
if (secretStoreKey !== undefined)
|
|
104
|
+
return refuse(["secretEnv", secretStoreKey], `${secretStoreKey} is set from a host secret — the migration cannot read it; declare runtime.store by hand`);
|
|
105
|
+
if (has(raw.secretEnv, "REMOTE_EXEC"))
|
|
106
|
+
return refuse(["secretEnv", "REMOTE_EXEC"], "the execution lane is set from a host secret — the migration cannot read it; declare runtime.execution.lane by hand");
|
|
107
|
+
const needLane = !hasExecution && (enabled || laneTouched);
|
|
108
|
+
const needStore = !hasStore && (enabled || storeTouched);
|
|
109
|
+
if (!needLane && !needStore)
|
|
110
|
+
return UNCHANGED;
|
|
111
|
+
const w = structuredClone(raw);
|
|
112
|
+
const runtime = isRec(w.runtime) ? w.runtime : {};
|
|
113
|
+
w.runtime = runtime;
|
|
114
|
+
const env = isRec(w.env) ? w.env : {};
|
|
115
|
+
const secretEnv = isRec(w.secretEnv) ? w.secretEnv : {};
|
|
116
|
+
const coords = isRec(w.coords) ? w.coords : {};
|
|
117
|
+
const secretRefs = isRec(w.secretRefs) ? w.secretRefs : {};
|
|
118
|
+
const dropped = [];
|
|
119
|
+
const drop = (container, slot, key) => {
|
|
120
|
+
delete container[key];
|
|
121
|
+
dropped.push({ name, index, field: [slot, key] });
|
|
122
|
+
};
|
|
123
|
+
// ── 车道 ─────────────────────────────────────────────────────────────────────────────────────────
|
|
124
|
+
if (needLane) {
|
|
125
|
+
const rx = env.REMOTE_EXEC;
|
|
126
|
+
if (rx === undefined || rx === "") {
|
|
127
|
+
if (enabled) {
|
|
128
|
+
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. ` +
|
|
129
|
+
`The migration does not guess a lane: set runtime.execution.lane (${EXECUTION_LANE_WORDS.join(" | ")}) by hand, or disable the worker`);
|
|
130
|
+
}
|
|
131
|
+
for (const k of envLaneKeys)
|
|
132
|
+
drop(env, "env", k); // 停用且没点名车道 ⇒ 每个车道键都是惰性的
|
|
133
|
+
for (const k of secretLaneKeys)
|
|
134
|
+
drop(secretEnv, "secretEnv", k);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
if (!isLaneWord(rx)) {
|
|
138
|
+
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.lane by hand`);
|
|
139
|
+
}
|
|
140
|
+
const lane = rx;
|
|
141
|
+
const laneCoords = {};
|
|
142
|
+
const laneSecretRefs = {};
|
|
143
|
+
for (const k of envLaneKeys) {
|
|
144
|
+
if (k === "REMOTE_EXEC") {
|
|
145
|
+
delete env[k];
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const owner = LANE_ENV_OWNER.get(k);
|
|
149
|
+
if (owner === undefined || owner.lane !== lane) {
|
|
150
|
+
drop(env, "env", k); // REQUIRE_EXECUTION_LANE(声明了车道就恒渲 true)与别车道的键(引擎只读被点名那一臂)
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const v = env[k];
|
|
154
|
+
if (owner.secret) {
|
|
155
|
+
return refuse(["env", k], `${k} is a secret of lane "${lane}" but env carries it as a literal value — the migration does not move a value into a secret slot: put it in the host secret source and reference it from runtime.execution.secretRefs.${k}`);
|
|
156
|
+
}
|
|
157
|
+
if (typeof v !== "string")
|
|
158
|
+
return refuse(["env", k], `env.${k} is not a string`);
|
|
159
|
+
laneCoords[k] = v;
|
|
160
|
+
delete env[k];
|
|
161
|
+
}
|
|
162
|
+
for (const k of secretLaneKeys) {
|
|
163
|
+
const owner = LANE_ENV_OWNER.get(k);
|
|
164
|
+
if (owner === undefined || owner.lane !== lane) {
|
|
165
|
+
drop(secretEnv, "secretEnv", k);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const v = secretEnv[k];
|
|
169
|
+
if (!owner.secret) {
|
|
170
|
+
return refuse(["secretEnv", k], `${k} is a non-secret coordinate of lane "${lane}" but is set from a host secret — the migration cannot read the value; write it in runtime.execution.coords.${k} by hand`);
|
|
171
|
+
}
|
|
172
|
+
if (typeof v !== "string")
|
|
173
|
+
return refuse(["secretEnv", k], `secretEnv.${k} is not a string`);
|
|
174
|
+
laneSecretRefs[k] = v;
|
|
175
|
+
delete secretEnv[k];
|
|
176
|
+
}
|
|
177
|
+
runtime.execution = { lane, coords: laneCoords, secretRefs: laneSecretRefs };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// ── 店 ───────────────────────────────────────────────────────────────────────────────────────────
|
|
181
|
+
if (needStore) {
|
|
182
|
+
if (Object.hasOwn(runtime, "memoryBackend"))
|
|
183
|
+
drop(runtime, "runtime", "memoryBackend"); // 从未到达引擎
|
|
184
|
+
const sessionRaw = runtime.sessionBackend;
|
|
185
|
+
const host = coords.tidbHost;
|
|
186
|
+
const hostSet = typeof host === "string" && host !== "";
|
|
187
|
+
const dbBackendRaw = env.DB_BACKEND;
|
|
188
|
+
const dbBackendSet = dbBackendRaw !== undefined && dbBackendRaw !== "";
|
|
189
|
+
const named = hostSet || dbBackendSet || sessionRaw === "tidb";
|
|
190
|
+
const sqlCoordFields = [
|
|
191
|
+
[runtime, "runtime", "db"],
|
|
192
|
+
[coords, "coords", "tidbHost"],
|
|
193
|
+
[coords, "coords", "tidbPort"],
|
|
194
|
+
[coords, "coords", "tidbUser"],
|
|
195
|
+
[secretRefs, "secretRefs", "tidbPasswordEnv"],
|
|
196
|
+
];
|
|
197
|
+
if (!enabled && !named) {
|
|
198
|
+
// 停用、旧形没点名任何店 ⇒ 不替它发明一个;旧字段全是惰性的
|
|
199
|
+
if (Object.hasOwn(runtime, "sessionBackend"))
|
|
200
|
+
drop(runtime, "runtime", "sessionBackend");
|
|
201
|
+
for (const [c, slot, k] of sqlCoordFields)
|
|
202
|
+
if (Object.hasOwn(c, k))
|
|
203
|
+
drop(c, slot, k);
|
|
204
|
+
for (const k of STORE_ENV_KEYS)
|
|
205
|
+
if (Object.hasOwn(env, k))
|
|
206
|
+
drop(env, "env", k);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
let session;
|
|
210
|
+
if (sessionRaw === undefined)
|
|
211
|
+
session = "auto"; // 6.x 的缺省
|
|
212
|
+
else if (sessionRaw === "tidb")
|
|
213
|
+
session = "mysql";
|
|
214
|
+
else if (sessionRaw === "memory" || sessionRaw === "auto")
|
|
215
|
+
session = sessionRaw;
|
|
216
|
+
else
|
|
217
|
+
return refuse(["runtime", "sessionBackend"], `runtime.sessionBackend ${describeWord(sessionRaw)} is not a 6.x session backend (tidb | memory | auto)`);
|
|
218
|
+
delete runtime.sessionBackend;
|
|
219
|
+
let backend;
|
|
220
|
+
if (dbBackendSet) {
|
|
221
|
+
if (!isStoreWord(dbBackendRaw))
|
|
222
|
+
return refuse(["env", "DB_BACKEND"], `env.DB_BACKEND ${describeWord(dbBackendRaw)} is not a store backend (${STORE_BACKENDS.join(" | ")}) — the engine refuses to start with it`);
|
|
223
|
+
backend = dbBackendRaw;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
backend = hostSet || sessionRaw === "tidb" ? "mysql" : "local";
|
|
227
|
+
}
|
|
228
|
+
delete env.DB_BACKEND;
|
|
229
|
+
const sqlCoordsPresent = sqlCoordFields.filter(([c, , k]) => Object.hasOwn(c, k));
|
|
230
|
+
const store = { backend, session };
|
|
231
|
+
if (backend === "mysql") {
|
|
232
|
+
if (hostSet) {
|
|
233
|
+
const port = coords.tidbPort;
|
|
234
|
+
store.db = {
|
|
235
|
+
host,
|
|
236
|
+
port: port === undefined ? MYSQL_DEFAULT_PORT : port,
|
|
237
|
+
...(Object.hasOwn(coords, "tidbUser") ? { user: coords.tidbUser } : {}),
|
|
238
|
+
...(Object.hasOwn(runtime, "db") ? { database: runtime.db } : {}),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (Object.hasOwn(secretRefs, "tidbPasswordEnv"))
|
|
242
|
+
secretRefs.dbPasswordEnv = secretRefs.tidbPasswordEnv;
|
|
243
|
+
for (const [c, , k] of sqlCoordsPresent)
|
|
244
|
+
delete c[k];
|
|
245
|
+
}
|
|
246
|
+
else if (backend === "pg") {
|
|
247
|
+
if (sqlCoordsPresent.length > 0) {
|
|
248
|
+
return refuse(["env", "DB_BACKEND"], `env.DB_BACKEND=pg but the SQL coordinates are the MySQL family (coords.tidb* / runtime.db / secretRefs.tidbPasswordEnv, rendered as MYSQL_*) — the migration does not move a MySQL server's coordinates onto pg; declare runtime.store by hand`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
for (const [c, slot, k] of sqlCoordsPresent)
|
|
253
|
+
drop(c, slot, k); // 非 SQL 店:SQL 坐标在引擎里是「声明不用」的惰性件
|
|
254
|
+
}
|
|
255
|
+
const memory = {};
|
|
256
|
+
const engineRaw = env.MEMORY_ENGINE;
|
|
257
|
+
if (engineRaw !== undefined) {
|
|
258
|
+
const word = typeof engineRaw === "string" ? engineRaw.trim().toLowerCase() : undefined;
|
|
259
|
+
if (word !== undefined && MEMORY_ENGINE_ON_WORDS.includes(word))
|
|
260
|
+
memory.engine = "on";
|
|
261
|
+
else if (word !== undefined && MEMORY_ENGINE_OFF_WORDS.includes(word))
|
|
262
|
+
memory.engine = "off";
|
|
263
|
+
else if (word !== "")
|
|
264
|
+
return refuse(["env", "MEMORY_ENGINE"], "env.MEMORY_ENGINE is not an on/off word — the engine refuses to start with it");
|
|
265
|
+
delete env.MEMORY_ENGINE;
|
|
266
|
+
}
|
|
267
|
+
const memoryBackendRaw = env.MEMORY_ENGINE_BACKEND;
|
|
268
|
+
if (memoryBackendRaw !== undefined) {
|
|
269
|
+
if (!isMemoryBackendWord(memoryBackendRaw)) {
|
|
270
|
+
return refuse(["env", "MEMORY_ENGINE_BACKEND"], `env.MEMORY_ENGINE_BACKEND ${describeWord(memoryBackendRaw)} is not a memory backend (${MEMORY_ENGINE_BACKENDS.join(" | ")}) — the engine refuses to start with it`);
|
|
271
|
+
}
|
|
272
|
+
memory.backend = memoryBackendRaw;
|
|
273
|
+
delete env.MEMORY_ENGINE_BACKEND;
|
|
274
|
+
}
|
|
275
|
+
if (Object.keys(memory).length > 0)
|
|
276
|
+
store.memory = memory;
|
|
277
|
+
runtime.store = store;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// 被搬空的 env / secretEnv 整键去掉(它们是可选字段;留一个 `{}` 只是噪声)
|
|
281
|
+
if (isRec(w.env) && Object.keys(env).length === 0 && isRec(raw.env) && Object.keys(raw.env).length > 0)
|
|
282
|
+
delete w.env;
|
|
283
|
+
if (isRec(w.secretEnv) && Object.keys(secretEnv).length === 0 && isRec(raw.secretEnv) && Object.keys(raw.secretEnv).length > 0)
|
|
284
|
+
delete w.secretEnv;
|
|
285
|
+
// 终判 = 7.0.0 schema 本身(迁移不复述写闸规则)
|
|
286
|
+
const verdict = WorkerSpec.safeParse(w);
|
|
287
|
+
if (!verdict.success) {
|
|
288
|
+
const issues = verdict.error.issues;
|
|
289
|
+
const shown = issues.slice(0, 3).map((i) => `${formatConfigPath(i.path)}: ${i.message}`);
|
|
290
|
+
const more = issues.length > shown.length ? ` (+${issues.length - shown.length} more)` : "";
|
|
291
|
+
const first = issues[0];
|
|
292
|
+
return refuse(first === undefined ? [] : first.path, `after migration the worker is not a valid 7.0.0 WorkerSpec — ${shown.join("; ")}${more}`);
|
|
293
|
+
}
|
|
294
|
+
return { kind: "migrated", entry: w, dropped };
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* 迁移一份 workers 域**原始**文档(`{ workers: [...] }`)。不是这个形 ⇒ 原样返回(判形是 `parseDomain` 的事,迁移不抛)。
|
|
298
|
+
* 规则、幂等与 refused 的处置见文件头。
|
|
299
|
+
*/
|
|
300
|
+
export function migrateWorkersDomain(doc) {
|
|
301
|
+
if (!isRec(doc) || !Array.isArray(doc.workers))
|
|
302
|
+
return { doc, changed: [], dropped: [], refused: [] };
|
|
303
|
+
const workers = doc.workers;
|
|
304
|
+
const kept = [];
|
|
305
|
+
const changed = [];
|
|
306
|
+
const dropped = [];
|
|
307
|
+
const refused = [];
|
|
308
|
+
workers.forEach((entry, index) => {
|
|
309
|
+
const out = migrateEntry(entry, index);
|
|
310
|
+
switch (out.kind) {
|
|
311
|
+
case "unchanged":
|
|
312
|
+
kept.push(entry);
|
|
313
|
+
break;
|
|
314
|
+
case "migrated":
|
|
315
|
+
kept.push(out.entry);
|
|
316
|
+
changed.push(String(out.entry.name));
|
|
317
|
+
dropped.push(...out.dropped);
|
|
318
|
+
break;
|
|
319
|
+
case "refused":
|
|
320
|
+
refused.push(out.refusal);
|
|
321
|
+
break;
|
|
322
|
+
default: {
|
|
323
|
+
const unhandled = out;
|
|
324
|
+
throw new Error(`migrateWorkersDomain: unhandled outcome ${JSON.stringify(unhandled)}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
if (changed.length === 0 && refused.length === 0)
|
|
329
|
+
return { doc, changed: [], dropped: [], refused: [] };
|
|
330
|
+
return { doc: { ...doc, workers: kept }, changed, dropped, refused };
|
|
331
|
+
}
|