@sema-agent/settings-schema 14.4.0 → 14.6.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 +303 -0
- package/README.md +22 -1
- package/conformance/VERSION +1 -1
- package/dist/api/scopes.d.ts +1 -1
- package/dist/api/scopes.js +1 -1
- package/dist/atomic-write.d.ts +11 -0
- package/dist/atomic-write.js +38 -5
- package/dist/bundle.d.ts +1 -1
- package/dist/bundle.js +1 -1
- package/dist/config-d-file.d.ts +4 -0
- package/dist/config-d-file.js +11 -4
- package/dist/config-dir-io.js +12 -2
- package/dist/config-dir-migration.d.ts +44 -0
- package/dist/config-dir-migration.js +91 -36
- package/dist/config-fns.js +3 -9
- package/dist/conformance/canonical.js +4 -7
- package/dist/conformance/corpus.js +3 -3
- package/dist/conformance/derive.js +6 -5
- package/dist/conformance/run.js +2 -1
- package/dist/definition-copy.d.ts +23 -0
- package/dist/definition-copy.js +52 -0
- package/dist/file-store.d.ts +1 -31
- package/dist/file-store.js +3 -30
- package/dist/fleet.js +2 -1
- package/dist/fs-failure.d.ts +17 -2
- package/dist/fs-failure.js +55 -15
- package/dist/hooks-config.d.ts +2 -2
- package/dist/hooks-config.js +112 -31
- package/dist/http-hook-allowlists.d.ts +7 -3
- package/dist/http-hook-allowlists.js +102 -16
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/mcp-request-entry.d.ts +9 -6
- package/dist/mcp-request-entry.js +225 -93
- package/dist/node.d.ts +2 -1
- package/dist/node.js +2 -1
- package/dist/one-time-migrations.js +36 -41
- package/dist/plain-json-object.d.ts +31 -0
- package/dist/plain-json-object.js +52 -0
- package/dist/portable-domains.d.ts +31 -0
- package/dist/portable-domains.js +30 -0
- package/dist/safety-merge-spec.d.ts +88 -6
- package/dist/safety-merge-spec.js +233 -17
- package/dist/scheduler-store.js +3 -2
- package/dist/settings-units.d.ts +85 -0
- package/dist/settings-units.js +175 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +61 -53
- package/dist/url-credential.d.ts +41 -0
- package/dist/url-credential.js +95 -0
- package/dist/url-shape.d.ts +11 -5
- package/dist/url-shape.js +11 -5
- package/dist/workers-rows.d.ts +2 -1
- package/dist/workers-rows.js +5 -4
- package/docs/API-SURFACE.md +30 -4
- package/docs/CONSUMER-INTEGRATION.md +2 -0
- package/llms-full.txt +24 -2
- package/llms.txt +6 -2
- package/package.json +2 -3
package/dist/types.js
CHANGED
|
@@ -21,6 +21,7 @@ import { INTEGER } from "./integer.js";
|
|
|
21
21
|
import { MAX_EXTRA_BODY_DEPTH } from "./hash.js"; // reconcile the extraBody depth cap with the hash cap (R18)
|
|
22
22
|
import { DispatchPolicy, FLEET_GENERATION_ENV, FLEET_SCOPE_ENV } from "./fleet.js"; // WorkerSpec.fleet.dispatch (scheduling framework D3)
|
|
23
23
|
import { isLoopbackAuthHost } from "./loopback.js";
|
|
24
|
+
import { isJsonArray, isPlainJsonObject } from "./plain-json-object.js"; // THE "is this a JSON object" predicate + the total array test
|
|
24
25
|
import { httpUrlFlaws, httpUrlRule } from "./url-shape.js"; // 11.0.0: THE URL shape judge // 11.0.0: the one narrow「本机」predicate (auth relaxation)
|
|
25
26
|
import { secretScanForms } from "./secret-scan.js"; // package-private normalization — the one set of forms both scanners read (not in the barrel)
|
|
26
27
|
import { WEB_ADMIN_ENV } from "./web-admin-env.js"; // the three connection keys the config renderer writes are reserved worker env
|
|
@@ -193,6 +194,10 @@ function createNonSecretUrl(reachedByMigration) {
|
|
|
193
194
|
* echoed; a fragment's also names its migration, {@link nonSecretUrlIssue}); `params` carries the sentence (`template` / `operands`,
|
|
194
195
|
* no operands) and `flaw`, the rule word.
|
|
195
196
|
*
|
|
197
|
+
* Scope of the secret test: it judges the VALUE's shape (a value that looks like a secret, whatever the parameter is called) — the sibling
|
|
198
|
+
* name-shaped judge `findUrlCredential` (`./url-credential`) asks the other question (a parameter whose NAME is on the credential-name table,
|
|
199
|
+
* whatever the value looks like). Neither contains the other: `?key=abc` passes here and is a hit there; `?x=sk-live-…` is the reverse.
|
|
200
|
+
*
|
|
196
201
|
* This is the seat of every URL in a STORED document the one-time migration rewrites: the domain documents (models `baseUrl`, mcp `url`,
|
|
197
202
|
* `sessionMirror.engineUrl`, workers `gitApiBaseUrl`) and `config.d/remote-exec.json` (k8s `apiUrl`, `s3Snapshot.endpoint`). A URL
|
|
198
203
|
* that is not stored data — the web-admin credential envelope's `issuer`, a wire input — uses {@link NON_SECRET_URL_NO_MIGRATION}.
|
|
@@ -212,62 +217,66 @@ export const NON_SECRET_URL_NO_MIGRATION = createNonSecretUrl(false);
|
|
|
212
217
|
* is proven plain JSON (no toJSON/getters), the stored original == what JSON.stringify persists — closing the
|
|
213
218
|
* non-idempotent-toJSON / accessor-leak / non-plain-container bypasses. Round 2-5 hardening. */
|
|
214
219
|
export function extraBodyUnsafe(v) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
220
|
+
try {
|
|
221
|
+
const stack = [[v, 0]];
|
|
222
|
+
const seen = new WeakSet();
|
|
223
|
+
let budget = 100_000;
|
|
224
|
+
while (stack.length) {
|
|
225
|
+
if (budget-- <= 0)
|
|
226
|
+
return true; // too large to vet → fail-closed
|
|
227
|
+
const [cur, depth] = stack.pop();
|
|
228
|
+
if (depth > MAX_EXTRA_BODY_DEPTH)
|
|
229
|
+
return true; // too deep to hash later — cap = hash cap MINUS the wrapper reserve, since extraBody is always hashed NESTED (R18: a value passing at the raw hash cap would still overflow once wrapped → write fail-open + read hard-DoS)
|
|
230
|
+
if (cur === null)
|
|
231
|
+
continue;
|
|
232
|
+
const t = typeof cur;
|
|
233
|
+
if (t === "string") {
|
|
234
|
+
if (containsSecretToken(cur))
|
|
235
|
+
return true; // tokenized (mid-string secret too) — sibling of URL/coords/mcp. R8
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (t === "number") {
|
|
239
|
+
if (!Number.isFinite(cur))
|
|
240
|
+
return true; // Infinity/NaN → JSON null on persist
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (t === "boolean")
|
|
244
|
+
continue;
|
|
245
|
+
if (t !== "object")
|
|
246
|
+
return true; // undefined / function / symbol / bigint
|
|
247
|
+
if (seen.has(cur))
|
|
248
|
+
return true; // cycle → reject (would also break stableStringify)
|
|
249
|
+
seen.add(cur);
|
|
250
|
+
if (isJsonArray(cur)) {
|
|
251
|
+
if (cur.length !== Object.keys(cur).length)
|
|
252
|
+
return true; // sparse array (holes → null on persist)
|
|
253
|
+
// descriptor walk for arrays too (NOT `for…of`/index access — that would INVOKE an accessor index's
|
|
254
|
+
// getter, which could throw a secret-bearing message). Reject accessor elements. R11.
|
|
255
|
+
for (const [k, desc] of Object.entries(Object.getOwnPropertyDescriptors(cur))) {
|
|
256
|
+
if (k === "length")
|
|
257
|
+
continue; // the array's own `length` data prop — not an element
|
|
258
|
+
if (!Object.hasOwn(desc, "value"))
|
|
259
|
+
return true; // accessor element — do NOT invoke
|
|
260
|
+
stack.push([desc.value, depth + 1]);
|
|
261
|
+
}
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (!isPlainJsonObject(cur))
|
|
265
|
+
return true; // Map/Set/Date/class/toJSON-bearing → not plain
|
|
266
|
+
// descriptor walk: never read a value via [] (would invoke a getter); reject accessors outright.
|
|
249
267
|
for (const [k, desc] of Object.entries(Object.getOwnPropertyDescriptors(cur))) {
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
if (
|
|
253
|
-
return true; //
|
|
268
|
+
if (!Object.hasOwn(desc, "value"))
|
|
269
|
+
return true; // accessor (get/set) — do NOT invoke
|
|
270
|
+
if (containsSecretToken(k))
|
|
271
|
+
return true; // secret-shaped KEY (tokenized). R8
|
|
254
272
|
stack.push([desc.value, depth + 1]);
|
|
255
273
|
}
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
const proto = Object.getPrototypeOf(cur);
|
|
259
|
-
if (proto !== Object.prototype && proto !== null)
|
|
260
|
-
return true; // Map/Set/Date/class/toJSON-bearing → not plain
|
|
261
|
-
// descriptor walk: never read a value via [] (would invoke a getter); reject accessors outright.
|
|
262
|
-
for (const [k, desc] of Object.entries(Object.getOwnPropertyDescriptors(cur))) {
|
|
263
|
-
if (!("value" in desc))
|
|
264
|
-
return true; // accessor (get/set) — do NOT invoke
|
|
265
|
-
if (containsSecretToken(k))
|
|
266
|
-
return true; // secret-shaped KEY (tokenized). R8
|
|
267
|
-
stack.push([desc.value, depth + 1]);
|
|
268
274
|
}
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
return true; // a value the walk cannot read (a proxy that has been revoked, a trap that throws) cannot be vetted ⇒ UNSAFE (fail-closed), never a throw
|
|
269
279
|
}
|
|
270
|
-
return false;
|
|
271
280
|
}
|
|
272
281
|
/** THE vetting pipeline for every FREE-FORM JSON value that lands in the persisted config — `ModelEntry.extraBody`,
|
|
273
282
|
* `ModelEntry.compat` and `ModelEntry.headers` share this ONE function (1.10.0; before that it was inline in `extraBody`
|
|
@@ -514,8 +523,7 @@ export const ModelEntry = z.object({
|
|
|
514
523
|
.transform((v, ctx) => {
|
|
515
524
|
if (v === undefined)
|
|
516
525
|
return undefined;
|
|
517
|
-
|
|
518
|
-
if (typeof v !== "object" || v === null || Array.isArray(v) || (proto !== Object.prototype && proto !== null)) {
|
|
526
|
+
if (!isPlainJsonObject(v)) {
|
|
519
527
|
ctx.addIssue({ code: "custom", ...buildQuotedIssue `extraBody must be a plain JSON object` });
|
|
520
528
|
return z.NEVER;
|
|
521
529
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** 拒因词(闭集;判序 = 本数组序;词只在这一处写 —— {@link UrlCredentialReason} 与 {@link UrlCredential} 的成员都从它派生)。 */
|
|
2
|
+
export declare const URL_CREDENTIAL_REASONS: readonly ["url_userinfo", "url_credential_param"];
|
|
3
|
+
export type UrlCredentialReason = (typeof URL_CREDENTIAL_REASONS)[number];
|
|
4
|
+
/**
|
|
5
|
+
* 凭据参数名表(闭集,23 词,全部是小写 ASCII `[a-z][a-z0-9_-]*`)。query / fragment 里名字整名(ASCII 大小写不敏感)在这张表上的参数就是凭据。
|
|
6
|
+
* 加一个词 = 表里加一格(minor);判官与 {@link isCredentialParamName} 现算,不手抄第二份。
|
|
7
|
+
*/
|
|
8
|
+
export declare const CREDENTIAL_PARAM_NAMES: readonly ["api_key", "apikey", "api-key", "key", "token", "access_token", "refresh_token", "id_token", "auth", "authorization", "proxy-authorization", "password", "passwd", "pwd", "secret", "client_secret", "private_key", "signature", "sig", "x-amz-signature", "x-amz-security-token", "x-amz-credential", "sas"];
|
|
9
|
+
export type CredentialParamName = (typeof CREDENTIAL_PARAM_NAMES)[number];
|
|
10
|
+
/**
|
|
11
|
+
* 「这个**已解码**的参数名是不是凭据名」。入参是收方读到的那一形(`URLSearchParams` 解过一次码的名字):整名在 {@link CREDENTIAL_PARAM_NAMES} 里(ASCII 大小写不敏感)才算。
|
|
12
|
+
*/
|
|
13
|
+
export declare function isCredentialParamName(decodedName: string): boolean;
|
|
14
|
+
/** 每个拒因词带的细节(键集必须覆盖 {@link URL_CREDENTIAL_REASONS} 的每个词:数组里加一个词而这里没跟 ⇒ 下面的映射型编译红,{@link urlCredentialRule} 的穷举 switch 也红)。 */
|
|
15
|
+
interface UrlCredentialDetail {
|
|
16
|
+
readonly url_userinfo: unknown;
|
|
17
|
+
readonly url_credential_param: {
|
|
18
|
+
readonly at: "query" | "fragment";
|
|
19
|
+
readonly name: CredentialParamName;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** 一处命中:`url_userinfo`,或 `url_credential_param` + 它在哪一段、表里的哪个词(小写;不是调用方的原字节)。成员从 {@link URL_CREDENTIAL_REASONS} 派生。 */
|
|
23
|
+
export type UrlCredential = {
|
|
24
|
+
readonly [R in UrlCredentialReason]: {
|
|
25
|
+
readonly reason: R;
|
|
26
|
+
} & UrlCredentialDetail[R];
|
|
27
|
+
}[UrlCredentialReason];
|
|
28
|
+
/**
|
|
29
|
+
* 判:这个值作为 URL 读出来(WHATWG),带不带 **userinfo**(判官的第一问,也是 {@link findUrlCredential} 的第一步)。只问这一件的座位 —— 运行时本就拒带 userinfo 的 URL、
|
|
30
|
+
* 而凭据名参数不由它拒的载体 —— 问这一只,不在座位里另写一份 userinfo 谓词。纯函数。
|
|
31
|
+
*/
|
|
32
|
+
export declare function findUrlUserinfo(raw: string): Extract<UrlCredential, {
|
|
33
|
+
readonly reason: "url_userinfo";
|
|
34
|
+
}> | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* 判:这个值作为 URL 读出来(WHATWG),带不带凭据。首个命中(userinfo → query → fragment);没有(含不透明 / 解析不了的值)⇒ `undefined`。纯函数。
|
|
37
|
+
*/
|
|
38
|
+
export declare function findUrlCredential(raw: string): UrlCredential | undefined;
|
|
39
|
+
/** 一处命中的规则句(穷举;新拒因词不补 = 编译红)。只说规则与表词,不含值。 */
|
|
40
|
+
export declare function urlCredentialRule(c: UrlCredential): string;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **「这个 URL 的 userinfo 或 query / fragment 里,有没有名字在凭据名表上的参数」的名形判官**(14.5.0;lift 自 server `src/url-credential.ts`,规则、拒因词闭集与规则句逐字搬)。纯函数,零 import(除同包的 `./url-shape`),浏览器安全。
|
|
3
|
+
*
|
|
4
|
+
* ## 与 `NON_SECRET_URL`(`./types`)的关系:两个问题,互补,不是一个包含另一个
|
|
5
|
+
* 本判官按**名**判 —— 参数叫什么(名字整名在 {@link CREDENTIAL_PARAM_NAMES} 上就是凭据,不论值长什么样);`NON_SECRET_URL` 的 `urlQueryFragmentHasSecret` 按**值形**判密钥 —— 值长得像密钥
|
|
6
|
+
* (不论参数叫什么)。所以 `?key=abc` 本判官命中而 `NON_SECRET_URL` 放行(值不像密钥),`?x=sk-live-…` 反之;userinfo 两边都判。存量域文档位(`mcp` 的 `transport.url` 等)读 `NON_SECRET_URL`,
|
|
7
|
+
* 请求面等座位读本判官。把两个问题合成一只定义是另一件事,这里不做。
|
|
8
|
+
*
|
|
9
|
+
* ## 规则(一条):URL 里的凭据 = 日志脱敏器已经在用的那一个定义
|
|
10
|
+
* 一个 URL 里凭据住三个位置:
|
|
11
|
+
* · **userinfo**(`scheme://<这一段>@host`,只有用户名也算)—— 判由本包的 URL 形判官 {@link httpUrlFlaws}(`["userinfo"]`),本模块不留第二份谓词;
|
|
12
|
+
* · **query 参数**、**fragment 参数**(同一段文法)里名字整名在 {@link CREDENTIAL_PARAM_NAMES} 上的那一个 —— 名字按收方的读法读:WHATWG `URLSearchParams` 解一次码,
|
|
13
|
+
* ASCII 大小写不敏感(不做 Unicode 折叠),整名在表里才算(`db_password` / `tokens` / `keyword` 是另一个名字)。
|
|
14
|
+
* 答首个命中(判序 = 上面的序),拒因闭集两词 {@link URL_CREDENTIAL_REASONS};**值从不进答案**,参数名只回显表里的那个词(小写)。
|
|
15
|
+
* 不透明形 / 解析不了的值没有可读的部件 ⇒ 答「没有」—— 这一形收不收是**座位**自己的律,不在本判官里。
|
|
16
|
+
*
|
|
17
|
+
* ## 判官只判「有没有」;座位决定怎么用
|
|
18
|
+
* 本模块不决定哪个载体拒、哪个载体放:同一个键在不同载体上的律可以不同(例:新提交的请求面拒,续跑重放存量体的腿不拒 —— 拒会砖死升级前停驻的任务;
|
|
19
|
+
* 部署 env 与续跑重放只因 userinfo 拒,凭据名参数只在新提交门拒)。所以判官**不**接进任何一份读已落盘 / 重放字节的 schema 或读器 —— 那会把载体律焊死成
|
|
20
|
+
* 读器律;座位在自己的新提交门调 {@link findUrlCredential} / {@link findUrlUserinfo},拒句 / 拒体的形(HTTP 码、字段坐标、出路句)是座位自己的铸点。
|
|
21
|
+
*
|
|
22
|
+
* ## 名表的来源
|
|
23
|
+
* {@link CREDENTIAL_PARAM_NAMES} 是 server 日志脱敏器名表(锚强度表的键)的 23 词。脱敏器另有一列「自由文本里这个词的锚强度」,那一列是脱敏器的事,不在这里;
|
|
24
|
+
* 词表本身在这里成为契约面 —— 脱敏器与本判官读同一张词表(同一个定义)靠消费方把它的表键钉在本导出上(例:`Record<CredentialParamName, …>` 的穷尽映射 ⇒ 加 / 删词不同步即编译红)。
|
|
25
|
+
*/
|
|
26
|
+
import { httpUrlFlaws, httpUrlRule, isHierarchicalUrl } from "./url-shape.js";
|
|
27
|
+
/** 拒因词(闭集;判序 = 本数组序;词只在这一处写 —— {@link UrlCredentialReason} 与 {@link UrlCredential} 的成员都从它派生)。 */
|
|
28
|
+
export const URL_CREDENTIAL_REASONS = Object.freeze(["url_userinfo", "url_credential_param"]);
|
|
29
|
+
/**
|
|
30
|
+
* 凭据参数名表(闭集,23 词,全部是小写 ASCII `[a-z][a-z0-9_-]*`)。query / fragment 里名字整名(ASCII 大小写不敏感)在这张表上的参数就是凭据。
|
|
31
|
+
* 加一个词 = 表里加一格(minor);判官与 {@link isCredentialParamName} 现算,不手抄第二份。
|
|
32
|
+
*/
|
|
33
|
+
export const CREDENTIAL_PARAM_NAMES = Object.freeze([
|
|
34
|
+
"api_key", "apikey", "api-key", "key",
|
|
35
|
+
"token", "access_token", "refresh_token", "id_token",
|
|
36
|
+
"auth", "authorization", "proxy-authorization",
|
|
37
|
+
"password", "passwd", "pwd",
|
|
38
|
+
"secret", "client_secret", "private_key",
|
|
39
|
+
"signature", "sig",
|
|
40
|
+
"x-amz-signature", "x-amz-security-token", "x-amz-credential",
|
|
41
|
+
"sas",
|
|
42
|
+
]);
|
|
43
|
+
/** 名表的查找形(`Set`,不拿普通对象当查表:`constructor` / `__proto__` 会读到原型链上的东西)。 */
|
|
44
|
+
const CREDENTIAL_PARAM_NAME_SET = new Set(CREDENTIAL_PARAM_NAMES);
|
|
45
|
+
/** ASCII 大小写折叠(只折 `A-Z`,不做 Unicode 折叠:`TOKEN` 不是 `token`)。 */
|
|
46
|
+
const asciiLower = (s) => s.replace(/[A-Z]/g, (c) => c.toLowerCase());
|
|
47
|
+
/**
|
|
48
|
+
* 「这个**已解码**的参数名是不是凭据名」。入参是收方读到的那一形(`URLSearchParams` 解过一次码的名字):整名在 {@link CREDENTIAL_PARAM_NAMES} 里(ASCII 大小写不敏感)才算。
|
|
49
|
+
*/
|
|
50
|
+
export function isCredentialParamName(decodedName) {
|
|
51
|
+
return CREDENTIAL_PARAM_NAME_SET.has(asciiLower(decodedName));
|
|
52
|
+
}
|
|
53
|
+
/** 一段参数串(`?` / `#` 之后)里第一个凭据名参数的表词(小写);没有 ⇒ `undefined`。 */
|
|
54
|
+
function credentialParamIn(params) {
|
|
55
|
+
for (const name of new URLSearchParams(params).keys()) {
|
|
56
|
+
const word = asciiLower(name);
|
|
57
|
+
if (CREDENTIAL_PARAM_NAME_SET.has(word))
|
|
58
|
+
return word;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 判:这个值作为 URL 读出来(WHATWG),带不带 **userinfo**(判官的第一问,也是 {@link findUrlCredential} 的第一步)。只问这一件的座位 —— 运行时本就拒带 userinfo 的 URL、
|
|
64
|
+
* 而凭据名参数不由它拒的载体 —— 问这一只,不在座位里另写一份 userinfo 谓词。纯函数。
|
|
65
|
+
*/
|
|
66
|
+
export function findUrlUserinfo(raw) {
|
|
67
|
+
return isHierarchicalUrl(raw) && httpUrlFlaws(raw, ["userinfo"]).includes("userinfo") ? { reason: "url_userinfo" } : undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 判:这个值作为 URL 读出来(WHATWG),带不带凭据。首个命中(userinfo → query → fragment);没有(含不透明 / 解析不了的值)⇒ `undefined`。纯函数。
|
|
71
|
+
*/
|
|
72
|
+
export function findUrlCredential(raw) {
|
|
73
|
+
if (!isHierarchicalUrl(raw))
|
|
74
|
+
return undefined;
|
|
75
|
+
const userinfo = findUrlUserinfo(raw);
|
|
76
|
+
if (userinfo !== undefined)
|
|
77
|
+
return userinfo;
|
|
78
|
+
const u = new URL(raw);
|
|
79
|
+
const inQuery = credentialParamIn(u.search);
|
|
80
|
+
if (inQuery !== undefined)
|
|
81
|
+
return { reason: "url_credential_param", at: "query", name: inQuery };
|
|
82
|
+
const inFragment = credentialParamIn(u.hash.slice(1));
|
|
83
|
+
if (inFragment !== undefined)
|
|
84
|
+
return { reason: "url_credential_param", at: "fragment", name: inFragment };
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
/** 一处命中的规则句(穷举;新拒因词不补 = 编译红)。只说规则与表词,不含值。 */
|
|
88
|
+
export function urlCredentialRule(c) {
|
|
89
|
+
switch (c.reason) {
|
|
90
|
+
case "url_userinfo":
|
|
91
|
+
return httpUrlRule("userinfo"); // 与形判官诸座同一句(不写第二句)
|
|
92
|
+
case "url_credential_param":
|
|
93
|
+
return `must not carry a credential parameter (the ${c.at} names "${c.name}", a name the service's log redactor treats as a credential)`;
|
|
94
|
+
}
|
|
95
|
+
}
|
package/dist/url-shape.d.ts
CHANGED
|
@@ -13,10 +13,16 @@
|
|
|
13
13
|
*
|
|
14
14
|
* 拒句里值的样子(展示铸点)不在本模块:那是各宿主自己的展示律(server `buildRefusalShownUrl`)。本模块零 import,纯函数,浏览器安全。
|
|
15
15
|
*
|
|
16
|
-
* ## 本模块里的第二个答案(如实,14.0.0)
|
|
16
|
+
* ## 本模块里的第二个答案(如实,14.0.0 起;14.6.0 把分歧写成读数)
|
|
17
17
|
* 请求面 MCP 传输的 url(任务提交体 `mcpServers[i].transport.url`,`./mcp-request-entry`)今天不走上面的形判官,而是只看前缀
|
|
18
|
-
* ({@link startsWithHttpScheme})
|
|
19
|
-
*
|
|
18
|
+
* ({@link startsWithHttpScheme}):那是消费方请求腿既有的读法,入包时语义逐字冻结。两者对同一个串的答案是两张表(读数逐串钉在 `test/request-url-two-answers-14.6.0.test.ts`):
|
|
19
|
+
* · **读器收、判官拒** —— 前缀对、WHATWG 解析不了的串:裸 scheme `https://`、host 里有空格、未闭合的 IPv6 字面量、非法端口……读器收下,判官答 `not-http-url`;
|
|
20
|
+
* · **判官收、读器拒** —— 不是照字面写全 `http(s)://` 的串:`https:host`、前导空白。WHATWG 会把它们修成合法 URL,读器按字面规则拒;
|
|
21
|
+
* · **部件** —— 读器的座位今天不禁 userinfo / query / fragment(全收),判官在禁这些部件的座位上点名它们。
|
|
22
|
+
* 这是一项**已知待收、收口是收紧所以不走 minor** 的分歧(S-2106):读器收下的是此前能解析出传输的提交体(含重放存量提交体时读它的那一路),把读器收严 = 同一份配置解析出不同结果,发版纪律里是 major,
|
|
23
|
+
* 不是 minor;而「读器直接换成判官」还会放宽第二张表,更不行。S-2106 的收口形与 `PLUGIN_ORIGIN` 座位(`./types`)同构 —— 读器收 ⟺ `startsWithHttpScheme(url)`(座位自己的「照字面写全」规则)∧
|
|
24
|
+
* `httpUrlFlaws(url, [座位禁的部件]).length === 0`(形与部件归本判官)—— 只收紧,不放宽;座位禁不禁 userinfo 是同一次 major 里要定的另一格(读器的拒句已写着凭据别放 URL)。
|
|
25
|
+
* 这个前缀守卫放在这里,是为了让「本包里判 URL 的正则」只有这一个模块。
|
|
20
26
|
*/
|
|
21
27
|
/** 座位可以禁掉的部件(闭集;判序 = 本数组序)。 */
|
|
22
28
|
export declare const HTTP_URL_PARTS: readonly ["userinfo", "query", "fragment"];
|
|
@@ -35,8 +41,8 @@ export declare function isHierarchicalUrl(raw: string): boolean;
|
|
|
35
41
|
* 层级式非 http(s) ⇒ `not-http-url` 之后照判部件。返回类型按 `forbid` 收窄:座位没禁的部件不会出现在答案里。
|
|
36
42
|
*/
|
|
37
43
|
export declare function httpUrlFlaws<P extends HttpUrlPart>(raw: string, forbid: readonly P[]): ReadonlyArray<"not-http-url" | P>;
|
|
38
|
-
/** 以 `http://` / `https://` 起头(大小写不敏感)—— 一道**语法前缀**守卫,不是形判官:不解析、不看 host /
|
|
39
|
-
* 唯一读者是请求面 MCP 传输读器(`readRequestMcpTransport`)
|
|
44
|
+
/** 以 `http://` / `https://` 起头(大小写不敏感)—— 一道**语法前缀**守卫,不是形判官:不解析、不看 host / 部件,所以 `https://` 这样的裸 scheme 它也收(见头注「第二个答案」)。
|
|
45
|
+
* 唯一读者是请求面 MCP 传输读器(`readRequestMcpTransport`);S-2106 收口时它退成那个读器座位的「照字面写全」规则,与形判官合取。 */
|
|
40
46
|
export declare function startsWithHttpScheme(raw: string): boolean;
|
|
41
47
|
/** 一个拒因词的规则句(穷举;新词不补 = 编译红)。句子只说规则,不含值。 */
|
|
42
48
|
export declare function httpUrlRule(flaw: HttpUrlFlaw): string;
|
package/dist/url-shape.js
CHANGED
|
@@ -13,10 +13,16 @@
|
|
|
13
13
|
*
|
|
14
14
|
* 拒句里值的样子(展示铸点)不在本模块:那是各宿主自己的展示律(server `buildRefusalShownUrl`)。本模块零 import,纯函数,浏览器安全。
|
|
15
15
|
*
|
|
16
|
-
* ## 本模块里的第二个答案(如实,14.0.0)
|
|
16
|
+
* ## 本模块里的第二个答案(如实,14.0.0 起;14.6.0 把分歧写成读数)
|
|
17
17
|
* 请求面 MCP 传输的 url(任务提交体 `mcpServers[i].transport.url`,`./mcp-request-entry`)今天不走上面的形判官,而是只看前缀
|
|
18
|
-
* ({@link startsWithHttpScheme})
|
|
19
|
-
*
|
|
18
|
+
* ({@link startsWithHttpScheme}):那是消费方请求腿既有的读法,入包时语义逐字冻结。两者对同一个串的答案是两张表(读数逐串钉在 `test/request-url-two-answers-14.6.0.test.ts`):
|
|
19
|
+
* · **读器收、判官拒** —— 前缀对、WHATWG 解析不了的串:裸 scheme `https://`、host 里有空格、未闭合的 IPv6 字面量、非法端口……读器收下,判官答 `not-http-url`;
|
|
20
|
+
* · **判官收、读器拒** —— 不是照字面写全 `http(s)://` 的串:`https:host`、前导空白。WHATWG 会把它们修成合法 URL,读器按字面规则拒;
|
|
21
|
+
* · **部件** —— 读器的座位今天不禁 userinfo / query / fragment(全收),判官在禁这些部件的座位上点名它们。
|
|
22
|
+
* 这是一项**已知待收、收口是收紧所以不走 minor** 的分歧(S-2106):读器收下的是此前能解析出传输的提交体(含重放存量提交体时读它的那一路),把读器收严 = 同一份配置解析出不同结果,发版纪律里是 major,
|
|
23
|
+
* 不是 minor;而「读器直接换成判官」还会放宽第二张表,更不行。S-2106 的收口形与 `PLUGIN_ORIGIN` 座位(`./types`)同构 —— 读器收 ⟺ `startsWithHttpScheme(url)`(座位自己的「照字面写全」规则)∧
|
|
24
|
+
* `httpUrlFlaws(url, [座位禁的部件]).length === 0`(形与部件归本判官)—— 只收紧,不放宽;座位禁不禁 userinfo 是同一次 major 里要定的另一格(读器的拒句已写着凭据别放 URL)。
|
|
25
|
+
* 这个前缀守卫放在这里,是为了让「本包里判 URL 的正则」只有这一个模块。
|
|
20
26
|
*/
|
|
21
27
|
/** 座位可以禁掉的部件(闭集;判序 = 本数组序)。 */
|
|
22
28
|
export const HTTP_URL_PARTS = ["userinfo", "query", "fragment"];
|
|
@@ -51,8 +57,8 @@ export function httpUrlFlaws(raw, forbid) {
|
|
|
51
57
|
const shape = u.protocol === "http:" || u.protocol === "https:" ? [] : ["not-http-url"];
|
|
52
58
|
return [...shape, ...HTTP_URL_PARTS.filter((p) => forbid.includes(p) && present[p])];
|
|
53
59
|
}
|
|
54
|
-
/** 以 `http://` / `https://` 起头(大小写不敏感)—— 一道**语法前缀**守卫,不是形判官:不解析、不看 host /
|
|
55
|
-
* 唯一读者是请求面 MCP 传输读器(`readRequestMcpTransport`)
|
|
60
|
+
/** 以 `http://` / `https://` 起头(大小写不敏感)—— 一道**语法前缀**守卫,不是形判官:不解析、不看 host / 部件,所以 `https://` 这样的裸 scheme 它也收(见头注「第二个答案」)。
|
|
61
|
+
* 唯一读者是请求面 MCP 传输读器(`readRequestMcpTransport`);S-2106 收口时它退成那个读器座位的「照字面写全」规则,与形判官合取。 */
|
|
56
62
|
export function startsWithHttpScheme(raw) {
|
|
57
63
|
return /^https?:\/\//i.test(raw);
|
|
58
64
|
}
|
package/dist/workers-rows.d.ts
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* web-admin 的 workers 读口先过本函数:每一只**认得出名字**的 worker 单独过读侧自己的判据(只含这一行的 workers 域读不读得进,
|
|
8
8
|
* `WorkersConfig` —— 与 `parseDomain("workers")` 同一只 schema,不另抄任何规则;判据与拒句拼装在包内私有的 `./worker-row-verdict`,
|
|
9
9
|
* 一次性迁移 SS-M4 判改写后的行调同一只);读不进的那一行扣下(`held`,带原文与拒因),
|
|
10
|
-
* 其余照常进 `doc`。整域读侧仍是最后围栏,只剩两类归它:认不出名字的条目(
|
|
10
|
+
* 其余照常进 `doc`。整域读侧仍是最后围栏,只剩两类归它:认不出名字的条目(不是朴素 JSON 对象〔类实例 / `Map` / `Date` 一类,JSON 解析不出来,走到这里只能是程序拼的〕/ 没有字符串名字 —— 原样留在 `doc`;
|
|
11
|
+
* 非朴素的**坏**行因此不在这里单行扣下,而是交整域 `parseDomain("workers")` 判,整张舰队清单读不出 —— 响亮、fail-closed,只落在非 JSON 值上)与跨行规则
|
|
11
12
|
* (名字 / 路由前缀唯一 —— 两行单独都读得进、合起来才不行,这里不替运维挑留哪一行)。
|
|
12
13
|
*
|
|
13
14
|
* 被扣下的行**不进** `doc`,但随 `held[].original` 逐字带回:`doc` ∪ `held[].original` 覆盖输入的全部 worker。读口不写回 —— 原文留在盘上,
|
package/dist/workers-rows.js
CHANGED
|
@@ -7,27 +7,28 @@
|
|
|
7
7
|
* web-admin 的 workers 读口先过本函数:每一只**认得出名字**的 worker 单独过读侧自己的判据(只含这一行的 workers 域读不读得进,
|
|
8
8
|
* `WorkersConfig` —— 与 `parseDomain("workers")` 同一只 schema,不另抄任何规则;判据与拒句拼装在包内私有的 `./worker-row-verdict`,
|
|
9
9
|
* 一次性迁移 SS-M4 判改写后的行调同一只);读不进的那一行扣下(`held`,带原文与拒因),
|
|
10
|
-
* 其余照常进 `doc`。整域读侧仍是最后围栏,只剩两类归它:认不出名字的条目(
|
|
10
|
+
* 其余照常进 `doc`。整域读侧仍是最后围栏,只剩两类归它:认不出名字的条目(不是朴素 JSON 对象〔类实例 / `Map` / `Date` 一类,JSON 解析不出来,走到这里只能是程序拼的〕/ 没有字符串名字 —— 原样留在 `doc`;
|
|
11
|
+
* 非朴素的**坏**行因此不在这里单行扣下,而是交整域 `parseDomain("workers")` 判,整张舰队清单读不出 —— 响亮、fail-closed,只落在非 JSON 值上)与跨行规则
|
|
11
12
|
* (名字 / 路由前缀唯一 —— 两行单独都读得进、合起来才不行,这里不替运维挑留哪一行)。
|
|
12
13
|
*
|
|
13
14
|
* 被扣下的行**不进** `doc`,但随 `held[].original` 逐字带回:`doc` ∪ `held[].original` 覆盖输入的全部 worker。读口不写回 —— 原文留在盘上,
|
|
14
15
|
* web-admin 在列表里标红点名 `field` / `reason`。拒因只点名字段、不回显值。
|
|
15
16
|
*/
|
|
16
17
|
import { outgoingPath, redactSecrets } from "./config-fns.js";
|
|
18
|
+
import { isJsonArray, isPlainJsonObject } from "./plain-json-object.js";
|
|
17
19
|
import { workerRowVerdict } from "./worker-row-verdict.js";
|
|
18
|
-
const isRec = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
19
20
|
/**
|
|
20
21
|
* 逐行读一份 workers 域**原始**文档(`{ workers: [...] }`)。不是这个形 ⇒ 原样返回、零扣下(判形是 `parseDomain` 的事)。
|
|
21
22
|
* 规则见文件头。
|
|
22
23
|
*/
|
|
23
24
|
export function readWorkersRows(raw) {
|
|
24
|
-
if (!
|
|
25
|
+
if (!isPlainJsonObject(raw) || !isJsonArray(raw.workers))
|
|
25
26
|
return { doc: raw, held: [] };
|
|
26
27
|
const rows = raw.workers;
|
|
27
28
|
const kept = [];
|
|
28
29
|
const held = [];
|
|
29
30
|
rows.forEach((row, index) => {
|
|
30
|
-
if (!
|
|
31
|
+
if (!isPlainJsonObject(row) || typeof row.name !== "string") {
|
|
31
32
|
kept.push(row); // 认不出名字:整域读侧判
|
|
32
33
|
return;
|
|
33
34
|
}
|
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` 不是模块,不列),导出名合计 1365 个(同名在多条子路径上各计一次)。
|
|
8
8
|
|
|
9
|
-
## `.`(
|
|
9
|
+
## `.`(680)
|
|
10
10
|
|
|
11
11
|
- `ABSOLUTE_HOST_PATH_MAX` — value
|
|
12
12
|
- `ACCESS_TOKEN_TTL_SECONDS` — value
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
- `COMMAND_DECISIONS` — value
|
|
37
37
|
- `CONFIG_CHANGE_SOURCES` — value
|
|
38
38
|
- `CONTROL_AND_BIDI_CHARS` — value
|
|
39
|
+
- `CREDENTIAL_PARAM_NAMES` — value
|
|
39
40
|
- `CROSS_SESSION_INBOUND_UNSET` — value
|
|
40
41
|
- `CROSS_SESSION_INBOUND_VALUES` — value
|
|
41
42
|
- `CancelSource` — value+type
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
- `ConfigChangeHookInput` — value
|
|
56
57
|
- `ConfigPath` — type
|
|
57
58
|
- `ConfigReader` — type
|
|
59
|
+
- `CredentialParamName` — type
|
|
58
60
|
- `CrossSessionInboundSetting` — value
|
|
59
61
|
- `CrossSessionInboundSettingValue` — type
|
|
60
62
|
- `CrossSessionInboundValue` — type
|
|
@@ -393,6 +395,7 @@
|
|
|
393
395
|
- `SESSION_MIRROR_ENGINE_URL` — value
|
|
394
396
|
- `SETTINGS_SOURCES` — value
|
|
395
397
|
- `SETTINGS_SOURCE_CONTRACT` — value
|
|
398
|
+
- `SETTINGS_UNITS` — value
|
|
396
399
|
- `SHA256_DIGEST_RE` — value
|
|
397
400
|
- `STALE_HEARTBEAT_MULTIPLIER` — value
|
|
398
401
|
- `STALE_SESSION_RECORD_MS` — value
|
|
@@ -440,6 +443,7 @@
|
|
|
440
443
|
- `SettingSource` — value+type
|
|
441
444
|
- `SettingsSource` — type
|
|
442
445
|
- `SettingsSourceContract` — type
|
|
446
|
+
- `SettingsUnitName` — type
|
|
443
447
|
- `SetupHookInput` — value
|
|
444
448
|
- `Sha256Digest` — value
|
|
445
449
|
- `SiblingResolver` — type
|
|
@@ -463,6 +467,7 @@
|
|
|
463
467
|
- `TCP_PORT` — value
|
|
464
468
|
- `TENANT_SEPARATION_BASES` — value
|
|
465
469
|
- `TENANT_SEPARATION_WORDS` — value
|
|
470
|
+
- `TIGHTEN_VERDICTS` — value
|
|
466
471
|
- `TOKEN_SCOPE_REFUSALS` — value
|
|
467
472
|
- `TOOL_WIRE_NAME_MAX_CHARS` — value
|
|
468
473
|
- `TaskCompletedHookInput` — value
|
|
@@ -473,16 +478,20 @@
|
|
|
473
478
|
- `TierBindings` — value+type
|
|
474
479
|
- `TierGroup` — value+type
|
|
475
480
|
- `TightenRule` — value+type
|
|
481
|
+
- `TightenVerdict` — type
|
|
476
482
|
- `TokenGrantResponse` — value+type
|
|
477
483
|
- `TokenQuota` — value+type
|
|
478
484
|
- `TokenRefreshRequest` — value+type
|
|
479
485
|
- `TokenScopeRefusal` — type
|
|
480
486
|
- `UI_LANGUAGE_SETTING` — value
|
|
481
487
|
- `UI_LANGUAGE_VALUES` — value
|
|
488
|
+
- `URL_CREDENTIAL_REASONS` — value
|
|
482
489
|
- `USER_ROLES` — value
|
|
483
490
|
- `UiLanguageSetting` — value
|
|
484
491
|
- `UiLanguageSettings` — value+type
|
|
485
492
|
- `UiLanguageValue` — type
|
|
493
|
+
- `UrlCredential` — type
|
|
494
|
+
- `UrlCredentialReason` — type
|
|
486
495
|
- `UsageEntry` — value+type
|
|
487
496
|
- `UsageReport` — value+type
|
|
488
497
|
- `User` — type
|
|
@@ -548,6 +557,8 @@
|
|
|
548
557
|
- `emptyEffective` — value
|
|
549
558
|
- `extraBodyUnsafe` — value
|
|
550
559
|
- `findSkillContent` — value
|
|
560
|
+
- `findUrlCredential` — value
|
|
561
|
+
- `findUrlUserinfo` — value
|
|
551
562
|
- `fnv1a64` — value
|
|
552
563
|
- `formatConfigPath` — value
|
|
553
564
|
- `hasControlOrBidi` — value
|
|
@@ -563,6 +574,7 @@
|
|
|
563
574
|
- `isAtLeast` — value
|
|
564
575
|
- `isCanonicalGeneration` — value
|
|
565
576
|
- `isContentOriginWord` — value
|
|
577
|
+
- `isCredentialParamName` — value
|
|
566
578
|
- `isDisclosableIdentity` — value
|
|
567
579
|
- `isDomainName` — value
|
|
568
580
|
- `isHierarchicalUrl` — value
|
|
@@ -593,6 +605,7 @@
|
|
|
593
605
|
- `mcpServerPolicyVerdict` — value
|
|
594
606
|
- `memberAdmission` — value
|
|
595
607
|
- `memberMinTrust` — value
|
|
608
|
+
- `memberTighten` — value
|
|
596
609
|
- `membersOf` — value
|
|
597
610
|
- `mintScopedToken` — value
|
|
598
611
|
- `narrowAtMention` — value
|
|
@@ -623,6 +636,7 @@
|
|
|
623
636
|
- `readScopedTokenBinding` — value
|
|
624
637
|
- `readServingLeaseRequest` — value
|
|
625
638
|
- `readServingLeaseResponse` — value
|
|
639
|
+
- `readSettingsUnit` — value
|
|
626
640
|
- `readTokenScope` — value
|
|
627
641
|
- `readUiLanguage` — value
|
|
628
642
|
- `readWorkersRows` — value
|
|
@@ -637,6 +651,7 @@
|
|
|
637
651
|
- `resolveCatalogDefaultModel` — value
|
|
638
652
|
- `resolveEffectiveForWorker` — value
|
|
639
653
|
- `resolveMemberAdmission` — value
|
|
654
|
+
- `resolveMemberTighten` — value
|
|
640
655
|
- `resolveTokenScope` — value
|
|
641
656
|
- `retiredDomainSentence` — value
|
|
642
657
|
- `retiredEnvRefusal` — value
|
|
@@ -659,10 +674,13 @@
|
|
|
659
674
|
- `statusRowsToPrune` — value
|
|
660
675
|
- `stopAttributionText` — value
|
|
661
676
|
- `stripControlAndBidi` — value
|
|
677
|
+
- `tightenVerdict` — value
|
|
662
678
|
- `toSkillsManifest` — value
|
|
663
679
|
- `toVerifiedIdentity` — value
|
|
664
680
|
- `trustRank` — value
|
|
681
|
+
- `unitTighten` — value
|
|
665
682
|
- `urlAllowPatternMatches` — value
|
|
683
|
+
- `urlCredentialRule` — value
|
|
666
684
|
- `urlDenyPatternMatches` — value
|
|
667
685
|
- `urlQueryFragmentHasSecret` — value
|
|
668
686
|
- `verifyScopedToken` — value
|
|
@@ -1038,7 +1056,7 @@
|
|
|
1038
1056
|
- `REMOTE_EXEC_DOCUMENT` — value
|
|
1039
1057
|
- `RemoteExecSpec` — value+type
|
|
1040
1058
|
|
|
1041
|
-
## `./safety-merge-spec`(
|
|
1059
|
+
## `./safety-merge-spec`(24)
|
|
1042
1060
|
|
|
1043
1061
|
- `FailMode` — value+type
|
|
1044
1062
|
- `MIN_TRUST_WORDS` — value
|
|
@@ -1052,25 +1070,32 @@
|
|
|
1052
1070
|
- `SafetyMergeCategory` — type
|
|
1053
1071
|
- `SafetyMergeMember` — type
|
|
1054
1072
|
- `SpecEnforcement` — value+type
|
|
1073
|
+
- `TIGHTEN_VERDICTS` — value
|
|
1055
1074
|
- `TightenRule` — value+type
|
|
1075
|
+
- `TightenVerdict` — type
|
|
1056
1076
|
- `isAtLeast` — value
|
|
1057
1077
|
- `memberAdmission` — value
|
|
1058
1078
|
- `memberMinTrust` — value
|
|
1079
|
+
- `memberTighten` — value
|
|
1059
1080
|
- `membersOf` — value
|
|
1060
1081
|
- `resolveMemberAdmission` — value
|
|
1082
|
+
- `resolveMemberTighten` — value
|
|
1083
|
+
- `tightenVerdict` — value
|
|
1061
1084
|
- `trustRank` — value
|
|
1062
1085
|
|
|
1063
|
-
## `./node`(
|
|
1086
|
+
## `./node`(39)
|
|
1064
1087
|
|
|
1065
1088
|
- `ConfigBundle` — type
|
|
1066
1089
|
- `ConfigDirectoryDocument` — type
|
|
1067
1090
|
- `ConfigDirectoryIo` — type
|
|
1068
1091
|
- `ConfigDirectoryMigration` — type
|
|
1069
1092
|
- `ConfigDirectoryRefusal` — type
|
|
1093
|
+
- `ConfigDocumentsMigration` — type
|
|
1070
1094
|
- `DomainReadError` — type
|
|
1071
1095
|
- `FileConfigStore` — value+type
|
|
1072
1096
|
- `LocalConfig` — type
|
|
1073
1097
|
- `MigrateConfigDirectoryOptions` — type
|
|
1098
|
+
- `MigrateConfigDocumentsOptions` — type
|
|
1074
1099
|
- `NON_DOMAIN_CONFIG_FILES` — value
|
|
1075
1100
|
- `NON_PORTABLE_DOMAIN_REASONS` — value
|
|
1076
1101
|
- `PORTABLE_DOMAINS` — value
|
|
@@ -1088,6 +1113,7 @@
|
|
|
1088
1113
|
- `loadRemoteExec` — value
|
|
1089
1114
|
- `loadSchedulerStore` — value
|
|
1090
1115
|
- `migrateConfigDirectory` — value
|
|
1116
|
+
- `migrateConfigDocuments` — value
|
|
1091
1117
|
- `mutateSchedulerStore` — value
|
|
1092
1118
|
- `nodeConfigDirectoryIo` — value
|
|
1093
1119
|
- `readBundle` — value
|
|
@@ -234,6 +234,8 @@ server 直接拒启,不是静默降级。上一代 `SEMA_REGISTRY_*` 八名(`URL
|
|
|
234
234
|
- **本地 `config.d` 的一次性迁移(引擎起服)**:调 `@sema-agent/settings-schema/node` 的 `migrateConfigDirectory(dir, { documents, io: nodeConfigDirectoryIo })`,`documents` 填你本地读腿会读的域名集
|
|
235
235
|
(覆盖面外的文件不读不判不写不删;要迁 retired 的 `runtime` 域就得把 `governance` 与 `limits` 一并放进来 —— 迁出的旧值要写那两份,缺了 `runtime` 按 `partial` 拒迁、一字不写);按返回的 `kind` 穷举处置(`applied` / `noop` / `pending` / `refused` / `no-documents` / `unreadable`)。**别按同语义自写**读目录、迁、整域终判、原子写、删退役域文件那一串 ——
|
|
236
236
|
命令行 `migrate` 与这只函数同一份实现;写盘用的就是本包那一只原子写器(契约文档新建 0o644、已有的沿用原模式、符号链接保留)。
|
|
237
|
+
手里已有存量文档原文、不经目录的消费方(例:命令行壳按层读 `settings` 里的 models 域,要的是「引擎起服那一次迁移之后这份文档整域读不读得进」)调同一个内核的同步口 `migrateConfigDocuments(docs, { documents })`(14.5.0,`./node`):
|
|
238
|
+
同一个闭集的无 io 形(`refused` ⇒ 引擎同样不写回、整域落缺省;**反向不成立** —— 引擎对整个目录一判,别的文档被拒也会让整目录不写,单层单域的 `noop` / `migrated` 不保证引擎那一次放行;`docs` 是壳的写手要的迁移后那一份)。一层一次调用;**别自己把 `migrateStoredDocuments` 与 `parseDomain` 按这个顺序再组装一遍**。
|
|
237
239
|
- **会话网页链接**:要给用户一个 web-admin 里的会话链接,调 `webAdminSessionUrl(webBaseUrl, { worker, sessionId })`(`webBaseUrl` 取引擎 `/health` 自报的人用界面根 URL,
|
|
238
240
|
`worker` 取引擎向 web-admin 登记用的 worker 名),得到 `<webBaseUrl>/chat?agent=<worker>&session=<sessionId>`;**别按地址形手拼** —— 路由属于 web-admin,模板与读回的
|
|
239
241
|
`parseWebAdminSessionUrl(link)` 同在本包,路径与参数名只此一份。两位必填:只带 `session` 的链接会落到页面自己挑的 worker 上、可能找不到会话。
|