@sema-agent/server 7.123.0 → 7.124.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +138 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +53 -51
- package/dist/approval-ask-audit-store.d.ts +27 -32
- package/dist/approval-ask-audit-store.js +10 -9
- package/dist/approval-crash-convergence.d.ts +2 -2
- package/dist/approval-crash-convergence.js +5 -5
- package/dist/approval-deny-reasons.d.ts +163 -51
- package/dist/approval-deny-reasons.js +79 -8
- package/dist/approval-policy-settlement.d.ts +4 -1
- package/dist/approval-policy-settlement.js +3 -2
- package/dist/approval-reconciler.d.ts +3 -3
- package/dist/approval-reconciler.js +10 -10
- package/dist/approval.d.ts +1 -1
- package/dist/approval.js +1 -1
- package/dist/audit.js +1 -1
- package/dist/auth-bridge.d.ts +8 -8
- package/dist/auth-bridge.js +6 -6
- package/dist/auth-keys.d.ts +2 -2
- package/dist/auto-mode-face.d.ts +1 -1
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/execution-env.js +1 -0
- package/dist/boot/leader.d.ts +1 -1
- package/dist/boot/memory-consolidation.d.ts +4 -4
- package/dist/boot/org-memory.d.ts +3 -3
- package/dist/boot/org-memory.js +5 -5
- package/dist/boot/resolve-spec.js +7 -7
- package/dist/boot/runner-deps.d.ts +1 -1
- package/dist/boot/runtime-caps.d.ts +1 -1
- package/dist/boot/runtime-caps.js +10 -10
- package/dist/boot/shutdown.d.ts +2 -2
- package/dist/boot/shutdown.js +2 -2
- package/dist/boot/side-query-lane.d.ts +3 -3
- package/dist/boot/{stage-02-registry-pull.d.ts → stage-02-web-admin-pull.d.ts} +2 -2
- package/dist/boot/{stage-02-registry-pull.js → stage-02-web-admin-pull.js} +4 -4
- package/dist/boot/stage-03-store-backend.d.ts +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +6 -6
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +6 -5
- package/dist/boot/stage-08-reapers.d.ts +3 -3
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +3 -3
- package/dist/boot/stage-09-leader.js +7 -7
- package/dist/boot/stage-10-http-server.d.ts +3 -3
- package/dist/boot/stage-10-http-server.js +19 -19
- package/dist/boot/unpriced-ceiling-verdict.d.ts +1 -1
- package/dist/boot/{config-center.d.ts → web-admin.d.ts} +11 -11
- package/dist/boot/{config-center.js → web-admin.js} +38 -22
- package/dist/boot/webfetch-summarize-lane.d.ts +2 -2
- package/dist/brain.d.ts +1 -1
- package/dist/brain.js +1 -1
- package/dist/budget.d.ts +3 -3
- package/dist/canonical-json.d.ts +2 -2
- package/dist/capabilities/center-plugins.d.ts +1 -1
- package/dist/capabilities/center-wiring.d.ts +12 -5
- package/dist/capabilities/center-wiring.js +11 -2
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capabilities/team.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/center-credential-renewer.d.ts +1 -1
- package/dist/center-credential-renewer.js +7 -7
- package/dist/center-credential.d.ts +9 -9
- package/dist/center-credential.js +6 -6
- package/dist/config-catalog.d.ts +7 -10
- package/dist/config-catalog.js +15 -15
- package/dist/config-d-migration.d.ts +32 -0
- package/dist/config-d-migration.js +59 -0
- package/dist/config-invariants.d.ts +14 -4
- package/dist/config-invariants.js +31 -2
- package/dist/config-lkg.d.ts +26 -6
- package/dist/config-lkg.js +43 -14
- package/dist/config-provider.d.ts +23 -19
- package/dist/config-provider.js +24 -26
- package/dist/config-types.d.ts +52 -51
- package/dist/config.d.ts +9 -46
- package/dist/config.js +33 -92
- package/dist/execution-lane-caps.d.ts +1 -1
- package/dist/fleet/drain-entry.d.ts +1 -1
- package/dist/fleet/engine-signal.d.ts +8 -8
- package/dist/fleet/fleet-bus.d.ts +12 -3
- package/dist/fleet/fleet-bus.js +13 -9
- package/dist/fleet/serving-lease.d.ts +3 -3
- package/dist/fleet/serving-lease.js +1 -1
- package/dist/fleet-client.d.ts +7 -7
- package/dist/fleet-client.js +5 -4
- package/dist/fleet-lease.d.ts +3 -3
- package/dist/fleet-lease.js +2 -2
- package/dist/host-lsp-manager.d.ts +1 -1
- package/dist/hosted-posture.d.ts +4 -4
- package/dist/http/admission.js +4 -4
- package/dist/http/checkpoint-ctx.d.ts +20 -3
- package/dist/http/checkpoint-ctx.js +13 -5
- package/dist/http/dispatch.js +2 -2
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/health-self-report.d.ts +4 -4
- package/dist/http/health-self-report.js +3 -3
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/resume-legs.d.ts +4 -3
- package/dist/http/resume-legs.js +31 -18
- package/dist/http/route-ctx.d.ts +8 -0
- package/dist/http/routes/admin-config-refresh.d.ts +1 -1
- package/dist/http/routes/admin-config-refresh.js +1 -1
- package/dist/http/routes/admin-drain.d.ts +1 -1
- package/dist/http/routes/admin-drain.js +5 -4
- package/dist/http/routes/approvals-assistant.js +4 -4
- package/dist/http/routes/capabilities.js +5 -5
- package/dist/http/routes/config-catalog.js +2 -3
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/runs.js +8 -4
- package/dist/http/routes/sessions-list.js +2 -1
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/side-query.js +3 -3
- package/dist/http/routes/tasks.js +3 -2
- package/dist/http/run-meta.d.ts +1 -1
- package/dist/http/run-meta.js +2 -2
- package/dist/http/server.d.ts +7 -7
- package/dist/http/server.js +1 -1
- package/dist/http/stored-body-replay.js +3 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/key-resolver.d.ts +1 -1
- package/dist/leader/wire.d.ts +2 -2
- package/dist/leader/wire.js +2 -1
- package/dist/main.js +1 -1
- package/dist/mcp-tool-faces.d.ts +2 -2
- package/dist/model-entry-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.js +4 -4
- package/dist/model-provider.d.ts +2 -2
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-route-url.d.ts +1 -1
- package/dist/model-route-url.js +2 -1
- package/dist/model-select.d.ts +24 -21
- package/dist/model-select.js +22 -17
- package/dist/observability/metrics.d.ts +1 -1
- package/dist/org-memory-admission.d.ts +1 -1
- package/dist/plugins/approval-ask-store-file.d.ts +4 -2
- package/dist/plugins/approval-ask-store-file.js +3 -3
- package/dist/plugins/approval-ask-store-memory.d.ts +4 -3
- package/dist/plugins/approval-ask-store-memory.js +10 -4
- package/dist/plugins/approval-ask-store-sql.d.ts +16 -2
- package/dist/plugins/approval-ask-store-sql.js +20 -13
- package/dist/plugins/caching-session-store.d.ts +0 -10
- package/dist/plugins/caching-session-store.js +0 -3
- package/dist/plugins/file-run-store.js +9 -3
- package/dist/plugins/leader-run-store-sql.d.ts +3 -2
- package/dist/plugins/leader-run-store-sql.js +3 -2
- package/dist/plugins/local-session-store.d.ts +20 -27
- package/dist/plugins/local-session-store.js +19 -17
- package/dist/plugins/memory-run-store.js +9 -3
- package/dist/plugins/one-time-migrations.js +26 -2
- package/dist/plugins/scheduler-support.d.ts +26 -7
- package/dist/plugins/scheduler-support.js +96 -53
- package/dist/plugins/store-backend.js +1 -1
- package/dist/plugins/store-contracts.d.ts +20 -4
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -1
- package/dist/posture-source.d.ts +1 -1
- package/dist/read-face-posture.d.ts +3 -3
- package/dist/read-face-posture.js +1 -1
- package/dist/run-local.js +6 -3
- package/dist/runtime-caps-resolver.d.ts +4 -4
- package/dist/runtime-caps-resolver.js +2 -2
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sealed-key.d.ts +4 -4
- package/dist/sealed-key.js +3 -2
- package/dist/security.d.ts +3 -3
- package/dist/service-version.d.ts +1 -1
- package/dist/service-version.js +5 -0
- package/dist/session-titler.d.ts +0 -11
- package/dist/session-titler.js +3 -5
- package/dist/shared-memory-scope-authorizer.d.ts +4 -4
- package/dist/task-a2a.d.ts +1 -1
- package/dist/tool-approval.d.ts +43 -4
- package/dist/tool-approval.js +147 -98
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/project.d.ts +3 -3
- package/dist/trace/redact.d.ts +2 -23
- package/dist/trace/redact.js +17 -15
- package/dist/trace/self-minted-wire.d.ts +134 -3
- package/dist/trace/self-minted-wire.js +46 -9
- package/dist/url-refusal.d.ts +35 -0
- package/dist/url-refusal.js +15 -0
- package/dist/user-authored-text.d.ts +31 -0
- package/dist/user-authored-text.js +18 -0
- package/dist/{config-center → web-admin-client}/apply-effective.d.ts +13 -70
- package/dist/{config-center → web-admin-client}/apply-effective.js +100 -119
- package/dist/{config-center → web-admin-client}/apply-ledger.d.ts +2 -2
- package/dist/{config-center → web-admin-client}/center-request.d.ts +7 -7
- package/dist/{config-center → web-admin-client}/center-request.js +5 -4
- package/dist/{config-center → web-admin-client}/effective-keys.d.ts +1 -3
- package/dist/{config-center → web-admin-client}/effective-keys.js +0 -9
- package/dist/{config-center → web-admin-client}/facade.d.ts +6 -6
- package/dist/{config-center → web-admin-client}/facade.js +2 -2
- package/dist/{config-center → web-admin-client}/http-client.d.ts +6 -10
- package/dist/{config-center → web-admin-client}/http-client.js +12 -12
- package/dist/{config-center → web-admin-client}/pull-state.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/read-face.js +4 -4
- package/dist/{config-center → web-admin-client}/read-warnings.d.ts +23 -16
- package/dist/{config-center → web-admin-client}/read-warnings.js +33 -23
- package/dist/{config-center → web-admin-client}/restart-signal.d.ts +5 -3
- package/dist/{config-center → web-admin-client}/restart-signal.js +3 -4
- package/dist/{config-center → web-admin-client}/skills-mcp.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/skills-mcp.js +13 -13
- package/dist/{config-center → web-admin-client}/stage-limits.d.ts +1 -7
- package/dist/{config-center → web-admin-client}/stage-limits.js +6 -7
- package/dist/{config-center → web-admin-client}/types.d.ts +12 -39
- package/package.json +2 -2
- package/skills/find-skills.md +2 -2
- package/dist/url-shape.d.ts +0 -31
- package/dist/url-shape.js +0 -41
- /package/dist/{config-center → web-admin-client}/apply-ledger.js +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.js +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.js +0 -0
- /package/dist/{config-center → web-admin-client}/pull-state.js +0 -0
- /package/dist/{config-center → web-admin-client}/read-face.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/types.js +0 -0
|
@@ -3,7 +3,7 @@ export const COMPOSITE_CAPABILITY_MEMBERS = {
|
|
|
3
3
|
fleet: ["stream", "sessionScope", "observe", "resume", "bgNotifyFailClosed", "steerPriority", "hookFailureNotice", "leaseClient", "generationEcho"],
|
|
4
4
|
memoryEngine: ["backend", "vectorMode"],
|
|
5
5
|
store: ["declared", "backend", "durable", "degradedReason", "sql"],
|
|
6
|
-
centerWiring: ["effectivePull", "effectiveRevision", "lease", "leaseHealth", "failMode", "usageReport", "issuer", "caller"],
|
|
6
|
+
centerWiring: ["effectivePull", "effectiveRevision", "lease", "leaseHealth", "failMode", "usageReport", "issuer", "worker", "caller"],
|
|
7
7
|
webSearch: ["backend"],
|
|
8
8
|
background: ["listFace", "exitFaces"],
|
|
9
9
|
taskSettings: ["permissions", "permissionMode", "model", "outputStyle", "env", "hooks"],
|
|
@@ -222,12 +222,12 @@ export function centerScenarios(deps, specs, builtinNames, logger) {
|
|
|
222
222
|
if (spec.enabled === false)
|
|
223
223
|
continue;
|
|
224
224
|
if (typeof spec.name !== "string" || !SCENARIO_NAME_RE.test(spec.name)) {
|
|
225
|
-
logger?.warn("
|
|
225
|
+
logger?.warn("web_admin_scenario_invalid", { name: String(spec?.name), reason: "name must match " + String(SCENARIO_NAME_RE) });
|
|
226
226
|
continue;
|
|
227
227
|
}
|
|
228
228
|
const toolset = Object.hasOwn(TOOLSETS, spec.toolset) ? TOOLSETS[spec.toolset] : undefined;
|
|
229
229
|
if (!toolset) {
|
|
230
|
-
logger?.warn("
|
|
230
|
+
logger?.warn("web_admin_scenario_invalid", { name: spec.name, reason: `unknown toolset "${String(spec.toolset)}" (have: ${Object.keys(TOOLSETS).join(", ")})` });
|
|
231
231
|
continue;
|
|
232
232
|
}
|
|
233
233
|
const name = spec.name;
|
|
@@ -83,7 +83,7 @@ export interface TeamTemplate {
|
|
|
83
83
|
systemPrompt?: string;
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
-
/** Built-in team templates (placeholder until the
|
|
86
|
+
/** Built-in team templates (placeholder until the web-admin supplies them). */
|
|
87
87
|
export declare const BUILTIN_TEAMS: Record<string, TeamTemplate>;
|
|
88
88
|
export declare function registerTeams(extra: Record<string, TeamTemplate>): void;
|
|
89
89
|
export declare function getTeam(name: string): TeamTemplate | undefined;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* 在本仓缺省场景下那会连 Agent 与 Task 四件一起卷进候选(与 CC 姿态相反),所以我们**刻意不开**;
|
|
19
19
|
* 🔴 该判断在 core 5.40.0 / 7.15.0 上**逐字仍成立**(亲验):两次收窄动的都是「候选集是谁」,而
|
|
20
20
|
* Agent / Task 四件是 **caller spec 工具**,收窄后照样在候选池里 —— 不开 auto 的理由没有被消解。
|
|
21
|
-
* 我方**从不接线**这个座:全树零 `RunnerDeps.deferMode` 赋值(
|
|
21
|
+
* 我方**从不接线**这个座:全树零 `RunnerDeps.deferMode` 赋值(web-admin 曾有同形近名 `deferModelPlane`,
|
|
22
22
|
* 与本轴无关,已于 S-668 波 0 退役)。机器钉见 test/tool-defer.test.ts 的 [ref] 件⑤ 格 —— 这条「零接线」正是本仓
|
|
23
23
|
* 在两个 core 世代之间行为逐字不变的**全部**依据,所以它值一根钉而不只是一句注。
|
|
24
24
|
* - per-tool `ToolSpec.defer:true` 是本 helper 施加的那一位。**7.15.0 起它是一次「进候选集」的声明,
|
|
@@ -59,7 +59,7 @@ export declare function buildCenterCredentialHealthView(health: CenterCredential
|
|
|
59
59
|
export interface CenterCredentialRenewerDeps {
|
|
60
60
|
/** 文件形凭证来源(续签器只从它的取值被触发;文件路径取自它的 `origin`)。 */
|
|
61
61
|
readonly source: CenterCredentialSource;
|
|
62
|
-
/** `
|
|
62
|
+
/** `webAdmin.url` —— 续签只向与它同源的 `issuer` 发。 */
|
|
63
63
|
readonly centerUrl: string;
|
|
64
64
|
/** `CENTER_CREDENTIAL_RENEW_LEAD_MS`(已过 {@link parseCenterCredentialRenewLeadMs});0 = 关续签。 */
|
|
65
65
|
readonly leadMs: number;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { closeSync, fchmodSync, fsyncSync, openSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { WEB_ADMIN_ENV, WebAdminCredentialEnvelope, parseWebAdminCredentialFile } from "@sema-agent/settings-schema";
|
|
5
5
|
import { ACCESS_TOKEN_TTL_SECONDS, OAuthErrorResponse, TokenGrantResponse } from "@sema-agent/settings-schema/api/auth";
|
|
6
|
-
import { CENTER_PULL_TIMEOUT_MS, centerTokenRefresh } from "./
|
|
6
|
+
import { CENTER_PULL_TIMEOUT_MS, centerTokenRefresh } from "./web-admin-client/center-request.js";
|
|
7
7
|
export const CENTER_CREDENTIAL_RENEW_LEAD_KEY = "CENTER_CREDENTIAL_RENEW_LEAD_MS";
|
|
8
8
|
export const CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS = 5 * 60_000;
|
|
9
9
|
export const CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS = CENTER_PULL_TIMEOUT_MS;
|
|
@@ -42,7 +42,7 @@ function readFileNow(path) {
|
|
|
42
42
|
const code = err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : "read error";
|
|
43
43
|
return code === "ENOENT" ? { kind: "no-session" } : { kind: "unreadable", code };
|
|
44
44
|
}
|
|
45
|
-
const file =
|
|
45
|
+
const file = parseWebAdminCredentialFile(text);
|
|
46
46
|
return file.kind === "envelope" ? { kind: "session", envelope: file.envelope, refreshDigest: digest16(file.envelope.refreshToken) } : { kind: "no-session" };
|
|
47
47
|
}
|
|
48
48
|
const TMP_RANDOM_BYTES = 6;
|
|
@@ -83,7 +83,7 @@ function sessionExpiryOf(read) {
|
|
|
83
83
|
export function createCenterCredentialRenewer(deps) {
|
|
84
84
|
const { source, centerUrl, leadMs, logger } = deps;
|
|
85
85
|
if (source.origin.from !== "file")
|
|
86
|
-
throw new Error(`center credential renewer needs the ${
|
|
86
|
+
throw new Error(`center credential renewer needs the ${WEB_ADMIN_ENV.tokenFile} form (got ${source.origin.from})`);
|
|
87
87
|
const path = source.origin.path;
|
|
88
88
|
const now = deps.now ?? Date.now;
|
|
89
89
|
const tickMs = Math.min(Math.max(leadMs / 2, 1000), 60_000);
|
|
@@ -147,7 +147,7 @@ export function createCenterCredentialRenewer(deps) {
|
|
|
147
147
|
return;
|
|
148
148
|
unavailableStreak = read.failure;
|
|
149
149
|
logger.warn("center_credential_unreadable", {
|
|
150
|
-
key:
|
|
150
|
+
key: WEB_ADMIN_ENV.tokenFile,
|
|
151
151
|
failure: read.failure,
|
|
152
152
|
reason: read.reason,
|
|
153
153
|
note: "no center request carries this credential until the file holds a usable one again (each consumer takes its own failure arm); warned once per unavailability streak",
|
|
@@ -172,7 +172,7 @@ export function createCenterCredentialRenewer(deps) {
|
|
|
172
172
|
subject = refreshDigest;
|
|
173
173
|
held = undefined;
|
|
174
174
|
if (reason === "issuer_mismatch") {
|
|
175
|
-
logger.warn("center_credential_issuer_mismatch", { ...fields, note:
|
|
175
|
+
logger.warn("center_credential_issuer_mismatch", { ...fields, note: `the sign-in envelope was issued by a different center than ${WEB_ADMIN_ENV.url} — its refresh token is never sent there; the access token is used until it expires, then sign in again against this center` });
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
178
|
logger.warn("center_credential_renew_failed", {
|
|
@@ -297,7 +297,7 @@ export function createCenterCredentialRenewer(deps) {
|
|
|
297
297
|
judgeRefusal(usedDigest, "malformed_grant", { status: res.status });
|
|
298
298
|
return;
|
|
299
299
|
}
|
|
300
|
-
const next =
|
|
300
|
+
const next = WebAdminCredentialEnvelope.safeParse({
|
|
301
301
|
kind: envelope.kind,
|
|
302
302
|
accessToken: grant.data.access_token,
|
|
303
303
|
refreshToken: grant.data.refresh_token,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
/** 凭证种类闭集。新增一个种类 = 本文件与 `
|
|
1
|
+
import { type WebAdminCredentialDeclaration } from "@sema-agent/settings-schema";
|
|
2
|
+
/** 凭证种类闭集。新增一个种类 = 本文件与 `web-admin-client/center-request.ts` 的穷举 switch 当场编译红。 */
|
|
3
3
|
export type CenterCredentialKind = "pull-token" | "user-jwt" | "user-session" | "none";
|
|
4
4
|
/** 一次取值失败的成因闭集(见模块头注「四个成因」;boot 判决 {@link judgeCenterCredentialAtBoot} 对它穷举)。 */
|
|
5
5
|
export type CenterCredentialFailure = "missing" | "malformed" | "multi_tenant_session" | "expired";
|
|
@@ -84,7 +84,7 @@ export declare class CenterCredentialUnavailableError extends Error {
|
|
|
84
84
|
* 凭证来源的构造入参:settings-schema 的声明形,文件形**必须**带上本部署的租户姿态(多租户拒人的会话信封,见模块头注)——
|
|
85
85
|
* 姿态只在文件形有意义(env 值形是部署注入的拉取令牌,永远不是信封),所以只在那一臂上必填:漏传 = 编译红,不是静默单租户。
|
|
86
86
|
*/
|
|
87
|
-
export type CenterCredentialSourceSpec = Exclude<
|
|
87
|
+
export type CenterCredentialSourceSpec = Exclude<WebAdminCredentialDeclaration, {
|
|
88
88
|
from: "file";
|
|
89
89
|
}> | {
|
|
90
90
|
readonly from: "file";
|
|
@@ -92,16 +92,16 @@ export type CenterCredentialSourceSpec = Exclude<CenterCredentialDeclaration, {
|
|
|
92
92
|
readonly multiTenant: boolean;
|
|
93
93
|
};
|
|
94
94
|
/**
|
|
95
|
-
* 读两个声明键 —— 判定全交 settings-schema 的判官 `
|
|
95
|
+
* 读两个声明键 —— 判定全交 settings-schema 的判官 `judgeWebAdminCredentialDeclaration`(S-742:二选一规则的**唯一**实现;空串 = 未设、
|
|
96
96
|
* 不 trim;两个都设 ⇒ throw,boot 拒启,拒句点名两个键、不回显值)。本仓只多读方自己的那一步:文件路径按本进程 cwd 解析成绝对
|
|
97
97
|
* 路径(之后 cwd 变了也读同一个文件 —— 纯契约不碰 `node:path`,这一步归读方)。
|
|
98
98
|
*/
|
|
99
|
-
export declare function readCenterCredentialDeclaration(env?: Readonly<Record<string, string | undefined>>):
|
|
99
|
+
export declare function readCenterCredentialDeclaration(env?: Readonly<Record<string, string | undefined>>): WebAdminCredentialDeclaration;
|
|
100
100
|
/** 拉取令牌形的身份 = worker(拉取令牌代表 worker,不是人)。种类判别与 formatVersion 1 LKG 的一次性迁移
|
|
101
101
|
* (`config-lkg.ts` `migrateV1Lkg`:v1 信封只记 worker,只有这一形的身份推得出来)共用这一只。 */
|
|
102
102
|
export declare function pullTokenIdentity(worker: string | undefined): string;
|
|
103
103
|
/**
|
|
104
|
-
* 凭证来源工厂(活对象:文件形每次取值读盘并记健康态)。`worker` = `
|
|
104
|
+
* 凭证来源工厂(活对象:文件形每次取值读盘并记健康态)。`worker` = `SEMA_WEB_ADMIN_WORKER`(拉取令牌形的身份)。
|
|
105
105
|
* `readFile` / `now` 只为测试注入。
|
|
106
106
|
*/
|
|
107
107
|
export declare function createCenterCredentialSource(spec: CenterCredentialSourceSpec, worker: string | undefined, deps?: {
|
|
@@ -128,13 +128,13 @@ export declare const CENTER_CREDENTIAL_REFUSAL_CODES: {
|
|
|
128
128
|
};
|
|
129
129
|
export type CenterCredentialRefusalCode = (typeof CENTER_CREDENTIAL_REFUSAL_CODES)[keyof typeof CENTER_CREDENTIAL_REFUSAL_CODES];
|
|
130
130
|
/**
|
|
131
|
-
* S-698 —— boot 读到的凭证怎么处置(`boot/
|
|
131
|
+
* S-698 —— boot 读到的凭证怎么处置(`boot/web-admin.ts` 执行,规则只在这里):
|
|
132
132
|
* · `missing` / `expired` ⇒ 响亮(error 级一行),照常起服 —— 前者是「此刻没有凭证」(登出形,文件出现即恢复),
|
|
133
133
|
* 后者续签器立刻续签;
|
|
134
134
|
* · `malformed` / `multi_tenant_session` ⇒ **拒启**,拒句带码、点名文件键,不回显内容(`reason` 由本模块铸,本来就不含内容)。
|
|
135
135
|
*/
|
|
136
136
|
export declare function judgeCenterCredentialAtBoot(read: CenterCredentialRead): CenterCredentialBootVerdict;
|
|
137
|
-
/** 需要作用域 worker 的拉取令牌(`wpt_<self>`)却没配 `
|
|
137
|
+
/** 需要作用域 worker 的拉取令牌(`wpt_<self>`)却没配 `SEMA_WEB_ADMIN_WORKER` ⇒ 中心对 per-principal caps
|
|
138
138
|
* 逐次 403(caps fail-closed 拒全部 workflow)。boot 期一次性诊断用;判据只读本模块的种类判别。 */
|
|
139
139
|
export declare function scopedTokenNeedsWorker(read: CenterCredentialRead, worker: string | undefined): boolean;
|
|
140
140
|
/**
|
|
@@ -148,7 +148,7 @@ export declare function centerStateBin(worker: string | undefined, identity: str
|
|
|
148
148
|
export declare function credentialIdentityNow(source: CenterCredentialSource): string | undefined;
|
|
149
149
|
/**
|
|
150
150
|
* [ref] E-15 ② —— 一次中心往返的答复**仍属于此刻的凭证**:发出它所用的身份 = 此刻的身份。
|
|
151
|
-
* 拉配置(`boot/
|
|
151
|
+
* 拉配置(`boot/web-admin.ts` `pullEffective`)与两条 `?principal=` 视图(`web-admin-client/http-client.ts`)
|
|
152
152
|
* 都只问这一只;不成立 ⇒ 答复丢弃,当这次拉取失败(各走各的失败臂)。
|
|
153
153
|
*/
|
|
154
154
|
export declare function answerStillCurrent(source: CenterCredentialSource, usedIdentity: string | undefined): usedIdentity is string;
|
|
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { resolve as resolvePath } from "node:path";
|
|
4
4
|
import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
5
|
-
import {
|
|
5
|
+
import { WEB_ADMIN_ENV, judgeWebAdminCredentialDeclaration, parseWebAdminCredentialFile } from "@sema-agent/settings-schema";
|
|
6
6
|
export class CenterCredentialUnavailableError extends Error {
|
|
7
7
|
reason;
|
|
8
8
|
constructor(reason) {
|
|
@@ -12,7 +12,7 @@ export class CenterCredentialUnavailableError extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
export function readCenterCredentialDeclaration(env = process.env) {
|
|
15
|
-
const declared =
|
|
15
|
+
const declared = judgeWebAdminCredentialDeclaration(env, "access");
|
|
16
16
|
return declared.from === "file" ? { from: "file", path: resolvePath(declared.path) } : declared;
|
|
17
17
|
}
|
|
18
18
|
const digest16 = (s) => createHash("sha256").update(s, "utf8").digest("hex").slice(0, 16);
|
|
@@ -43,8 +43,8 @@ export function pullTokenIdentity(worker) {
|
|
|
43
43
|
const NONE = { ok: true, kind: "none", identity: "none" };
|
|
44
44
|
const PARSER_EMPTY_REASON = "empty";
|
|
45
45
|
function readCredentialText(text, spec, worker, now) {
|
|
46
|
-
const where = `${
|
|
47
|
-
const file =
|
|
46
|
+
const where = `${WEB_ADMIN_ENV.tokenFile} ${spec.path}`;
|
|
47
|
+
const file = parseWebAdminCredentialFile(text);
|
|
48
48
|
switch (file.kind) {
|
|
49
49
|
case "bare":
|
|
50
50
|
return classify(file.bearer, worker);
|
|
@@ -105,7 +105,7 @@ export function createCenterCredentialSource(spec, worker, deps = {}) {
|
|
|
105
105
|
}
|
|
106
106
|
catch (err) {
|
|
107
107
|
const code = err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : "read error";
|
|
108
|
-
return { ok: false, failure: "missing", reason: `${
|
|
108
|
+
return { ok: false, failure: "missing", reason: `${WEB_ADMIN_ENV.tokenFile} ${spec.path} is unreadable (${code})` };
|
|
109
109
|
}
|
|
110
110
|
return readCredentialText(text, spec, worker, now());
|
|
111
111
|
};
|
|
@@ -155,7 +155,7 @@ export function judgeCenterCredentialAtBoot(read) {
|
|
|
155
155
|
return {
|
|
156
156
|
verdict: "refuse",
|
|
157
157
|
code,
|
|
158
|
-
sentence: `center credential refused at boot [${code}]: ${read.reason}. The file must hold one bare token line, or the sign-in envelope the shell writes (a
|
|
158
|
+
sentence: `center credential refused at boot [${code}]: ${read.reason}. The file must hold one bare token line, or the sign-in envelope the shell writes (a WebAdminCredentialEnvelope JSON object); sign in again so the shell rewrites it, or fix the file — its content is never echoed.`,
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
161
|
case "multi_tenant_session": {
|
package/dist/config-catalog.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { type RetiredEnvWire } from "@sema-agent/settings-schema";
|
|
1
2
|
import type { ServiceConfig, ServiceConfigGroups } from "./config-types.js";
|
|
2
|
-
import type { ConfigKnobRecord
|
|
3
|
-
import type { RestartSlice } from "./
|
|
3
|
+
import type { ConfigKnobRecord } from "./config.js";
|
|
4
|
+
import type { RestartSlice } from "./web-admin-client/restart-signal.js";
|
|
4
5
|
export declare const CATALOG_VALUE_TYPES: readonly ["boolean", "number", "string", "enum", "csv", "json", "path", "url", "secret", "opaque"];
|
|
5
6
|
export type CatalogValueType = (typeof CATALOG_VALUE_TYPES)[number];
|
|
6
7
|
export declare const CATALOG_TIMINGS: readonly ["hot", "restart", "deploy", "inert"];
|
|
@@ -14,7 +15,7 @@ export declare const CATALOG_RESTART_SLICES: readonly RestartSlice[];
|
|
|
14
15
|
export interface CatalogCenterSeatSpec {
|
|
15
16
|
readonly domain: string;
|
|
16
17
|
readonly path: string;
|
|
17
|
-
/** `centerManagedKeys`(
|
|
18
|
+
/** `centerManagedKeys`(web-admin 在管键集)里查「在管」用的键;models 面统一 "models"。 */
|
|
18
19
|
readonly centerKey: string;
|
|
19
20
|
readonly precedence: "center-wins" | "env-wins";
|
|
20
21
|
readonly timing: "hot" | "restart" | "inert";
|
|
@@ -144,12 +145,8 @@ export interface ConfigCatalogInputs {
|
|
|
144
145
|
* 它值级 import 了 core 与契约包(词表 / 中心凭证键名 / S-866 ⑤a 的 `DOMAIN_SCHEMAS` 座位门);这一格照旧由调用方传,
|
|
145
146
|
* 只因为它是 wire 上的一段列表、由路由决定取哪一份。 */
|
|
146
147
|
readonly centerDomains: readonly string[];
|
|
147
|
-
/**
|
|
148
|
+
/** web-admin 当前在管的 ServiceConfig 键集(缺席 = 没有配置管道)。 */
|
|
148
149
|
readonly centerManagedKeys?: ReadonlySet<string> | undefined;
|
|
149
|
-
/** S-885:退役 env 墓碑表的投影 —— 调用方传 `config.ts` 的 `buildRetiredEnvWire()`(同一张 `RETIRED_ENVS`,不手抄)。**必填**:
|
|
150
|
-
* 本模块不能值引 config.ts(`server-secret-env.ts` 值引本模块 ⇒ config → server-secret-env → config-catalog → config 成环,
|
|
151
|
-
* 干净进程直接 import 本模块即 TDZ 崩;codex r1 F2)⇒ 由调用方注入,缺了就是编译错而不是一张静默空表。 */
|
|
152
|
-
readonly retired: readonly RetiredEnvWire[];
|
|
153
150
|
}
|
|
154
151
|
export interface ConfigCatalogWire {
|
|
155
152
|
readonly catalogVersion: 1;
|
|
@@ -164,8 +161,8 @@ export interface ConfigCatalogWire {
|
|
|
164
161
|
readonly centerManaged: number;
|
|
165
162
|
};
|
|
166
163
|
readonly rows: readonly ConfigCatalogRow[];
|
|
167
|
-
/** S-885(7.106.0)—— 退役 env 墓碑表(
|
|
168
|
-
* rows 是「本进程会读的 env」,这里是「本进程会拒的旧名」—— 一个名字不会同时在两处(墓碑名没有读取点)。 */
|
|
164
|
+
/** S-885(7.106.0)—— 退役 env 墓碑表(settings-schema `RETIRED_ENVS`,本进程起服按它拒启)的投影 `buildRetiredEnvWire()`,**恒在场**
|
|
165
|
+
* (可为空数组)。与 `rows` 分开:rows 是「本进程会读的 env」,这里是「本进程会拒的旧名」—— 一个名字不会同时在两处(墓碑名没有读取点)。 */
|
|
169
166
|
readonly retired: readonly RetiredEnvWire[];
|
|
170
167
|
}
|
|
171
168
|
/**
|
package/dist/config-catalog.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
|
|
2
2
|
import { GLOBAL_SCOPE } from "@sema-agent/settings-schema/api/scopes";
|
|
3
|
-
import {
|
|
3
|
+
import { WEB_ADMIN_ENV, DOMAIN_SCHEMAS, buildRetiredEnvWire } from "@sema-agent/settings-schema";
|
|
4
4
|
import { CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS, CENTER_CREDENTIAL_RENEW_LEAD_MAX_MS, CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS } from "./center-credential-renewer.js";
|
|
5
5
|
import { THINKING_FORMATS } from "./model-compat.js";
|
|
6
6
|
import { AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, CENTER_BASE_URL_RULE, CONFIG_PROVIDER_WORDS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS, FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
|
|
@@ -72,7 +72,7 @@ export const CONFIG_CATALOG = [
|
|
|
72
72
|
r("ATTACHMENT_ORPHAN_GRACE_MS", "limitsHttp", "number", "附件孤儿对象 GC 宽限(ms;0=关本腿,坏值回默认+warn)", { staticDefault: "3600000", danger: DATA, center: cLimits("attachmentOrphanGraceMs") }),
|
|
73
73
|
r("ATTACHMENT_UNBOUND_TTL_MS", "limitsHttp", "number", "未绑定附件的 TTL(ms)", { staticDefault: String(24 * 3600 * 1000), danger: DATA, configKey: "attachmentUnboundTtlMs", center: cLimits("attachmentUnboundTtlMs") }),
|
|
74
74
|
r("AUTH_BRIDGE_AUDIENCE", "auth", "string", "registry SSO-JWT 验签期望 aud", { danger: SEC }),
|
|
75
|
-
r("AUTH_BRIDGE_ISSUER", "auth", "url", "registry SSO-JWT issuer(auth bridge 的开关之一;S-671 / C-R100 ① 起**不是**托管证据 —— 多租户只认 REQUIRE_PRINCIPAL=true)", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 桥的装配处回退到
|
|
75
|
+
r("AUTH_BRIDGE_ISSUER", "auth", "url", "registry SSO-JWT issuer(auth bridge 的开关之一;S-671 / C-R100 ① 起**不是**托管证据 —— 多租户只认 REQUIRE_PRINCIPAL=true)", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 桥的装配处回退到 webAdmin.baseUrl(配了 center 即默认开桥);显式值与回退值在托管判定上同判(都不算)", configKey: "authBridgeIssuer" }),
|
|
76
76
|
r("AUTH_BRIDGE_JWKS_URL", "auth", "url", "registry SSO-JWT 的 JWKS 地址(缺省从 issuer 派生)", { danger: SEC }),
|
|
77
77
|
r("AUTONOMY", "auth", "enum", "运维 autonomy 模式(read-only ⇒ 只读手 + 写面 roots,每段续跑重折,子代同受、委派照挂;ask ⇒ 恒 shellGate,每段重折;plan ⇒ 提交入口的模式词地板:新提交的任务以 permissionMode:plan 起步,请求词钳到它之下,写与非只读调用在调用时问 / 拒,批准计划之后整条 run 按批准的档跑完);center 腿热且 non-sticky(plan 地板只作用于之后提交的任务)", { danger: SEC, enumValues: AUTONOMY_MODES, configKey: "autonomy", center: cGov("autonomy") }),
|
|
78
78
|
r("AUTO_MODE_CLASSIFIER_FALLBACK", "approval", "enum", "auto 分类器席答不出裁决时的梯子(main=主模型代判 / none=只留分类器那一级,整条拒)", { enumValues: ["main", "none"], danger: SEC, derivedDefaultNote: "未设/空串 ⇒ 不铸键,跟随引擎缺省 main(core 7.18.0 起;空串按未设,与其余词表旋钮同判);none = 7.77.0 及以前的行为逐字;词表外的值启动期拒启。权限轴放宽,升级须知见 DEPLOY-PREREQS", configKey: "autoModeClassifierFallback" }),
|
|
@@ -88,7 +88,7 @@ export const CONFIG_CATALOG = [
|
|
|
88
88
|
r("BG_AGENT_RETENTION_MS", "orchestration", "number", "TERMINAL 后台 agent 行保留期(ms,reaper 按 scope 清)", { staticDefault: String(7 * 24 * 60 * 60 * 1000), danger: DATA, configKey: "backgroundAgentRetentionMs" }),
|
|
89
89
|
r("BG_AGENT_STALE_RUNNING_MS", "orchestration", "number", "running 后台 agent 行写者心跳断更超此值翻 failed(ms;下限 10min 硬钳)", { staticDefault: String(15 * 60_000), configKey: "backgroundAgentStaleRunningMs" }),
|
|
90
90
|
r("BIND_HOST", "auth", "string", "监听绑址(显式值恒生效)", { danger: SEC, derivedDefaultNote: "缺席:无鉴权写面(allowUnauthedWrites ∧ 零凭证)自动收窄 127.0.0.1,其余全接口", resolve: (c) => c.bindHost ?? null }),
|
|
91
|
-
r("CENTER_CREDENTIAL_RENEW_LEAD_MS", "integrations", "number", `center 凭证续签提前量(ms;${
|
|
91
|
+
r("CENTER_CREDENTIAL_RENEW_LEAD_MS", "integrations", "number", `center 凭证续签提前量(ms;${WEB_ADMIN_ENV.tokenFile} 指向人的登录会话信封时,访问令牌离到期 ≤ 本值即由引擎续签并原子改写同一文件;0 = 关续签只读信封;其余须为 [${CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS}, ${CENTER_CREDENTIAL_RENEW_LEAD_MAX_MS}] 内整数,坏值拒启;裸行 / 值形凭证不续签)`, { staticDefault: String(CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS), danger: SEC, configKey: "centerCredentialRenewLeadMs" }),
|
|
92
92
|
r("COMPLIANCE_ADDITIONAL_DENIES", "auth", "csv", "合规档位之上额外拒绝的能力名(只能收紧;词表=core 闭集,拼错拒启)", { danger: SEC }),
|
|
93
93
|
r("COMPLIANCE_PROFILE", "auth", "enum", "本部署合规档位(core compliancePostureResolver 供数;拼错拒启)", { danger: SEC, enumValues: ["hipaa", "zdr"], resolve: (c) => c.compliancePosture?.profile ?? null }),
|
|
94
94
|
r("CONFIG_BOOT_FETCH_BUDGET_MS", "integrations", "number", "boot 首拉配置的抢答窗(ms;过窗以 env/兜底起服,拉取转后台热应用)", { staticDefault: "1500", configKey: "configBootFetchBudgetMs" }),
|
|
@@ -97,7 +97,7 @@ export const CONFIG_CATALOG = [
|
|
|
97
97
|
r("CONFIG_LKG_ENABLED", "integrations", "boolean", "last-known-good 配置盘缓存读写开关(仅远程 provider 有意义)", { staticDefault: "true", configKey: "configLkgEnabled" }),
|
|
98
98
|
r("CONFIG_LKG_PATH", "integrations", "path", "LKG 落盘路径(设了 ⇒ 派生一条读拒行 = 这条绝对路径:根锚定,只守这一个文件,经符号链接的目录时引擎沿链连目标一起守;大小写不敏感;用户仓里同尾的路径读得到;空串拒启)", { derivedDefaultNote: "缺席 ⇒ 配置根(SEMA_CONFIG_DIR,缺省 ~/.sema)下按 worker + 凭证身份分箱派生", configKey: "configLkgPath" }),
|
|
99
99
|
r("CONFIG_LOCAL_DIR", "integrations", "path", "本地配置源根(config.d/<domain>.json);兼作数据根回落链一环", { configKey: "configLocalDir", danger: DATA }),
|
|
100
|
-
r("CONFIG_PROVIDER", "integrations", "enum", "配置源双模选择(闭集,逐字比较;认不出的词 ⇒ 拒启,S-841)", { enumValues: CONFIG_PROVIDER_WORDS, derivedDefaultNote:
|
|
100
|
+
r("CONFIG_PROVIDER", "integrations", "enum", "配置源双模选择(闭集,逐字比较;认不出的词 ⇒ 拒启,S-841)", { enumValues: CONFIG_PROVIDER_WORDS, derivedDefaultNote: `缺席:${WEB_ADMIN_ENV.url} 在场 ⇒ remote,否则纯 env(无管道)`, configKey: "configProvider" }),
|
|
101
101
|
r("CONFIG_REQUIRE_ROSTER", "integrations", "boolean", "远程 registry 非 dryRun 时布防 ready 门:roster 未落地 ⇒ 计费提交 503 + /health ready:false(严格 'true')", { staticDefault: "false" }),
|
|
102
102
|
r("CORS_ORIGIN", "auth", "csv", "CORS 白名单(单值 v1 兼容回显;多值按请求 Origin 匹配 + Vary;未设=无 CORS 面)", { danger: SEC, configKey: "corsOrigins" }),
|
|
103
103
|
r("COST_QUOTA_WINDOW_SEC", "limitsHttp", "number", "per-principal 成本配额窗长(秒);SQL 腿窗长进桶主键 ⇒ center 腿显式 deferred(重启生效)", { staticDefault: "86400", danger: BILL, configKey: "costQuotaWindowSec", center: cLimits("costQuotaWindowSec", { timing: "restart", restartReason: "cost-quota-window" }) }),
|
|
@@ -105,7 +105,7 @@ export const CONFIG_CATALOG = [
|
|
|
105
105
|
r("CROSS_SESSION_INBOUND", "approval", "enum", "入站跨会话消息的部署/组织层治理(CC settings 键名 crossSessionInbound 的 managed 层;未设=本层不表态≠ next-turn;词表外拒启)", { danger: SEC, enumValues: [...CROSS_SESSION_INBOUND_SETTINGS], configKey: "crossSessionInbound", derivedDefaultNote: "缺席 ⇒ managed 层不设,由其余层与 mode-parity 决定", resolve: (c) => c.crossSessionInbound ?? null }),
|
|
106
106
|
r("DB_BACKEND", "store", "enum", "store 后端引擎选择(mysql 兼容/pg/本机文件店/纯内存)", { enumValues: STORE_BACKENDS, danger: DATA, derivedDefaultNote: "裸 boot ⇒ local(多租户裸 boot ⇒ memory + notice);有 SQL 信号 ⇒ mysql", configKey: "dbBackend", resolve: (c) => c.dbBackend }),
|
|
107
107
|
r("DB_QUERY_TIMEOUT_MS", "store", "number", "per-query DB 超时(ms;坏值回默认姿态+warn,绝不 NaN 进 driver)", { derivedDefaultNote: "缺席 ⇒ 池级不设/计数族 30s(按引擎)", configKey: "dbQueryTimeoutMs" }),
|
|
108
|
-
r("DEFAULT_SCENARIO", "integrations", "string", "出厂缺省场景(编码产品出厂态;通用中性 persona 用 default 显式选回);必须是本部署在册的场景名(内建 ∪
|
|
108
|
+
r("DEFAULT_SCENARIO", "integrations", "string", "出厂缺省场景(编码产品出厂态;通用中性 persona 用 default 显式选回);必须是本部署在册的场景名(内建 ∪ web-admin 声明的),不在册 ⇒ 拒启", { staticDefault: "code", configKey: "defaultScenario" }),
|
|
109
109
|
r("DELEGATION_MAX_CONCURRENT", "orchestration", "number", "委派入口并发帽", { derivedDefaultNote: "缺席 ⇒ 不铸键,core 自缺省(CC parity 20)" }),
|
|
110
110
|
r("DELEGATION_MAX_PER_SESSION", "orchestration", "number", "委派入口每会话累计帽", { derivedDefaultNote: "缺席 ⇒ 不铸键,core 自缺省(CC parity 200)" }),
|
|
111
111
|
r("DEVICE_DELIVERY_TIMEOUT_MS", "orchestration", "number", "device 车道等在途槽上限(ms;到期 device.busy)", { derivedDefaultNote: "缺席 ⇒ adapter 默认 10s" }),
|
|
@@ -401,16 +401,16 @@ export const CONFIG_CATALOG = [
|
|
|
401
401
|
r("SEMA_NPM_REGISTRY", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 npm registry 地址"),
|
|
402
402
|
r("SEMA_PARENT_PID", "limitsHttp", "number", "引擎 parent 监视(壳 pid;被过继走或该 pid 不存在即把引擎标成孤儿——不直接停机,改用 ENGINE_ORPHAN_LINGER_MS 窗走同一条租约自退;0/负数语义完全不同 ⇒ 拒启)", { derivedDefaultNote: "缺席 ⇒ 看门狗不装配", configKey: "parentPid" }),
|
|
403
403
|
r("SEMA_PIP_INDEX", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 pip index 地址"),
|
|
404
|
-
r("SEMA_REGISTRY_ADMIN_TOKEN", "integrations", "secret", "写 center 的管理凭证(仅 boot 上报执行公钥用;刻意不入 ServiceConfig —— 不进 configHash/日志面)", { danger: SEC, scope: "bootstrap-only" }),
|
|
405
|
-
r("SEMA_REGISTRY_DRY_RUN", "integrations", "boolean", "center 腿 dry-run(拉而不应用)", { staticDefault: "false" }),
|
|
406
|
-
r("SEMA_REGISTRY_LIVE_URL", "integrations", "url", `fleet 活性组(心跳 / 注销、服务租约 = settings-schema FLEET_LIVE_PATHS)的中心基址:给管理面在就绪门后、活性面另走一条不看就绪的入口的部署。缺席 = SEMA_REGISTRY_URL。${CENTER_BASE_URL_RULE}`),
|
|
407
|
-
r(CENTER_CREDENTIAL_ENV.token, "integrations", "secret", `center 拉取凭据(值形;与 ${CENTER_CREDENTIAL_ENV.tokenFile} 二选一,都设=拒启)`, { danger: SEC }),
|
|
408
|
-
r(CENTER_CREDENTIAL_ENV.tokenFile, "integrations", "path", `center 拉取凭据文件(每条中心请求都重读 ⇒ 轮换 / 换账号不重启;与 ${CENTER_CREDENTIAL_ENV.token} 二选一,都设=拒启;内容两种类:裸行 = 静态 bearer / JSON 信封 user-session = 人的登录会话(可续签,S-698);文件不可读 / 为空 = 响亮 + 健康态 unreadable,不当作无凭证;内容读不成 / 多租户读到信封 = 拒启)`, { danger: SEC }),
|
|
409
|
-
r("SEMA_REGISTRY_URL", "integrations", "url", `sema-registry 地址(设了=remote 配置源开关)。${CENTER_BASE_URL_RULE}`),
|
|
410
|
-
r("SEMA_REGISTRY_WEB_URL", "integrations", "url", `中心 web 面的固定基址:引擎原样转述到 /health 的 center.webBaseUrl,壳按它拼会话深链(路径归壳)。缺席即缺席(引擎不拼不猜);只在接了中心(SEMA_REGISTRY_URL)时挂。${CENTER_BASE_URL_RULE}`),
|
|
411
|
-
r("SEMA_REGISTRY_WORKER", "integrations", "string", "本 worker 在 center 的名字(per-worker effective)"),
|
|
412
404
|
r("SEMA_UV_INDEX", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 uv index 地址"),
|
|
413
405
|
r("SEMA_UV_PY_MIRROR", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 uv python 分发镜像地址"),
|
|
406
|
+
r(WEB_ADMIN_ENV.adminToken, "integrations", "secret", "写 web-admin 的管理凭证(仅 boot 上报执行公钥用;刻意不入 ServiceConfig —— 不进 configHash/日志面)", { danger: SEC, scope: "bootstrap-only" }),
|
|
407
|
+
r(WEB_ADMIN_ENV.dryRun, "integrations", "boolean", "web-admin 腿 dry-run(拉而不应用)", { staticDefault: "false" }),
|
|
408
|
+
r(WEB_ADMIN_ENV.liveUrl, "integrations", "url", `fleet 活性组(心跳 / 注销、服务租约 = settings-schema FLEET_LIVE_PATHS)的 web-admin 基址:给管理面在就绪门后、活性面另走一条不看就绪的入口的部署。缺席 = ${WEB_ADMIN_ENV.url}。传输地址,不参与中心身份互证。${CENTER_BASE_URL_RULE}`),
|
|
409
|
+
r(WEB_ADMIN_ENV.token, "integrations", "secret", `center 拉取凭据(值形;与 ${WEB_ADMIN_ENV.tokenFile} 二选一,都设=拒启)`, { danger: SEC }),
|
|
410
|
+
r(WEB_ADMIN_ENV.tokenFile, "integrations", "path", `center 拉取凭据文件(每条中心请求都重读 ⇒ 轮换 / 换账号不重启;与 ${WEB_ADMIN_ENV.token} 二选一,都设=拒启;内容两种类:裸行 = 静态 bearer / JSON 信封 user-session = 人的登录会话(可续签,S-698);文件不可读 / 为空 = 响亮 + 健康态 unreadable,不当作无凭证;内容读不成 / 多租户读到信封 = 拒启)`, { danger: SEC }),
|
|
411
|
+
r(WEB_ADMIN_ENV.url, "integrations", "url", `web-admin 基址(设了=remote 配置源开关;传输地址 —— 编排器注入的可以是集群内地址;AUTH_BRIDGE_ISSUER 缺席时它也是中心身份)。${CENTER_BASE_URL_RULE}`),
|
|
412
|
+
r(WEB_ADMIN_ENV.webUrl, "integrations", "url", `web-admin 人用界面的固定基址:引擎原样转述到 /health 的 center.webBaseUrl,壳按它拼会话深链(路径归壳)。缺席即缺席(引擎不拼不猜);只在接了中心(${WEB_ADMIN_ENV.url})时挂;必须与中心身份(AUTH_BRIDGE_ISSUER,缺席 = ${WEB_ADMIN_ENV.url})同主机同端口(http ↔ https 同族),否则拒启。${CENTER_BASE_URL_RULE}`),
|
|
413
|
+
r(WEB_ADMIN_ENV.worker, "integrations", "string", "本 worker 在 web-admin 上的名字(per-worker effective)"),
|
|
414
414
|
r("SEND_USER_FILE_SANDBOX_PUT_ENDPOINT", "store", "url", "沙箱侧 presigned PUT Host 绑定 endpoint(in-cluster 可用内网面)", { derivedDefaultNote: "缺席 ⇒ publicEndpoint" }),
|
|
415
415
|
r("SEND_USER_FILE_URL_TTL", "store", "number", "公链 TTL(秒;非法值回 0=永久轨,绝不 NaN 进签名)", { staticDefault: "0" }),
|
|
416
416
|
r("SENSITIVE_WRITE_PATTERNS", "approval", "csv", "敏感路径写 deny 集(unset=core 推荐集;显式值整表替换;off/空=关 —— 真放松杆)", { danger: SEC, derivedDefaultNote: "缺席 ⇒ core RECOMMENDED_SENSITIVE_PATTERNS", configKey: "sensitiveWritePatterns" }),
|
|
@@ -609,7 +609,7 @@ function valueClassOf(type) {
|
|
|
609
609
|
return "plain";
|
|
610
610
|
}
|
|
611
611
|
export function buildConfigCatalog(inputs) {
|
|
612
|
-
const { config, env, boot, knobs, centerDomains, centerManagedKeys
|
|
612
|
+
const { config, env, boot, knobs, centerDomains, centerManagedKeys } = inputs;
|
|
613
613
|
const knobByName = new Map(knobs.map((k) => [k.name, k]));
|
|
614
614
|
const rows = [...CONFIG_CATALOG]
|
|
615
615
|
.sort((a, b) => (a.name < b.name ? -1 : 1))
|
|
@@ -686,7 +686,7 @@ export function buildConfigCatalog(inputs) {
|
|
|
686
686
|
centerManaged: rows.filter((x) => x.center?.managedNow === true).length,
|
|
687
687
|
},
|
|
688
688
|
rows,
|
|
689
|
-
retired,
|
|
689
|
+
retired: buildRetiredEnvWire(),
|
|
690
690
|
};
|
|
691
691
|
}
|
|
692
692
|
//# sourceMappingURL=config-catalog.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ConfigDirectoryMigration } from "@sema-agent/settings-schema/node";
|
|
2
|
+
/** 一次起服迁移的结果:契约的结果闭集,外加本仓入口的两形(目录不在 ⇒ 不调;调用抛 ⇒ 失败)。纯数据。 */
|
|
3
|
+
export type ConfigDirMigration = {
|
|
4
|
+
readonly kind: "no-config-dir";
|
|
5
|
+
readonly dir: string;
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: "threw";
|
|
8
|
+
readonly dir: string;
|
|
9
|
+
readonly reason: string;
|
|
10
|
+
} | (ConfigDirectoryMigration & {
|
|
11
|
+
readonly dir: string;
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* 对 `<root>/config.d` 的读回面跑一次契约的目录迁移(见文件头)。`root` = 本地配置根(`FileConfigStore` 吃的同一个)。
|
|
15
|
+
* 不抛:每一形都是一个结果,调用方 {@link logConfigDirMigration}。
|
|
16
|
+
*/
|
|
17
|
+
export declare function migrateLocalConfigDirectory(root: string): Promise<ConfigDirMigration>;
|
|
18
|
+
/** 迁移结果的日志口(logger 的子集)。 */
|
|
19
|
+
export interface ConfigDirMigrationLog {
|
|
20
|
+
info(msg: string, meta?: Record<string, unknown>): void;
|
|
21
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 一次起服迁移的**唯一**说法(两个入口同调),对契约结果穷举:
|
|
25
|
+
* · `applied` ⇒ info `config_dir_migrated`(收据:迁移 id / 文档 / 坐标 / 处置,不带值;写回 / 删除的文件名);
|
|
26
|
+
* · `noop` / `no-documents` / 目录不在 ⇒ 不出声(没有东西可迁);
|
|
27
|
+
* · `refused` ⇒ warn `config_dir_migration_refused`(一字未写;逐条点名迁移 id、坐标与契约的拒句 —— 读侧随后对这些旧形照旧响亮拒);
|
|
28
|
+
* · `unreadable` / 调用抛 ⇒ warn `config_dir_migration_failed`(一字未写或写到一半;原因 —— 坏文件的句子与读腿同一只判官)。
|
|
29
|
+
* · `pending` 只在契约的 `check: true` 下出现,本仓从不传 ⇒ 撞上即是接线错,响亮抛。
|
|
30
|
+
*/
|
|
31
|
+
export declare function logConfigDirMigration(log: ConfigDirMigrationLog, m: ConfigDirMigration): void;
|
|
32
|
+
//# sourceMappingURL=config-d-migration.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { RETIRED_DOMAINS, formatConfigPath, isRetiredDomainName } from "@sema-agent/settings-schema";
|
|
4
|
+
import { PORTABLE_DOMAINS, migrateConfigDirectory, nodeConfigDirectoryIo } from "@sema-agent/settings-schema/node";
|
|
5
|
+
const READ_BACK_DOCUMENTS = [...PORTABLE_DOMAINS, ...Object.keys(RETIRED_DOMAINS).filter(isRetiredDomainName)];
|
|
6
|
+
const errnoOf = (err) => (err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : undefined);
|
|
7
|
+
export async function migrateLocalConfigDirectory(root) {
|
|
8
|
+
const dir = join(root, "config.d");
|
|
9
|
+
try {
|
|
10
|
+
if (!(await stat(dir)).isDirectory())
|
|
11
|
+
return { kind: "no-config-dir", dir };
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
if (errnoOf(err) === "ENOENT")
|
|
15
|
+
return { kind: "no-config-dir", dir };
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return { ...(await migrateConfigDirectory(dir, { documents: READ_BACK_DOCUMENTS, io: nodeConfigDirectoryIo })), dir };
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
return { kind: "threw", dir, reason: `${err instanceof Error ? err.message : String(err)}${errnoOf(err) !== undefined ? ` (${errnoOf(err)})` : ""}` };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function logConfigDirMigration(log, m) {
|
|
25
|
+
switch (m.kind) {
|
|
26
|
+
case "no-config-dir":
|
|
27
|
+
case "no-documents":
|
|
28
|
+
case "noop":
|
|
29
|
+
return;
|
|
30
|
+
case "applied":
|
|
31
|
+
log.info("config_dir_migrated", {
|
|
32
|
+
dir: m.dir,
|
|
33
|
+
receipts: m.applied.map((r) => ({ migration: r.migration, document: r.document, path: formatConfigPath(r.path), appliedAs: r.appliedAs })),
|
|
34
|
+
written: m.written,
|
|
35
|
+
removed: m.removed,
|
|
36
|
+
note: "settings-schema one-time migrations rewrote the documents the local reader reads, once (idempotent: the next run changes nothing); files outside the reader's coverage were not touched",
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
case "refused":
|
|
40
|
+
log.warn("config_dir_migration_refused", {
|
|
41
|
+
dir: m.dir,
|
|
42
|
+
refusals: m.refused,
|
|
43
|
+
why: m.why,
|
|
44
|
+
note: "nothing was written — the named coordinates cannot be migrated automatically; the reader refuses those old forms (each refusal names its migration id) until they are fixed by hand",
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
case "unreadable":
|
|
48
|
+
case "threw":
|
|
49
|
+
log.warn("config_dir_migration_failed", { dir: m.dir, reason: m.reason, note: "the one-time migration did not complete — the reader judges the files as they are (old forms are refused loudly)" });
|
|
50
|
+
return;
|
|
51
|
+
case "pending":
|
|
52
|
+
throw new Error(`config.d migration: the contract answered "pending" (check-only) but this engine never asks for a check-only run — wiring error at ${m.dir}`);
|
|
53
|
+
default: {
|
|
54
|
+
const unhandled = m;
|
|
55
|
+
throw new Error(`config.d migration: unknown result ${JSON.stringify(unhandled)}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=config-d-migration.js.map
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
* 打开、bake runner 凭据被删、直连门半开、reaper 追杀活 run……),而进程照跑。逐条在 stage 里手抄一遍
|
|
7
7
|
* 是必错的形(抄漏一条没人会发现),所以本文件是**唯一**的表:
|
|
8
8
|
*
|
|
9
|
-
* · stage 面(
|
|
9
|
+
* · stage 面(web-admin):批1 的任一键触发**全表**校验,过不了 ⇒ 整批拒 + 旧值继续服务 + 铸
|
|
10
10
|
* `restartReason`(运维据此知道「这个变更只能重启生效」,而不是被静默吞掉)。
|
|
11
11
|
* · boot 面:每条 throw 点旁挂 `CROSS-INVARIANT:<id>` 锚,由 test/config-hot/batch1-cross-invariants.test.ts
|
|
12
12
|
* 双向对表 —— 新增一条拒启却不登记 = 门红。boot 的判据文本与拒启行为**逐字不变**(本批不改拒启),
|
|
13
13
|
* 表在这一侧承担的是「不许有表外不变量」的登记职责。
|
|
14
14
|
*
|
|
15
15
|
* 🔴 **报文次序**(车PI 验修轮 F7,如实登记):boot 期一次只报一条。条目按 `bootAnchor` 分住几个区 —— `config.ts` 的 loadConfig 区与
|
|
16
|
-
* `boot/stage-01-config.ts`(第 1 段)、`boot/execution-lane.ts`(第 2 段开头的车道合成步)、`boot/stage-02-
|
|
16
|
+
* `boot/stage-01-config.ts`(第 1 段)、`boot/execution-lane.ts`(第 2 段开头的车道合成步)、`boot/stage-02-web-admin-pull.ts`(第 2 段
|
|
17
17
|
* 目录应用之后)。**跨区次序 = 段序,区内次序 = 表序**:同时撞上两个区的两条,先到的区先报,与表序无关(例:`LEADER_ENABLED` + host
|
|
18
18
|
* + 无 SQL 先报 loadConfig 区的 `leader-durable-store`,改完再在合成步撞 `leader-host-lane`)。锚点按「读什么键、那个键在哪一段定形」定,
|
|
19
19
|
* 不为报文次序挪区 —— 多一次往返,没有安全后果。
|
|
@@ -34,9 +34,9 @@ import type { ServiceConfigFlat } from "./config-types.js";
|
|
|
34
34
|
*/
|
|
35
35
|
export declare const LIVENESS_HEARTBEAT_MS = 30000;
|
|
36
36
|
/** 闭集:新增一条不变量必须在这里加词(拼错 = 编译红,而不是运行期悄悄不判)。 */
|
|
37
|
-
export type CrossInvariantId = "run-stale-heartbeat" | "bind-posture" | "bake-runner-credential" | "direct-door-anchors" | "operator-principals-required" | "bake-operator-principals" | "sensitive-write-patterns" | "leader-host-lane" | "multi-tenant-shared-lane" | "leader-durable-store" | "device-durable-store" | "device-durable-approval" | "model-provider-word" | "gateway-url-userinfo" | "model-route-endpoint" | "default-model-resolvable" | "refused-names-not-minted" | "require-execution-lane";
|
|
37
|
+
export type CrossInvariantId = "run-stale-heartbeat" | "bind-posture" | "bake-runner-credential" | "direct-door-anchors" | "operator-principals-required" | "bake-operator-principals" | "sensitive-write-patterns" | "leader-host-lane" | "multi-tenant-shared-lane" | "leader-durable-store" | "device-durable-store" | "device-durable-approval" | "model-provider-word" | "gateway-url-userinfo" | "model-route-endpoint" | "default-model-resolvable" | "refused-names-not-minted" | "require-execution-lane" | "center-one-identity";
|
|
38
38
|
/** 表读的键集(取自平铺配置型 ⇒ 键名/类型永不与真配置漂)。 */
|
|
39
|
-
export type CrossInvariantView = Readonly<Pick<ServiceConfigFlat, "runStaleSec" | "authToken" | "authTokens" | "allowUnauthedWrites" | "imageBakes" | "directApprovalDoor" | "durableApproval" | "approvalHmacKeys" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "requirePrincipal" | "operatorPrincipals" | "sensitiveWritePatterns" | "leaderEnabled" | "remoteExec" | "requireExecutionLane" | "dbBackend" | "tidb" | "pg" | "envModel" | "gatewayBaseUrl" | "gatewayFallbackUrls" | "anthropic" | "models" | "modelRefusals" | "tiers">>;
|
|
39
|
+
export type CrossInvariantView = Readonly<Pick<ServiceConfigFlat, "runStaleSec" | "authToken" | "authTokens" | "allowUnauthedWrites" | "imageBakes" | "directApprovalDoor" | "durableApproval" | "approvalHmacKeys" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "requirePrincipal" | "operatorPrincipals" | "sensitiveWritePatterns" | "leaderEnabled" | "remoteExec" | "requireExecutionLane" | "dbBackend" | "tidb" | "pg" | "envModel" | "gatewayBaseUrl" | "gatewayFallbackUrls" | "anthropic" | "models" | "modelRefusals" | "tiers" | "authBridgeIssuer" | "webAdmin">>;
|
|
40
40
|
export interface CrossInvariantInput {
|
|
41
41
|
/** 现役配置(boot 期 = 候选自身)。 */
|
|
42
42
|
readonly live: CrossInvariantView;
|
|
@@ -84,6 +84,16 @@ export declare function bakeRunnerCredentialRefusal(v: Pick<CrossInvariantView,
|
|
|
84
84
|
* 「多副本」在 config 层判不了(副本数不是本进程的知识),可判的**代理**就是 durable 前提本身。
|
|
85
85
|
*/
|
|
86
86
|
export declare function leaderDurableStoreRefusal(v: Pick<CrossInvariantView, "leaderEnabled" | "dbBackend">): string | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* `center-one-identity`(7.124.0)—— **中心只有一个身份**。引擎接的 web-admin 由三只旋钮描述:基址(`WEB_ADMIN_ENV.url`,传输地址)、
|
|
89
|
+
* 鉴权桥 issuer(`AUTH_BRIDGE_ISSUER`,缺席 = 基址 —— {@link authBridgeIssuerOf} 一只取值口)、web 基址(`WEB_ADMIN_ENV.webUrl`,上
|
|
90
|
+
* `/health.center.webBaseUrl`)。壳先判 `center.issuer` 是不是自己登录的那个中心,再按 `center.webBaseUrl` 拼会话深链 ⇒ 两者不是同一个中心
|
|
91
|
+
* = 把人带到另一个中心。规则:**web 基址必须落在身份(issuer)的 origin 族**。**origin 族 = `URL.host` 相同**(主机 + 非缺省端口;scheme 不比 ⇒ http ↔ https 同族:TLS 终结在
|
|
92
|
+
* 前面的部署两种写法都见)。判不出主机(issuer 不是 http(s) 的层级式 URL)= 证不了同一个中心 ⇒ 同样拒(fail-closed)。
|
|
93
|
+
* 基址 / 活性基址是**传输地址**,不参与:编排器给 worker 注入的基址是集群内地址,issuer 是对外域名 —— 两者天然不同主机,拿基址互证会拒掉
|
|
94
|
+
* 标准部署形。没有 web 基址 ⇒ 无可互证,过。
|
|
95
|
+
*/
|
|
96
|
+
export declare function centerIdentityRefusal(v: Pick<CrossInvariantView, "authBridgeIssuer" | "webAdmin">): string | undefined;
|
|
87
97
|
export declare const CONFIG_CROSS_INVARIANTS: readonly CrossInvariant[];
|
|
88
98
|
/**
|
|
89
99
|
* 按 id 取**一条**(S-157②:boot 面的单源化出口)。
|
|
@@ -3,6 +3,9 @@ import { modelProviderRefusal } from "./model-provider.js";
|
|
|
3
3
|
import { modelRouteUrlRefusal } from "./model-route-url.js";
|
|
4
4
|
import { modelRouteEndpointRefusal } from "./model-route-endpoint.js";
|
|
5
5
|
import { defaultModelRefusal, refusedNameMintedRefusal } from "./model-entry-refusal.js";
|
|
6
|
+
import { WEB_ADMIN_ENV, httpUrlFlaws } from "@sema-agent/settings-schema";
|
|
7
|
+
import { authBridgeIssuerOf } from "./auth-bridge.js";
|
|
8
|
+
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
6
9
|
import { executionLaneCaps, executionLaneOf, executionLaneRequiredRefusal, laneCapabilityHolds, REMOTE_EXEC_PROVIDER_WORDS } from "./execution-lane-caps.js";
|
|
7
10
|
export const LIVENESS_HEARTBEAT_MS = 30_000;
|
|
8
11
|
const anyServiceAuth = (v) => Boolean(v.authToken) || Object.keys(v.authTokens ?? {}).length > 0;
|
|
@@ -61,6 +64,25 @@ export function leaderDurableStoreRefusal(v) {
|
|
|
61
64
|
"(#193 车4 件2 / staleness ledger P1-9+P1-10 boot invariant)"
|
|
62
65
|
: undefined;
|
|
63
66
|
}
|
|
67
|
+
export function centerIdentityRefusal(v) {
|
|
68
|
+
const webBaseUrl = v.webAdmin?.webBaseUrl;
|
|
69
|
+
if (webBaseUrl === undefined)
|
|
70
|
+
return undefined;
|
|
71
|
+
const identity = authBridgeIssuerOf(v);
|
|
72
|
+
if (identity === undefined)
|
|
73
|
+
return undefined;
|
|
74
|
+
const hostOf = (raw) => (httpUrlFlaws(raw, []).length === 0 ? new URL(raw).host : undefined);
|
|
75
|
+
const issuerHost = hostOf(identity.issuer);
|
|
76
|
+
const webHost = hostOf(webBaseUrl);
|
|
77
|
+
if (issuerHost !== undefined && issuerHost === webHost)
|
|
78
|
+
return undefined;
|
|
79
|
+
const issuerSeat = identity.source === "explicit" ? "AUTH_BRIDGE_ISSUER" : `AUTH_BRIDGE_ISSUER (unset, so ${WEB_ADMIN_ENV.url})`;
|
|
80
|
+
const issuerShown = issuerHost === undefined ? `${buildDisplaySafeUrl(identity.issuer)}, which is not an http(s) URL with a host` : `${buildDisplaySafeUrl(identity.issuer)} (host ${issuerHost})`;
|
|
81
|
+
return (`${WEB_ADMIN_ENV.webUrl}=${buildDisplaySafeUrl(webBaseUrl)} (host ${webHost ?? "?"}) is not on this engine's web-admin identity — ${issuerSeat} = ${issuerShown}. ` +
|
|
82
|
+
`A client checks the reported issuer against the web-admin it signed in to and then links sessions under the reported web console base, so both must name ` +
|
|
83
|
+
`the same web-admin: same host and port (http and https on one host count as one). Set ${WEB_ADMIN_ENV.webUrl} to that web-admin's console, set ` +
|
|
84
|
+
`AUTH_BRIDGE_ISSUER to the web-admin's public origin, or unset ${WEB_ADMIN_ENV.webUrl}. Refusing to boot.`);
|
|
85
|
+
}
|
|
64
86
|
export const CONFIG_CROSS_INVARIANTS = [
|
|
65
87
|
{
|
|
66
88
|
id: "run-stale-heartbeat",
|
|
@@ -217,16 +239,23 @@ export const CONFIG_CROSS_INVARIANTS = [
|
|
|
217
239
|
id: "default-model-resolvable",
|
|
218
240
|
keys: ["models", "modelRefusals"],
|
|
219
241
|
restartReason: "default-model",
|
|
220
|
-
bootAnchor: { file: "src/boot/stage-02-
|
|
242
|
+
bootAnchor: { file: "src/boot/stage-02-web-admin-pull.ts", marker: "CROSS-INVARIANT:default-model-resolvable" },
|
|
221
243
|
check: ({ live, next }) => (next.models === live.models ? undefined : defaultModelRefusal(next, "catalog")),
|
|
222
244
|
},
|
|
223
245
|
{
|
|
224
246
|
id: "refused-names-not-minted",
|
|
225
247
|
keys: ["models", "tiers", "modelRefusals"],
|
|
226
248
|
restartReason: "refused-model-name",
|
|
227
|
-
bootAnchor: { file: "src/boot/stage-02-
|
|
249
|
+
bootAnchor: { file: "src/boot/stage-02-web-admin-pull.ts", marker: "CROSS-INVARIANT:refused-names-not-minted" },
|
|
228
250
|
check: ({ live, next }) => (next.models === live.models ? undefined : refusedNameMintedRefusal(next)),
|
|
229
251
|
},
|
|
252
|
+
{
|
|
253
|
+
id: "center-one-identity",
|
|
254
|
+
keys: ["authBridgeIssuer", "webAdmin"],
|
|
255
|
+
restartReason: "center-identity",
|
|
256
|
+
bootAnchor: { file: "src/config.ts", marker: "CROSS-INVARIANT:center-one-identity" },
|
|
257
|
+
check: ({ next }) => centerIdentityRefusal(next),
|
|
258
|
+
},
|
|
230
259
|
];
|
|
231
260
|
export function crossInvariant(id) {
|
|
232
261
|
const inv = CONFIG_CROSS_INVARIANTS.find((i) => i.id === id);
|