@sema-agent/settings-schema 14.8.0 → 15.1.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 +119 -0
- package/README.md +27 -1
- package/conformance/VERSION +1 -1
- package/conformance/cases/c59-s784-g3-k8s-missing-image/input.json +4 -1
- package/conformance/cases/c61-s784-g3-other-lane-key/input.json +1 -0
- package/conformance/cases/c64-s784-multitenant-local-store/input.json +2 -1
- package/dist/execution-lane-index.d.ts +11 -2
- package/dist/execution-lane-index.js +13 -2
- package/dist/execution-lanes.d.ts +2 -1
- package/dist/execution-lanes.js +6 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/remote-exec.d.ts +1 -1
- package/dist/remote-exec.js +4 -2
- package/dist/types.js +5 -5
- package/docs/API-SURFACE.md +3 -2
- package/llms-full.txt +8 -3
- package/llms.txt +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,125 @@
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
## 15.1.0 — 2026-10-28 — minor:车道坐标「空白按缺」的唯一判官 `isBlankCoordinate` 升为包根导出(加性:包根多一个名字,函数体与既有答案逐位不变)
|
|
12
|
+
|
|
13
|
+
> 档位(按发版纪律):**minor** —— 加性:新导出一个(包根 `isBlankCoordinate`;导出面登记表 包根 761 → 762 名,合计 1460 → 1461 名)。没有新子路径、没有删改的导出;既有导出的答案逐位不变(写闸读的仍是同一个函数对象)。无 BREAKING,无迁移动作。
|
|
14
|
+
> 15.0.0 节写的「判官 `isBlankCoordinate` 在包内私有、不进任何导出面」是 15.0.0 的事实;本版起它在包根,那一句不再成立(已发布的节不改,更正记在这里)。
|
|
15
|
+
|
|
16
|
+
- **`isBlankCoordinate(value: string): boolean`(包根,具名导出;函数体就是写闸一直在读的那一只,`src/execution-lane-index.ts`)**。必需坐标「未设 / 空串 / 只含空白」一律算缺,15.0.0 起写闸对所有车道、所有必需坐标同判这一条;判官此前在包内私有,
|
|
17
|
+
想在自己一侧问同一个问题的消费方只能手抄一份同义函数,两份迟早在某个空白字符上各说各话。现在包根给同一只:`import { isBlankCoordinate } from "@sema-agent/settings-schema"`。
|
|
18
|
+
契约(与写闸逐位同):入参是**已经有的字符串值**,空串或只含空白(`String.prototype.trim` 认的空白:空格 / 制表 / 换行 / 不换行空格 / 字节序标记 / 全角空格 …)⇒ `true`,带任何非空白字符 ⇒ `false`;
|
|
19
|
+
它**不 trim 值、不管值的格式**(`" sandboxes "` 不是空白);「这一键根本没设」由调用方先判(写闸对没设的键另有一句 `requires coords.<键>`,两种缺法同样被拒);
|
|
20
|
+
它只答「这个值是不是空白」,不管键是必需还是可选 —— 据它**拒不拒**一个坐标由调用方按键的必需 / 可选决定:写闸只对必需坐标据它拒;个别可选坐标上空串有语义(如 `K8S_RUNTIME_CLASS` 的空串 = 不用 Kata),可选坐标的空串照收。
|
|
21
|
+
索引模块 `execution-lane-index` 里其余的导出(车道表的派生索引、迁移键集、谓词)仍是包私有,主桶只具名取走这一个名字(不是 `export *`)。
|
|
22
|
+
三问 —— **谁需要**:在自己一侧判「必需坐标算不算缺」的消费方(例:服务端的必需 env 门、命令行):调同一只,不再各抄一份;
|
|
23
|
+
**谁受伤**:无 —— 新导出,没有既有读者;既有导出的答案逐位不变;**补偿**:无需。
|
|
24
|
+
钉:`test/blank-coordinate-export-15.1.0.test.ts`(引用:包根导出与包内写闸读的是同一个函数对象 · 真值表:`""` / `" "` / `"\t\n"` ⇒ 缺,`"a"` / `" a "` ⇒ 不缺,另含 trim 认的其余空白与真值 · 与写闸对 `K8S_NAMESPACE` 逐样本同答 ·
|
|
25
|
+
可选坐标的空串写闸照收 · 面:索引模块其余导出不在包根、登记表里该名只在包根一处且形为 value)、`test/api-surface.test.ts`(登记表 +1 行,与生成器输出逐字同)。
|
|
26
|
+
|
|
27
|
+
**自 v15.0.0 起运行期代码有改动的公开导出(机器重算;上界 —— 可达 ≠ 一定变,不可达 ⇒ 一定不变;本版新增的导出不入表)**
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
(无)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 15.0.0 — 2026-10-28 — major:k8s 车道的沙箱命名空间 `K8S_NAMESPACE` 由可选挪进必需(车道表的 k8s 行 · 本地文件契约 `RemoteExecSpec` 的 k8s 臂)· 写闸对必需坐标的值:空串或只含空白一律按缺(校验收紧三处:此前受理的声明从受理变成拒绝)
|
|
34
|
+
|
|
35
|
+
> 档位(按发版纪律):**major** —— 校验收紧三处,每一处都让此前受理的输入被拒:**B1** 车道表把 `K8S_NAMESPACE` 挪进 k8s 行的必需栏(缺它的 k8s worker 被写闸拒;存下来的 workers 文档里有这样一行,
|
|
36
|
+
> 整个 workers 域读不出);**B2** 本地文件契约 `RemoteExecSpec` 的 k8s 臂 `namespace` 由可选改必填(缺它的 `config.d/remote-exec.json` 被拒);**B3** 写闸对必需坐标的值:空串或只含空白一律按缺(所有车道、所有必需坐标同一只判官)。
|
|
37
|
+
> 发版纪律按消费方在**运行期**的可见性判档,不看 diff 大小。
|
|
38
|
+
> 一件事,一条律:**k8s 车道的沙箱命名空间是部署自己声明的坐标 —— 车道表不能再把它放在「可选」栏**。消费方按本表行拒启:服务端的必需 env 门按本表行 `requiredEnv ∪ requiredSecretEnv` 拒启,部署面的车道清单读同一行;
|
|
39
|
+
> 表此前把它写在 `optionalEnv`,写闸与这道门因此放行不带它的 k8s 声明:同一件事,表、写闸、文件契约各说各话。现在只有一种:`EXECUTION_LANES` 的 k8s 行 `requiredEnv` = `["K8S_SANDBOX_IMAGE", "K8S_NAMESPACE"]`,`optionalEnv` 去掉它;其余六行逐字不动。
|
|
40
|
+
> 导出面:包根 / `./types` / `./node` / `./execution-lanes` 的名字零增删;答案变的是常量 `EXECUTION_LANES` 的 k8s 行、读它的写闸与读函数,以及 `RemoteExecSpec` 的 k8s 臂(见节末机器重算表);判官 `isBlankCoordinate` 在包内私有、不进任何导出面。
|
|
41
|
+
> 一致性语料:条数不变;c59 / c61 / c64 三条拒因语料的输入各补一格 `coords.K8S_NAMESPACE`(它们要打的格不是命名空间,不补则各多一条拒因、不再「只因这一格被拒」),三条的拒绝态答案与金样一字不动;其余语料逐字同。
|
|
42
|
+
|
|
43
|
+
### BREAKING 清单(逐条三问 + 下游动作)
|
|
44
|
+
|
|
45
|
+
**B1 · k8s 车道的 `K8S_NAMESPACE` 是必需坐标**(零别名、零过渡读法、不自动补写)。读车道表这一格的地方都从同一张车道表派生,没有第二份手抄的车道必需键清单(本地文件契约上的同一件事是 B2):
|
|
46
|
+
写闸 `WorkerSpec` —— 缺 ⇒ issue `lane "k8s" requires coords.K8S_NAMESPACE (the engine refuses to start without it)`,`path` 指 `runtime.execution.coords.K8S_NAMESPACE`;给空串或纯空白 ⇒ `required coordinate K8S_NAMESPACE of lane "k8s" is empty or whitespace-only — it counts as absent, and the engine refuses to start without it`(空串此前作为可选坐标照收;纯空白见 B3);
|
|
47
|
+
存下来的 workers 文档的两只读函数(`parseDomainLoud` / `buildEffective`);一次性迁移 SS-M4 对没写命名空间的 6.x k8s 行的终判。缺键拒句(`lane "k8s" requires coords.<键> (the engine refuses to start without it)`)的措辞本版不改(它本来就说「缺了就起不了服」,现在表与它一致);空值拒句本版改了措辞(B3:`is empty — the engine reads an empty value as unset and refuses to start` → `is empty or whitespace-only — it counts as absent, and the engine refuses to start without it`),点名同一格、`path` 不变。
|
|
48
|
+
**谁需要**:沙箱 pod 住哪只有部署知道,没有哪一方替它猜;表、写闸、中心表单、服务端的必需 env 门与部署面的车道清单读同一张表 —— 表说必需,保存时就拦下,而不是等服务起来才发现。
|
|
49
|
+
**谁受伤**:
|
|
50
|
+
① 存下来的 workers 文档里 `lane` 是 `k8s` 而 `coords` 没有 `K8S_NAMESPACE`(或为空串)的行:整域读的两只读函数 `parseDomainLoud` 与 `buildEffective` 遇到它就抛 —— workers 域不逐条抢救
|
|
51
|
+
(丢一行 = 对账器把这台 worker 当成不再声明、去退役它),一行不合 = 整个 workers 文档读不出,拒句点名 `runtime.execution.coords.K8S_NAMESPACE`(响亮,不静默丢行);
|
|
52
|
+
按行读的 `readWorkersRows`(web-admin 的 workers 读口)则把这一行扣下(`held`,带原文与拒因,`field` 指到同一格),其余行照常进 `doc`;
|
|
53
|
+
② 保存 k8s worker 的写入席(`WorkerSpec.safeParse`、中心表单):不带命名空间的写入被拒;
|
|
54
|
+
③ 一次性迁移 SS-M4:没写命名空间的 6.x k8s 行(此前靠内置缺省命名空间)⇒ `refused` 点名同一格、行原样留着 —— 迁移不替运维猜命名空间;
|
|
55
|
+
④ 按本包车道表算「缺了就起不了服的键」的消费方(服务端的必需 env 门按本表行 `requiredEnv ∪ requiredSecretEnv` 拒启、部署面车道清单读同一行、壳的执行车道预检):k8s 的必需键多一名,提货后随表重算。
|
|
56
|
+
不受伤:本机 `config.d`(workers 是非便携域,本机腿不读它);其余六条车道;带齐两键的 k8s 行;语料里的 `c52`(合法 k8s 行本来就带 `K8S_NAMESPACE`)。
|
|
57
|
+
**补偿**:给每个 k8s 行补一格 `K8S_NAMESPACE`,值 = 部署里沙箱 pod 所在的命名空间;**此前靠引擎内置缺省命名空间 `sandboxes` 的部署写 `sandboxes` 即与升级前同**。
|
|
58
|
+
落点看行的形:现行形的行写 `runtime.execution.coords.K8S_NAMESPACE`;还没过 SS-M4 的 6.x 行(仍带 `env.REMOTE_EXEC`)写它自己的 `env.K8S_NAMESPACE` 再跑迁移(迁移把它搬进 `coords`;往 6.x 行里加 `runtime.execution` 是新旧两形并存,迁移拒、不替运维挑赢家)。
|
|
59
|
+
**部署序:先补键、后升级** —— 14.8.x 上 `K8S_NAMESPACE` 本来就是合法的可选坐标,现行形的存量文档可以在旧版本下先补齐,升级时没有新旧两版同时在读的窗。
|
|
60
|
+
**下游**:中心(web-admin)的 k8s 写入路径要带命名空间,存量 workers 文档先补键;服务端提货后必需键门与车道清单随表重算(清单由同一张表生成);壳提货后 k8s 车道的半组判定多一名;三方各自按能力表态制回帖。
|
|
61
|
+
|
|
62
|
+
**B2 · 本地文件契约 `RemoteExecSpec` 的 k8s 臂:`namespace` 由可选字段改必填**(`config.d/remote-exec.json` 里 `provider` 为 `k8s` 的那一臂;零别名、零过渡读法)。B1 说的那件事在文件契约面的另一份说法 ——
|
|
63
|
+
v14.8.0 时两处都说可选;B1 把车道表收成必需之后,文件契约若仍说可选,同一个部署声明沙箱 pod 住哪,读两个地方就得两个答案 —— 所以同版把文件契约收成同一个答案:也要求写。判据 = 非空串(与 `image` 同一只 `execStr`,含不许 NUL)。
|
|
64
|
+
入口同答:`RemoteExecSpec.safeParse` / `validateRemoteExec` / `parseRemoteExecDocument` 拒(issue 指到 `namespace`);`loadRemoteExec` 读到这样的文件抛(文件存在即赢、存在而坏即抛,不静默退回 host 默认);`writeRemoteExec` 不落盘。
|
|
65
|
+
**谁需要**:一件事一个答案 —— 文件契约与车道表对「命名空间要不要写」不再各说各话。
|
|
66
|
+
**谁受伤**:`config.d/remote-exec.json` 里 k8s 臂没写 `namespace` 的部署:读侧 `loadRemoteExec` 抛,写侧(配置编辑器经 `writeRemoteExec`)不带命名空间的 k8s 写入被拒。不受伤:其余五个 provider;带 `namespace` 的 k8s 文件。
|
|
67
|
+
**补偿**:补 `namespace`,值 = 部署里沙箱 pod 所在的命名空间;**此前靠引擎内置缺省命名空间 `sandboxes` 的部署写 `sandboxes` 即与升级前同**。14.8.x 下 `namespace` 本来就是合法的可选字段,先补后升级,没有新旧两版同时在读的窗。
|
|
68
|
+
**下游**:读写 `config.d/remote-exec.json` 的消费方(读 `loadRemoteExec`、写 `writeRemoteExec`),提货后按同一句话补。
|
|
69
|
+
|
|
70
|
+
**B3 · 写闸对必需坐标的值:空串或只含空白一律按缺**(所有车道、所有必需坐标同一只判官 `isBlankCoordinate`:值 `trim()` 之后为空;零别名、零过渡读法)。此前写闸只判 `=== ""`,`" "` 一类纯空白被当成「写了」放行 —— 保存成功、写了却等于没写。
|
|
71
|
+
拒句同 B1 的空串句(`required coordinate <键> of lane "<车道>" is empty or whitespace-only — …`),`path` 指到该坐标位。判官只判「是不是空白」,不 trim 值、不管值的格式(`" sandboxes "` 照过 —— 格式不在这一条的范围);可选坐标不走它(个别可选坐标上空串有语义,如 `K8S_RUNTIME_CLASS` 的空串 = 不用 Kata)。
|
|
72
|
+
**谁需要**:一个必需坐标写成纯空白,效果与没写一样;写闸放行它,就是保存时不拦、到用的时候才失败。
|
|
73
|
+
**谁受伤**:必需坐标(任一车道:`K8S_SANDBOX_IMAGE` / `K8S_NAMESPACE` / `SSH_HOST` / `SSH_USER` / `ADB_SERIAL` / `DOCKER_IMAGE`)写成纯空白的声明(病态输入,正常写入不会有);存下来的 workers 文档里有这样的行,处置与 B1 的缺键行相同(整域读函数抛、`readWorkersRows` 扣下并指到该格)。
|
|
74
|
+
**补偿**:拒因点名那一格 —— 填真值(必需坐标缺席同样被拒,删掉那一键不是出路)。
|
|
75
|
+
**下游**:同 B1。
|
|
76
|
+
|
|
77
|
+
### 钉
|
|
78
|
+
|
|
79
|
+
`test/k8s-namespace-required-15.0.0.test.ts`(四面:表行〔必需栏恰为两名、次序镜像在前、可选栏不再有它、归属索引的 required 位、别的车道必需栏一格没动〕· 写闸〔只给镜像 ⇒ 恰一条 issue 指到该格、给空串 ⇒ 「必需坐标为空」一条、两键都给 ⇒ 过、一键不给 ⇒ 两条〕·
|
|
80
|
+
存下来的 workers 文档〔`parseDomainLoud` / `buildEffective` 抛并点名该键、`readWorkersRows` 扣下这一行并把 `field` 指到该格、补键后同一份文档读进、没有 k8s 行的文档不受影响〕· 随包文字〔升级指引与本节写明迁移动作,README / `llms` 里没有一句把这一键说成可选〕);
|
|
81
|
+
B3 的钉在同一文件:必需坐标〔`K8S_NAMESPACE` 取空格 / 制表 / 换行 / U+00A0 / U+FEFF / 混合 ⇒ 各恰一条 issue 指到该坐标位 · 正控:`"sandboxes"` / `" sandboxes "` / `"Sandboxes"` / `"a/b"` 照过 · 每条车道的每个必需坐标〔从表现算〕写成纯空白 ⇒ 都有 issue 指到自己的坐标位 · 可选坐标纯空白照收 · 结构:写闸读判官、源里没有 `value === ""` 一臂 · 存下来的文档同缺键行〕;
|
|
82
|
+
B2 的钉在同一文件:本地文件契约面〔k8s 臂必填字段恰为 `provider` / `image` / `namespace`(逐字段问 `undefined` 过不过,不手抄)· 只给镜像 ⇒ 恰一条 issue 指到 `namespace` · `validateRemoteExec` / `parseRemoteExecDocument` / `writeRemoteExec` / `loadRemoteExec` 同答 · 补键后照读 · 别的 provider 不受影响〕;
|
|
83
|
+
`test/remote-exec.test.ts` / `test/remote-exec-migration-11.3.0.test.ts` / `test/url-shape-11.0.0.test.ts` / `test/custom-issue-params-11.3.0.test.ts` / `test/file-edit.test.ts`(k8s 臂夹具补 `namespace`,各格仍只因自己要打的那一处被拒);
|
|
84
|
+
`test/execution-lanes-7.0.0.test.ts`(两处镜像钉改成现状:k8s 必需键并集含它、可选栏逐行快照去掉它;钉的注释不再说「引擎 `REQUIRED_ENVS_BY_LANE` 逐字」—— 服务端已直读本表);
|
|
85
|
+
`test/conformance-workers-7.0.0.test.ts`(三条拒因语料「issue 恰一条」原样绿);`test/one-time-migration-ss-m4-workers.test.ts`(没写命名空间的 6.x k8s 行 ⇒ refused 点名该格、文档同一引用;多租户格补命名空间);`test/file-edit.test.ts`(基准 worker 补命名空间)。
|
|
86
|
+
|
|
87
|
+
**自 v14.8.0 起运行期代码有改动的公开导出(机器重算;上界 —— 可达 ≠ 一定变,不可达 ⇒ 一定不变;本版新增的导出不入表)**
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
DOMAINS [., ./types]
|
|
91
|
+
DOMAIN_SCHEMAS [., ./types]
|
|
92
|
+
EXECUTION_LANES [., ./execution-lanes]
|
|
93
|
+
FileConfigStore [./node]
|
|
94
|
+
NON_PORTABLE_DOMAINS [./conformance]
|
|
95
|
+
RESERVED_WORKER_ENV [., ./types]
|
|
96
|
+
RemoteExecSpec [., ./remote-exec]
|
|
97
|
+
WorkerSpec [., ./types]
|
|
98
|
+
WorkersConfig [., ./types]
|
|
99
|
+
buildEffective [., ./config-fns]
|
|
100
|
+
deriveLocalExpectation [./conformance]
|
|
101
|
+
deriveWireExpectation [./conformance]
|
|
102
|
+
domainFilePath [./node]
|
|
103
|
+
emptyEffective [., ./config-fns]
|
|
104
|
+
foreignKeysOf [./conformance]
|
|
105
|
+
isDomainName [., ./types]
|
|
106
|
+
loadLocalConfig [./node]
|
|
107
|
+
loadRemoteExec [./node]
|
|
108
|
+
migrateConfigDirectory [./node]
|
|
109
|
+
migrateConfigDocuments [./node]
|
|
110
|
+
migrateEffectiveSnapshot [./one-time-migrations]
|
|
111
|
+
migrateEnvModelsDecision [./node]
|
|
112
|
+
migrateStoredDocuments [./one-time-migrations]
|
|
113
|
+
parseDomain [., ./config-fns]
|
|
114
|
+
parseDomainLoud [., ./config-fns]
|
|
115
|
+
parseDomainSalvaged [., ./config-fns]
|
|
116
|
+
planEnvModelTableWrite [.]
|
|
117
|
+
readBundle [./node]
|
|
118
|
+
readEffectiveWire [., ./config-fns]
|
|
119
|
+
readWorkersRows [.]
|
|
120
|
+
resolveLegExpectation [./conformance]
|
|
121
|
+
runConformance [./conformance]
|
|
122
|
+
urlSeatPatterns [./one-time-migrations]
|
|
123
|
+
validateDomain [./node]
|
|
124
|
+
validateRemoteExec [./node]
|
|
125
|
+
writeBundle [./node]
|
|
126
|
+
writeDomainFile [./node]
|
|
127
|
+
writeRemoteExec [./node]
|
|
128
|
+
```
|
|
129
|
+
|
|
11
130
|
## 14.8.0 — 2026-10-27 — minor:配置根上的两份根级文档(env 模型表被目录顶替的决定文档 `config.env-models.json` 与按 id 一份的迁移运行记录)· 两只读者 `readEnvModelsDecision` / `readMigrationMarker` 与决定文件的唯一写函数 `writeEnvModelsDecision`(`./node`)· 一次性迁移 SS-M11(`migrateEnvModelsDecision`)· 目录管不管模型面的原文判官 `catalogManagesModelPlane` 与计数 `enabledEntryCount` · env 模型表的一份读法与写者的计划助手(`readEnvModelSlots` · `buildEnvModelBaseline` · `planEnvModelTableWrite`)· 两只模型面组拒码 `CATALOG_REPLACES_ENV_MODELS_CODE` / `SUMMARIZE_MODEL_MISSING_CODE` · 中心引用解析口 `resolveCenterRefs` 与记录读器 `readPlainStringRecord` · MCP 条目的 `sessionMetaKey` / `startupTimeoutMs`(解析透明)· MCP 服务器名保留前缀 `REMOTE_MCP_SERVER_NAME_PREFIX` 与请求面判官一对 `isUsableMcpServerName` / `describeBadMcpServerName` · 引擎钉 7.58.0 与上游漂移门 ⑧ / ⑪
|
|
12
131
|
|
|
13
132
|
> 档位(按发版纪律):**minor** —— 加性:新导出(包根 +53 名,其中两只组拒码常量同时在 `./fleet` 上;`./node` +9 名。包根:根级文档的名字、闭集答案表与读数身份拼法、原文判官两只、env 模型表读法与计划助手族、两只组拒码常量、中心引用解析口 `resolveCenterRefs` 与它的两张词表和读数型、记录读器 `readPlainStringRecord` 与它的原因词表 / 保留成员名 / 读数型、MCP 服务器名的保留前缀 `REMOTE_MCP_SERVER_NAME_PREFIX` 与请求面 MCP 名判官一对 `isUsableMcpServerName` / `describeBadMcpServerName`;`./node`:决定文档与运行记录的两只读者、一只写函数、一次性迁移 SS-M11 的迁移体与它的 I/O 座;导出面登记表合计〔同名在多条子路径上各计一次〕1396 → 1460 名);
|
package/README.md
CHANGED
|
@@ -47,6 +47,32 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
|
47
47
|
> zod 3, nested inside every zod-4 host). The move to zod 4 did not change what any schema accepts or produces —
|
|
48
48
|
> the conformance corpus reads byte-identically on both — only the wording of validation errors (see CHANGELOG 11.0.0).
|
|
49
49
|
|
|
50
|
+
## 升级指引:15.0.x → 15.1.0(加性,零动作可升)
|
|
51
|
+
|
|
52
|
+
包根多一个导出 `isBlankCoordinate(value: string): boolean` —— 车道坐标「空白按缺」的唯一判官:空串或只含空白 ⇒ `true`(写闸对必需坐标一直读的就是它),不 trim 值、不管值的格式;
|
|
53
|
+
「这一键没设」由调用方先判;据它拒不拒由调用方按键的必需 / 可选决定(写闸只对必需坐标据它拒,可选坐标的空串照收)。在自己一侧判「必需坐标算不算缺」的消费方 import 它,退役手抄的同义函数。导出面只多这一个名字,既有导出的答案逐位不变。逐条见 CHANGELOG 15.1.0。
|
|
54
|
+
|
|
55
|
+
## 升级指引:14.8.x → 15.0.0(BREAKING:k8s 车道的 `K8S_NAMESPACE` 由可选变必需 —— 车道表与本地文件契约两处;必需坐标的纯空白值按缺)
|
|
56
|
+
|
|
57
|
+
- **改了什么**:车道表 `EXECUTION_LANES` 的 k8s 行把 `K8S_NAMESPACE`(沙箱 pod 所在的命名空间)从 `optionalEnv` 挪进 `requiredEnv`(现为 `["K8S_SANDBOX_IMAGE", "K8S_NAMESPACE"]`)。
|
|
58
|
+
其余六行逐字不动,导出面名字零增删。沙箱 pod 住哪是部署自己的声明,表不替它留缺省:服务端的必需 env 门按本表行 `requiredEnv ∪ requiredSecretEnv` 拒启,部署面的车道清单读同一行;
|
|
59
|
+
此前表把它写在可选栏、这道门放行不带它的 k8s 声明,现在车道表(写闸、归属索引与迁移终判也读它)是唯一来源。零别名、零过渡读法。
|
|
60
|
+
- **谁会红**:① 存下来的 workers 文档里,`runtime.execution.lane` 是 `k8s` 而 `coords` 没有 `K8S_NAMESPACE`(或为空串)的行 —— workers 域整域读、不逐条抢救,
|
|
61
|
+
一行不合 = 整个 workers 文档读不出(`buildEffective` / `parseDomainLoud` 抛,拒句点名 `runtime.execution.coords.K8S_NAMESPACE`),不是静默丢掉那一行;
|
|
62
|
+
按行读的 `readWorkersRows`(web-admin 的 workers 读口)把这一行扣下(`held`,带原文与拒因,`field` 指到同一格)、其余行照常进 `doc`;
|
|
63
|
+
② 保存新 k8s worker 的写入席(`WorkerSpec.safeParse` / 中心表单):不带命名空间的写入现在被拒,拒句 `lane "k8s" requires coords.K8S_NAMESPACE (the engine refuses to start without it)`;
|
|
64
|
+
③ 一次性迁移 SS-M4 遇到没写命名空间的 6.x k8s 行 ⇒ `refused` 点名同一格、行原样留着(迁移不替运维猜命名空间)。本机 `config.d` 不读 workers 文档,本机腿不受影响。
|
|
65
|
+
- **要做的**:给每个 k8s 行补一格 `K8S_NAMESPACE`,值 = 部署里沙箱 pod 所在的命名空间。**此前靠引擎内置缺省命名空间 `sandboxes` 的部署,写 `sandboxes` 即与升级前同。**
|
|
66
|
+
落点看行的形:现行形的行(有 `runtime.execution`)写在 `runtime.execution.coords.K8S_NAMESPACE`;还没过一次性迁移 SS-M4 的 6.x 行(仍带 `env.REMOTE_EXEC`)写在它自己的 `env.K8S_NAMESPACE`,再跑迁移,迁移把它搬进 `coords`
|
|
67
|
+
(往 6.x 行里加 `runtime.execution` 会造成新旧两形并存,迁移拒、不替运维挑赢家)。
|
|
68
|
+
- **文件契约面的同一件事**:本地文件契约 `RemoteExecSpec` 的 k8s 臂(`config.d/remote-exec.json`)的 `namespace` 由可选字段改必填 —— 没写的文件 `loadRemoteExec` 抛(文件存在即赢、存在而坏即抛)、`validateRemoteExec` / `parseRemoteExecDocument` 拒、`writeRemoteExec` 不落盘。
|
|
69
|
+
补 `namespace`(值 = 部署里沙箱 pod 所在的命名空间;此前靠内置缺省的写 `sandboxes`)。14.8.x 下该字段本就合法,先补后升级。
|
|
70
|
+
- **必需坐标的值(B3)**:写闸对所有车道、所有必需坐标同判一只判官 —— 空串或只含空白一律按缺(此前只判空串),拒因点名那一格(`required coordinate <键> of lane "<车道>" is empty or whitespace-only`)。
|
|
71
|
+
只影响把必需坐标写成纯空白的声明(病态输入);补偿 = 填真值(必需坐标缺席同样被拒,删掉那一键不是出路)。判官只判「是不是空白」,不 trim 值、不管值的格式。
|
|
72
|
+
- **部署序**:**先补键、后升级** —— 14.8.x 上 `K8S_NAMESPACE` 本来就是合法的可选坐标,现行形的存量文档可以在旧版本下先补齐,升级时不存在新旧两版同时在读的窗。
|
|
73
|
+
找存量:列出 `runtime.execution.lane` 为 `k8s` 且 `coords.K8S_NAMESPACE` 缺席或为空的 worker 行,以及带 `env.REMOTE_EXEC` 为 `k8s` 而 `env.K8S_NAMESPACE` 缺席或为空的 6.x 行。
|
|
74
|
+
- 逐条三问与钉见 CHANGELOG 15.0.0。
|
|
75
|
+
|
|
50
76
|
## 升级指引:14.7.x → 14.8.0(加性:配置根上的两份根级文档 · env 模型表的一份读法与写者的计划助手 · 一次性迁移 SS-M11)
|
|
51
77
|
|
|
52
78
|
- **新面**:配置根(持有 `config.d` 的那个目录)上多两份**根级文档** —— `config.env-models.json`(`{"envModels":"replaced"|"kept"}`:一份管模型面的
|
|
@@ -264,7 +290,7 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
|
264
290
|
`MEMORY_ENGINE_BACKENDS`. Web-admin's write gate (this package's `WorkerSpec.safeParse`), web-admin's env
|
|
265
291
|
renderer and the engine's boot checks all read that one table, so they cannot drift. A worker declares
|
|
266
292
|
`runtime.execution = { lane, coords, secretRefs }` (non-secret coordinates as literals, secrets as host env-NAMEs,
|
|
267
|
-
keys ⊆ that lane's columns, required ones present and
|
|
293
|
+
keys ⊆ that lane's columns, required ones present and not blank — an empty or whitespace-only value counts as absent) and `runtime.store = { backend, session, memory,
|
|
268
294
|
db? }` (+ `secretRefs.dbPasswordEnv`); an enabled worker must declare both. Cross-field refusals live in the
|
|
269
295
|
schema as zod issues whose `path` names the field to change and whose message carries the way out: a
|
|
270
296
|
multi-tenant worker (`requirePrincipal`) on a lane that does not separate tenants (outlets computed from the
|
package/conformance/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
15.1.0
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 车道表({@link EXECUTION_LANES})的两只**派生索引** + 「迁移能搬哪些键」的**一份**键集,
|
|
3
3
|
* 供 `types.ts`(写闸)与 `one-time-migrations.ts`(一次性迁移 SS-M4)同读。
|
|
4
|
-
*
|
|
4
|
+
* 本模块是包内私有的:不进 `package.json` 的 `exports`(与 `secret-scan.ts` 同一姿势),主桶也只具名取走一个名字 ——
|
|
5
|
+
* 必需坐标算不算「缺」的唯一判官 {@link isBlankCoordinate}(消费方要在自己一侧问同一个问题,调包根上这一只,不抄第二份);
|
|
6
|
+
* 其余导出(索引、键集、谓词)仍不进主桶 —— 消费方只拿表本身,它们是本包的实现细节。
|
|
5
7
|
* 索引只从表派生,不另存任何词:表改了,这里跟着变。
|
|
6
8
|
*
|
|
7
9
|
* ## 迁移指路(7.0.1;11.0.0 起指路句住 `retired-form.ts`)
|
|
@@ -22,6 +24,13 @@ export interface LaneEnvOwner {
|
|
|
22
24
|
}
|
|
23
25
|
/** 车道 env 名 → 归属。四栏两两不交、跨车道不交(`test/execution-lanes-7.0.0.test.ts` 钉),所以每个名恰好一个归属。 */
|
|
24
26
|
export declare const LANE_ENV_OWNER: ReadonlyMap<string, LaneEnvOwner>;
|
|
27
|
+
/**
|
|
28
|
+
* 车道坐标的**唯一判官**:一个必需坐标「未设 / 空串 / 只含空白」一律算缺。本函数判其中的后两种,且只看**已经有的字符串值**:空串,或只含空白(`String.prototype.trim` 认的空白:空格 / 制表 / 换行 / 不换行空格 / 字节序标记 …)⇒ `true`;
|
|
29
|
+
* 「这一键根本没设」由调用方先判(写闸对没设的键另有一句 `requires coords.<键>`,两种缺法同样被拒)。
|
|
30
|
+
* 写闸对所有车道、所有必需坐标同判这一只;它只判「是不是空白」,不 trim 值、不管值的格式(格式不在这一条的范围:`" sandboxes "` 不是空白)。
|
|
31
|
+
* 它只答「这个值是不是空白」,不管这个键是必需还是可选:据它**拒不拒**一个坐标,由调用方按键的必需 / 可选决定 —— 写闸只对必需坐标据它拒;个别可选坐标上空串有语义(如 `K8S_RUNTIME_CLASS` 的空串 = 不用 Kata),可选坐标的空串照收。
|
|
32
|
+
*/
|
|
33
|
+
export declare function isBlankCoordinate(value: string): boolean;
|
|
25
34
|
/** 一个车道 env 名在 WorkerSpec 里的**唯一写位**(保留键的拒因指路用它)。 */
|
|
26
35
|
export declare function laneEnvSeat(name: string, owner: LaneEnvOwner): string;
|
|
27
36
|
/** 车道块的两个特殊键(不在车道表里,但归车道块:迁移把 `REMOTE_EXEC` 搬成 `runtime.execution.lane`、`REQUIRE_EXECUTION_LANE` 丢弃)。 */
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 车道表({@link EXECUTION_LANES})的两只**派生索引** + 「迁移能搬哪些键」的**一份**键集,
|
|
3
3
|
* 供 `types.ts`(写闸)与 `one-time-migrations.ts`(一次性迁移 SS-M4)同读。
|
|
4
|
-
*
|
|
4
|
+
* 本模块是包内私有的:不进 `package.json` 的 `exports`(与 `secret-scan.ts` 同一姿势),主桶也只具名取走一个名字 ——
|
|
5
|
+
* 必需坐标算不算「缺」的唯一判官 {@link isBlankCoordinate}(消费方要在自己一侧问同一个问题,调包根上这一只,不抄第二份);
|
|
6
|
+
* 其余导出(索引、键集、谓词)仍不进主桶 —— 消费方只拿表本身,它们是本包的实现细节。
|
|
5
7
|
* 索引只从表派生,不另存任何词:表改了,这里跟着变。
|
|
6
8
|
*
|
|
7
9
|
* ## 迁移指路(7.0.1;11.0.0 起指路句住 `retired-form.ts`)
|
|
@@ -24,6 +26,15 @@ export const LANE_ENV_OWNER = new Map(EXECUTION_LANES.flatMap((r) => {
|
|
|
24
26
|
const own = (names, secret, required) => names.map((k) => [k, { lane: r.lane, secret, required }]);
|
|
25
27
|
return [...own(r.requiredEnv, false, true), ...own(r.optionalEnv, false, false), ...own(r.requiredSecretEnv, true, true), ...own(r.optionalSecretEnv, true, false)];
|
|
26
28
|
}));
|
|
29
|
+
/**
|
|
30
|
+
* 车道坐标的**唯一判官**:一个必需坐标「未设 / 空串 / 只含空白」一律算缺。本函数判其中的后两种,且只看**已经有的字符串值**:空串,或只含空白(`String.prototype.trim` 认的空白:空格 / 制表 / 换行 / 不换行空格 / 字节序标记 …)⇒ `true`;
|
|
31
|
+
* 「这一键根本没设」由调用方先判(写闸对没设的键另有一句 `requires coords.<键>`,两种缺法同样被拒)。
|
|
32
|
+
* 写闸对所有车道、所有必需坐标同判这一只;它只判「是不是空白」,不 trim 值、不管值的格式(格式不在这一条的范围:`" sandboxes "` 不是空白)。
|
|
33
|
+
* 它只答「这个值是不是空白」,不管这个键是必需还是可选:据它**拒不拒**一个坐标,由调用方按键的必需 / 可选决定 —— 写闸只对必需坐标据它拒;个别可选坐标上空串有语义(如 `K8S_RUNTIME_CLASS` 的空串 = 不用 Kata),可选坐标的空串照收。
|
|
34
|
+
*/
|
|
35
|
+
export function isBlankCoordinate(value) {
|
|
36
|
+
return value.trim() === "";
|
|
37
|
+
}
|
|
27
38
|
/** 一个车道 env 名在 WorkerSpec 里的**唯一写位**(保留键的拒因指路用它)。 */
|
|
28
39
|
export function laneEnvSeat(name, owner) {
|
|
29
40
|
return `runtime.execution.${owner.secret ? "secretRefs" : "coords"}.${name}`;
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* · `tenantSeparation`:同文件 `executionLaneCaps` 每臂的 `tenantSeparation` 位(:209 host / :230 e2b / :249 k8s /
|
|
18
18
|
* :269 ssh / :288 adb / :308 local-docker / :327 device)。`tenantSeparationBasis`(11.0.0):分租户的那几臂凭什么分 ——
|
|
19
19
|
* e2b / k8s / local-docker 每任务一只隔离目标(`isolation`),device 是发起者自己的设备(`principal-binding`)。
|
|
20
|
-
* · `requiredEnv ∪ requiredSecretEnv
|
|
20
|
+
* · `requiredEnv ∪ requiredSecretEnv`:这条车道缺了就起不了服的键。最初逐字抄自 `src/config.ts` 的手写表 `REQUIRED_ENVS_BY_LANE`(:1054),
|
|
21
|
+
* 那张手写表已退役 —— 服务端的必需 env 门按本表行的这两栏拒启,部署面的车道清单读同一行,所以今天**本表就是**必需键的唯一来源。按 `src/config-catalog.ts`
|
|
21
22
|
* 该键那一行的值类型分栏 —— 类型 `secret` / `opaque`(目录自己「绝不回显值」的两类)进 `*SecretEnv`,其余进 `*Env`。
|
|
22
23
|
* · `optionalEnv ∪ optionalSecretEnv`:`src/config.ts` `parseOrchestrationDomain` 的 `REMOTE_EXEC` 三元链里**该车道那一臂**
|
|
23
24
|
* 读的 env 名(:3925 e2b / :3957 k8s / :3991 ssh / :4006 adb / :4014 host / :4026 local-docker / :4055 device;
|
package/dist/execution-lanes.js
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* · `tenantSeparation`:同文件 `executionLaneCaps` 每臂的 `tenantSeparation` 位(:209 host / :230 e2b / :249 k8s /
|
|
18
18
|
* :269 ssh / :288 adb / :308 local-docker / :327 device)。`tenantSeparationBasis`(11.0.0):分租户的那几臂凭什么分 ——
|
|
19
19
|
* e2b / k8s / local-docker 每任务一只隔离目标(`isolation`),device 是发起者自己的设备(`principal-binding`)。
|
|
20
|
-
* · `requiredEnv ∪ requiredSecretEnv
|
|
20
|
+
* · `requiredEnv ∪ requiredSecretEnv`:这条车道缺了就起不了服的键。最初逐字抄自 `src/config.ts` 的手写表 `REQUIRED_ENVS_BY_LANE`(:1054),
|
|
21
|
+
* 那张手写表已退役 —— 服务端的必需 env 门按本表行的这两栏拒启,部署面的车道清单读同一行,所以今天**本表就是**必需键的唯一来源。按 `src/config-catalog.ts`
|
|
21
22
|
* 该键那一行的值类型分栏 —— 类型 `secret` / `opaque`(目录自己「绝不回显值」的两类)进 `*SecretEnv`,其余进 `*Env`。
|
|
22
23
|
* · `optionalEnv ∪ optionalSecretEnv`:`src/config.ts` `parseOrchestrationDomain` 的 `REMOTE_EXEC` 三元链里**该车道那一臂**
|
|
23
24
|
* 读的 env 名(:3925 e2b / :3957 k8s / :3991 ssh / :4006 adb / :4014 host / :4026 local-docker / :4055 device;
|
|
@@ -71,16 +72,17 @@ const LANE_ROWS = {
|
|
|
71
72
|
optionalSecretEnv: ["E2B_SANDBOX_ENV"],
|
|
72
73
|
},
|
|
73
74
|
/** server `config.ts` :3957 臂;`K8S_TOKEN` 目录 :412 类型 `secret`、`K8S_CA_CERT_B64` :403 类型 `opaque`;
|
|
74
|
-
* `execution-lane-caps.ts` :249 `supported`(每任务一只 pod)。`MINIO_*` 刻意不收(见文件头)。
|
|
75
|
+
* `execution-lane-caps.ts` :249 `supported`(每任务一只 pod)。`MINIO_*` 刻意不收(见文件头)。
|
|
76
|
+
* `K8S_NAMESPACE`(沙箱 pod 所在的命名空间)是**必需**坐标:pod 住哪是部署自己的声明,表不替它留缺省;服务端的必需 env 门按本行 `requiredEnv` 拒启,
|
|
77
|
+
* 所以这里没有「可选」一说。 */
|
|
75
78
|
k8s: {
|
|
76
79
|
tenantSeparation: "supported",
|
|
77
80
|
tenantSeparationBasis: "isolation",
|
|
78
|
-
requiredEnv: ["K8S_SANDBOX_IMAGE"],
|
|
81
|
+
requiredEnv: ["K8S_SANDBOX_IMAGE", "K8S_NAMESPACE"],
|
|
79
82
|
requiredSecretEnv: [],
|
|
80
83
|
optionalEnv: [
|
|
81
84
|
"K8S_API_URL",
|
|
82
85
|
"K8S_INSECURE_TLS",
|
|
83
|
-
"K8S_NAMESPACE",
|
|
84
86
|
"K8S_RUNTIME_CLASS",
|
|
85
87
|
"K8S_MOUNT_PATH",
|
|
86
88
|
"K8S_TIMEOUT_MS",
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -47,6 +47,7 @@ export * from "./control-and-bidi.js"; // the one-line echo strip set (C0 + DEL
|
|
|
47
47
|
export * from "./mcp-request-entry.js"; // 14.0.0: the request-plane MCP entry judges (isUsableWireName / isContentOriginWord / readMcpIdentityHeaders / readRequestMcpTransport + the value-shape describers) — one reading for the service legs and the shell pre-check, pure/browser-safe
|
|
48
48
|
export { REMOTE_MCP_SERVER_NAME_PREFIX } from "./mcp-server-name.js"; // the MCP server-name prefix reserved for the remote MCP servers a device executor offers (mount name = prefix + the executor's server name); the predicate and its sentence stay package-private — the request plane's MCP name judges (isUsableMcpServerName / describeBadMcpServerName) read them; the config plane's McpServerSpec.name accepts the prefix (refusing it would be a validation tightening — a major), pure/browser-safe
|
|
49
49
|
export * from "./center-refs.js"; // resolveCenterRefs(refs, lookup) — THE center-roster reference resolver: one verdict word per member (missing_name / not_a_string / read_threw, CENTER_REF_FAULTS), one whole verdict (resolved / unresolved / refs_unreadable, CENTER_REFS_VERDICTS); the refs record is read by the public readPlainStringRecord, the lookup is the caller's, it reads no process.env; pure/browser-safe
|
|
50
|
+
export { isBlankCoordinate } from "./execution-lane-index.js"; // THE lane-coordinate blank judge (empty or whitespace-only = absent; the write gate and every consumer ask this one function; whether a key is set, and whether an optional key may be empty, stays the caller's call); named: the lane tables' derived indexes stay package-private, pure/browser-safe
|
|
50
51
|
// NOTE: the Node-fs-backed helpers (FileConfigStore + the file editor) are NOT in this barrel — they pull
|
|
51
52
|
// `node:fs`/`node:path` and would break a browser/client bundle that only wants the contract (e.g.
|
|
52
53
|
// web-admin's client components import the zod types through here). Import them from the Node-only
|
package/dist/remote-exec.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ export declare const RemoteExecSpec: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
63
63
|
tokenEnv: z.ZodOptional<z.ZodString>;
|
|
64
64
|
caCertEnv: z.ZodOptional<z.ZodString>;
|
|
65
65
|
insecureTls: z.ZodOptional<z.ZodBoolean>;
|
|
66
|
-
namespace: z.
|
|
66
|
+
namespace: z.ZodString;
|
|
67
67
|
runtimeClass: z.ZodOptional<z.ZodString>;
|
|
68
68
|
mountPath: z.ZodOptional<z.ZodString>;
|
|
69
69
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
package/dist/remote-exec.js
CHANGED
|
@@ -84,7 +84,7 @@ export const RemoteExecSpec = z.discriminatedUnion("provider", [
|
|
|
84
84
|
/** Which of THIS process's env-NAMEs to forward into the sandbox (service: E2B_SANDBOX_ENV, a CSV of NAMEs). */
|
|
85
85
|
sandboxEnv: z.array(ENV_NAME).optional(),
|
|
86
86
|
}),
|
|
87
|
-
// ── k8s: per-task Kata pod (service provider "k8s"; REMOTE_EXEC=k8s + K8S_SANDBOX_IMAGE).
|
|
87
|
+
// ── k8s: per-task Kata pod (service provider "k8s"; REMOTE_EXEC=k8s + K8S_SANDBOX_IMAGE + K8S_NAMESPACE).
|
|
88
88
|
z.object({
|
|
89
89
|
provider: z.literal("k8s"),
|
|
90
90
|
/** Sandbox container image (service: K8S_SANDBOX_IMAGE). Required so a run is reproducible. */
|
|
@@ -97,7 +97,9 @@ export const RemoteExecSpec = z.discriminatedUnion("provider", [
|
|
|
97
97
|
/** env-NAME holding the base64 CA cert (service: K8S_CA_CERT_B64). NEVER the cert bytes. */
|
|
98
98
|
caCertEnv: ENV_NAME.optional(),
|
|
99
99
|
insecureTls: z.boolean().optional(),
|
|
100
|
-
namespace:
|
|
100
|
+
/** The namespace the sandbox pods live in (service: K8S_NAMESPACE). Required — the deployment declares where its pods run and nothing here guesses one;
|
|
101
|
+
* the execution-lane table lists K8S_NAMESPACE as a required coordinate of the k8s lane, and this file contract says the same. */
|
|
102
|
+
namespace: execStr,
|
|
101
103
|
runtimeClass: execStr.optional(),
|
|
102
104
|
mountPath: execStr.optional(),
|
|
103
105
|
timeoutMs: INTEGER.positive().optional(),
|
package/dist/types.js
CHANGED
|
@@ -30,7 +30,7 @@ import { PREVIOUS_WEB_ADMIN_ENV } from "./previous-web-admin-env.js"; // the pre
|
|
|
30
30
|
import { retiredEnvRefusal } from "./retired-envs.js"; // the worker env write gate asks the retired-env tombstone table too (one table, one refusal sentence)
|
|
31
31
|
import { Sha256Digest } from "./sha256.js"; // the one `sha256:<hex>` digest form
|
|
32
32
|
import { EXECUTION_LANES, EXECUTION_LANE_WORDS, MEMORY_ENGINE_BACKENDS, SESSION_BACKENDS, STORE_BACKENDS, retiredLaneWordRefusal } from "./execution-lanes.js"; // 7.0.0 M-R2: the lane / store vocabulary (one exported table)
|
|
33
|
-
import { LANE_ENV_OWNER, isMigratableEnvKey, laneEnvSeat, laneRowOf } from "./execution-lane-index.js"; // package-private indexes derived from that table (
|
|
33
|
+
import { LANE_ENV_OWNER, isBlankCoordinate, isMigratableEnvKey, laneEnvSeat, laneRowOf } from "./execution-lane-index.js"; // package-private indexes derived from that table (isBlankCoordinate is also named in the barrel; the rest are not)
|
|
34
34
|
import { migrationSignpost, retiredFormSentence, retiredKey } from "./retired-form.js"; // 11.0.0: the ONE retired-form sentence + key tombstone (package-private)
|
|
35
35
|
import { retiredRoleWordsRefusal } from "./retired-role-words.js"; // 12.0.0: the retired role-word tombstone (package-private; SS-M8)
|
|
36
36
|
import { buildQuotedIssue, fixed } from "./quoted-issue.js"; // THE minting point of custom issues (package-private)
|
|
@@ -1889,8 +1889,8 @@ const nonSecretLiteral = (minLength) => z
|
|
|
1889
1889
|
/**
|
|
1890
1890
|
* `runtime.execution` —— 这台 worker 的执行车道(渲染:`REMOTE_EXEC=<lane>` + `REQUIRE_EXECUTION_LANE=true` + coords / secretRefs 逐键)。
|
|
1891
1891
|
* - `lane`:{@link EXECUTION_LANE_WORDS} 之一。没有「无手」词。
|
|
1892
|
-
* - `coords`:该车道的**非密坐标**(服务变量名 → 字面值),键 ⊆ 表[lane] 的 `requiredEnv ∪ optionalEnv
|
|
1893
|
-
* (
|
|
1892
|
+
* - `coords`:该车道的**非密坐标**(服务变量名 → 字面值),键 ⊆ 表[lane] 的 `requiredEnv ∪ optionalEnv`;必需键在场且不是空白
|
|
1893
|
+
* (空串或只含空白按缺 —— 唯一判官 `isBlankCoordinate`)。可选键的空串照收(例:`K8S_RUNTIME_CLASS=""` 在引擎里是「不用 Kata」)。
|
|
1894
1894
|
* - `secretRefs`:该车道的**密钥**(服务变量名 → 宿主 env-NAME),键 ⊆ 表[lane] 的 `requiredSecretEnv ∪ optionalSecretEnv`,必需键在场。
|
|
1895
1895
|
* 别车道的键 ⇒ 拒并点名它属于哪条车道;本车道的密钥写进 coords / 坐标写进 secretRefs ⇒ 拒并指路另一栏。
|
|
1896
1896
|
*/
|
|
@@ -1925,8 +1925,8 @@ const ExecutionBlock = z
|
|
|
1925
1925
|
return true;
|
|
1926
1926
|
};
|
|
1927
1927
|
for (const [key, value] of Object.entries(x.coords)) {
|
|
1928
|
-
if (judgeKey("coords", key) && value
|
|
1929
|
-
issue("coords", key, buildQuotedIssue `required coordinate ${key} of lane "${x.lane}" is empty
|
|
1928
|
+
if (judgeKey("coords", key) && isBlankCoordinate(value) && LANE_ENV_OWNER.get(key)?.required === true) {
|
|
1929
|
+
issue("coords", key, buildQuotedIssue `required coordinate ${key} of lane "${x.lane}" is empty or whitespace-only — it counts as absent, and the engine refuses to start without it`);
|
|
1930
1930
|
}
|
|
1931
1931
|
}
|
|
1932
1932
|
for (const key of Object.keys(x.secretRefs))
|
package/docs/API-SURFACE.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
> 每次全量重算并逐名比对 —— 加 / 删 / 改形一个导出而不重生成本档 ⇒ 红。导出面的每次变动因此以本档的 diff 出现在评审里。
|
|
5
5
|
> 形:`value` = 运行期拿得到的值;`type` = 只在类型层;`value+type` = 同名的值与类型(zod schema 与它推出的类型)。
|
|
6
6
|
|
|
7
|
-
子路径 24 条(资产出口 `./package.json` / `./fleet-paths.json` 不是模块,不列),导出名合计
|
|
7
|
+
子路径 24 条(资产出口 `./package.json` / `./fleet-paths.json` 不是模块,不列),导出名合计 1461 个(同名在多条子路径上各计一次)。
|
|
8
8
|
|
|
9
|
-
## `.`(
|
|
9
|
+
## `.`(762)
|
|
10
10
|
|
|
11
11
|
- `ABSOLUTE_HOST_PATH_MAX` — value
|
|
12
12
|
- `ACCESS_TOKEN_TTL_SECONDS` — value
|
|
@@ -645,6 +645,7 @@
|
|
|
645
645
|
- `interpolateHookEnvVars` — value
|
|
646
646
|
- `isAbsoluteHostPath` — value
|
|
647
647
|
- `isAtLeast` — value
|
|
648
|
+
- `isBlankCoordinate` — value
|
|
648
649
|
- `isCanonicalGeneration` — value
|
|
649
650
|
- `isContentOriginWord` — value
|
|
650
651
|
- `isCredentialParamName` — value
|
package/llms-full.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
llms-full.txt — 自包含全文 · 消费方(使用者)视角 · @sema-agent/settings-schema
|
|
3
|
-
重写日期: 2026-10-
|
|
3
|
+
重写日期: 2026-10-28 | 包版本: 15.1.0
|
|
4
4
|
生成方式: 无生成脚本——按当前树 src/*.ts 导出面 + package.json + README/CHANGELOG 手写事实版。
|
|
5
5
|
证据约定: 每条"它怎么工作"的断言附 (文件: 符号) 坐标,刻意不用行号(行号逐版腐烂;符号名由
|
|
6
6
|
typecheck 与 test/package-surface.test.ts、test/docs-hygiene.test.ts 等常驻门钉住)。无证据推断显式标【推断】。
|
|
@@ -251,7 +251,7 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
|
|
|
251
251
|
- `host`:{ workdir?, allowedPaths?, shell?, commandTimeoutMs? } —— 无 secret。
|
|
252
252
|
- `local-docker`:{ image!, memory?, cpus?, network?("none"|"bridge"|"host"), workdir?, mountPath? }。
|
|
253
253
|
- `e2b`:{ apiKeyEnv!, template?, timeoutMs?, livenessMs?, allowInternetAccess?, sandboxEnv? }。
|
|
254
|
-
- `k8s`:{ image!, apiUrl?, tokenEnv?, caCertEnv?, insecureTls?, namespace
|
|
254
|
+
- `k8s`:{ image!, apiUrl?, tokenEnv?, caCertEnv?, insecureTls?, namespace!, runtimeClass?, mountPath?,
|
|
255
255
|
timeoutMs?, memory?, cpu?, s3Snapshot?{ endpoint, bucket?, accessKeyEnv, secretKeyEnv, … } }。
|
|
256
256
|
- `ssh`:{ host!, port?, username!, privateKeyEnv!, mountPath? }。
|
|
257
257
|
- `adb`:{ serial!, adbPath?, mountPath? }。
|
|
@@ -266,9 +266,14 @@ entitlement/execution/limits = `none`;`projects` = `salvage`,整域档位仍 `"t
|
|
|
266
266
|
- `EXECUTION_LANES: readonly ExecutionLaneRow[]`:每行 `{ lane, tenantSeparation: "supported"|"single-user-only", requiredEnv,
|
|
267
267
|
requiredSecretEnv, optionalEnv, optionalSecretEnv }`(深冻结;四栏 env 名两两不交、跨车道不交;行内容逐格抄自引擎,
|
|
268
268
|
坐标在每行 JSDoc)。`TENANT_SEPARATION_WORDS`。
|
|
269
|
+
k8s 行的非密必需栏 = `K8S_SANDBOX_IMAGE` + `K8S_NAMESPACE`(沙箱 pod 所在的命名空间,部署自己声明;服务端的必需 env 门按本行 `requiredEnv ∪ requiredSecretEnv` 拒启)——
|
|
270
|
+
写 worker 的 k8s 行时 `runtime.execution.coords` 两键都要给,缺一键 ⇒ 写闸 issue、存下来的 workers 文档整域读不出。
|
|
271
|
+
- `isBlankCoordinate(value: string): boolean`(包根,15.1.0):必需坐标的值算不算「缺」的**唯一判官** —— 空串或只含空白(`String.prototype.trim` 认的空白)⇒ `true`,带任何非空白字符 ⇒ `false`;
|
|
272
|
+
不 trim 值、不管值的格式(`" sandboxes "` 不是空白)。写闸对所有车道、所有必需坐标读的就是这一只,消费方在自己一侧问同一个问题调它、不抄第二份。
|
|
273
|
+
它只看已经有的字符串值:「这一键根本没设」由调用方先判(写闸对没设的键另有一句 `requires coords.<键>`),据它拒不拒由调用方按键是必需还是可选决定:写闸只对必需坐标据它拒,可选坐标的空串照收(`K8S_RUNTIME_CLASS` 的空串 = 不用 Kata)。(src/execution-lane-index.ts)
|
|
269
274
|
- `STORE_BACKENDS`(mysql/pg/local/memory)、`SESSION_BACKENDS`(memory/mysql/auto)、`MEMORY_ENGINE_BACKENDS`(file/pg/tidb)。
|
|
270
275
|
- 判据(都在 `WorkerSpec` schema 里,zod issue 的 `path` 指字段、message 带出路):enabled ⇒ 两块必填;coords / secretRefs 键 ⊆
|
|
271
|
-
表[lane]
|
|
276
|
+
表[lane] 对应栏、必需键在场且非空白(空串或只含空白按缺);别车道键点名车道;`requirePrincipal` ∧ single-user-only 车道拒(出路按表现算);host ∧ 副本 > 1
|
|
272
277
|
拒;device ⇒ SQL 店且 session ≠ memory;memory 店 ∧ session mysql 拒;记忆引擎 pg/tidb ∧ session memory 拒;SQL 坐标 ⇔ SQL 店;
|
|
273
278
|
记忆后端配店;多租户 ⇒ SQL 店;6.x 删除项(`sessionBackend` / `memoryBackend` / `runtime.db` /
|
|
274
279
|
`coords.tidb*` / `tidbPasswordEnv`)读到即拒并指路迁移。`WorkerSpec` 是 ZodEffects(无 `.shape`):名字判据单独导出 `WORKER_NAME`(src/types.ts)。
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
|
|
3
|
-
重写日期: 2026-10-
|
|
3
|
+
重写日期: 2026-10-28 | 包版本: 15.1.0
|
|
4
4
|
生成方式: 无生成脚本——按当前树的 src/*.ts 导出面 + package.json + README/CHANGELOG 手写事实版;
|
|
5
5
|
每条断言附证据坐标(文件:符号),刻意不用行号(行号随每版腐烂,符号名由 typecheck/test 钉住)。
|
|
6
6
|
刷新触发: package.json 版号变更(test/docs-hygiene.test.ts D 组钉「包版本:」与 package.json 相等、
|
|
@@ -87,6 +87,7 @@ llms.txt — 消费方(使用者)视角索引 · @sema-agent/settings-schema
|
|
|
87
87
|
每个 provider 词 ⊆ 车道词表(7.0.0 删 `remote-docker`:引擎零读点)。(src/remote-exec.ts: `RemoteExecSpec`)
|
|
88
88
|
- **执行车道 + 存储的词表正本**(7.0.0,纯数据):`EXECUTION_LANE_WORDS`(host / e2b / k8s / ssh / adb / local-docker / device)、
|
|
89
89
|
`EXECUTION_LANES`(每车道 `tenantSeparation`〔supported 行另带依据 `tenantSeparationBasis`:isolation / principal-binding,11.0.0 判别联合〕+ 它读的 env 名四栏)、退役车道值墓碑 `RETIRED_LANE_WORDS` + 拒句 `retiredLaneWordRefusal(word)`(11.0.0)、`STORE_BACKENDS` / `SESSION_BACKENDS` / `MEMORY_ENGINE_BACKENDS`。
|
|
90
|
+
必需坐标的值算不算「缺」的唯一判官 `isBlankCoordinate(value)`(包根,15.1.0:空串或只含空白 ⇒ true;写闸读的同一只,消费方在自己一侧问同一个问题调它;「键没设」由调用方先判,据它拒不拒由调用方按键的必需 / 可选决定〔写闸只对必需坐标据它拒〕;src/execution-lane-index.ts)。
|
|
90
91
|
WorkerSpec 的 `runtime.execution` / `runtime.store` 两块按这张表判(写闸 = `WorkerSpec.safeParse`,拒因在 zod issue 里);
|
|
91
92
|
6.x 存量 workers 文档在 `parseDomain` 之前跑一次 `migrateWorkersDomain`(或 bin `npx @sema-agent/settings-schema migrate-workers`;7.0.1 起 stdout = 结论行 `outcome=… written=…` + JSON,没写时 `wouldChange` 取代 `changed`)。
|
|
92
93
|
(src/execution-lanes.ts;src/types.ts: `WorkerSpec`;src/migrate-workers.ts)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/settings-schema",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "15.1.0",
|
|
4
4
|
"description": "Sema settings schema — pure config contract (zod domains + effective-config + roster resolution + ref-integrity + remoteExec) shared by sema-web-admin, sema-server, and the TOC desktop/CLI. One source of truth. (Renamed at 1.0.0 from @sema-agent/registry-core — name and docs only, no behavior change; note the range moves 0.19.0 -> 1.0.0, so a dependency must become ^1.0.0. The old names @sema-agent/registry-core <=0.19.0 and @sema-ai/registry-core remain published, frozen, for migration.)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|