@sema-agent/server 7.119.0 → 7.121.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/MIGRATION.md +129 -0
- package/USAGE.md +72 -23
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -2
- package/deploy/sema-up/chart/templates/server.yaml +2 -2
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +1 -1
- package/dist/boot/config-center.d.ts +5 -7
- package/dist/boot/config-center.js +37 -13
- package/dist/boot/engine-lease.d.ts +1 -1
- package/dist/boot/execution-env.js +3 -2
- package/dist/boot/fatal-exit.d.ts +7 -1
- package/dist/boot/fatal-exit.js +2 -1
- package/dist/boot/resolve-spec.js +1 -0
- package/dist/boot/runner-deps.d.ts +4 -3
- package/dist/boot/runner-deps.js +7 -3
- package/dist/boot/shutdown.d.ts +9 -10
- package/dist/boot/shutdown.js +54 -16
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-09-leader.d.ts +2 -9
- package/dist/boot/stage-10-http-server.d.ts +1 -9
- package/dist/boot/stage-10-http-server.js +31 -8
- package/dist/capabilities/builtin-tools.d.ts +31 -8
- package/dist/capabilities/builtin-tools.js +22 -1
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/scenarios.d.ts +8 -6
- package/dist/capabilities/scenarios.js +3 -3
- package/dist/capabilities/send-user-file-tool.d.ts +6 -3
- package/dist/capabilities/send-user-file-tool.js +1 -1
- package/dist/config-catalog.js +13 -7
- package/dist/config-center/center-request.d.ts +16 -1
- package/dist/config-center/center-request.js +36 -5
- package/dist/config-lkg.d.ts +33 -5
- package/dist/config-lkg.js +55 -11
- package/dist/config-types.d.ts +46 -3
- package/dist/config-types.js +6 -0
- package/dist/config.d.ts +33 -4
- package/dist/config.js +107 -15
- package/dist/deployment-governance.d.ts +3 -1
- package/dist/deployment-governance.js +3 -1
- package/dist/device-store.d.ts +7 -0
- package/dist/device-store.js +4 -0
- package/dist/device-ws-hub.js +17 -9
- package/dist/device-ws-protocol.d.ts +15 -0
- package/dist/fleet/drain-entry.d.ts +87 -0
- package/dist/fleet/drain-entry.js +35 -0
- package/dist/fleet/engine-signal.d.ts +144 -0
- package/dist/fleet/engine-signal.js +60 -0
- package/dist/fleet/serving-lease.d.ts +47 -0
- package/dist/fleet/serving-lease.js +235 -0
- package/dist/fleet-client.d.ts +35 -19
- package/dist/fleet-client.js +49 -25
- package/dist/http/approval-sweeps.js +3 -5
- package/dist/http/dispatch.js +18 -13
- package/dist/http/health-self-report.d.ts +60 -0
- package/dist/http/health-self-report.js +53 -0
- package/dist/http/principal-gate.d.ts +8 -1
- package/dist/http/principal-gate.js +1 -0
- package/dist/http/readiness.d.ts +25 -5
- package/dist/http/readiness.js +31 -6
- package/dist/http/resume-legs.js +5 -1
- package/dist/http/route-ctx.d.ts +6 -4
- package/dist/http/route-table.d.ts +3 -0
- package/dist/http/route-table.js +4 -1
- package/dist/http/routes/a2a-serve.d.ts +1 -1
- package/dist/http/routes/a2a-serve.js +5 -5
- package/dist/http/routes/admin-drain.d.ts +12 -31
- package/dist/http/routes/admin-drain.js +15 -9
- package/dist/http/routes/approvals-assistant.d.ts +3 -3
- package/dist/http/routes/approvals-assistant.js +3 -3
- package/dist/http/routes/capabilities.js +3 -1
- package/dist/http/routes/devices.js +6 -5
- package/dist/http/routes/leader.d.ts +1 -1
- package/dist/http/routes/leader.js +1 -1
- package/dist/http/routes/memory-consolidation.d.ts +1 -1
- package/dist/http/routes/memory-consolidation.js +1 -1
- package/dist/http/routes/notify-wake.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +1 -1
- package/dist/http/routes/rules.js +14 -6
- package/dist/http/routes/runs.d.ts +2 -2
- package/dist/http/routes/runs.js +2 -2
- package/dist/http/routes/side-query.d.ts +1 -1
- package/dist/http/routes/side-query.js +1 -1
- package/dist/http/routes/tasks.d.ts +2 -2
- package/dist/http/routes/tasks.js +3 -3
- package/dist/http/server.d.ts +7 -21
- package/dist/http/server.js +20 -1
- package/dist/http/wire-types.d.ts +7 -1
- package/dist/list-entry-display.d.ts +14 -0
- package/dist/list-entry-display.js +14 -0
- package/dist/model-reach-scope.d.ts +15 -3
- package/dist/model-reach-scope.js +3 -2
- package/dist/model-unreachable-notice.d.ts +2 -1
- package/dist/model-unreachable-notice.js +1 -0
- package/dist/namespaced-rule-name.js +2 -6
- package/dist/observability/fail-open.d.ts +20 -0
- package/dist/observability/fail-open.js +20 -0
- package/dist/operator-ask.d.ts +1 -1
- package/dist/plugins/one-time-migrations.d.ts +8 -0
- package/dist/plugins/one-time-migrations.js +72 -3
- package/dist/plugins/permission-rule-store-file.js +2 -6
- package/dist/plugins/permission-rule-store-sql.d.ts +65 -6
- package/dist/plugins/permission-rule-store-sql.js +166 -59
- package/dist/plugins/remote-env-device.d.ts +12 -1
- package/dist/plugins/remote-env-device.js +13 -6
- package/dist/plugins/scheduler-support.d.ts +8 -4
- package/dist/plugins/scheduler-support.js +5 -5
- package/dist/request-key-closure.js +1 -1
- package/dist/rule-entry-list.d.ts +0 -1
- package/dist/rule-entry-list.js +4 -9
- package/dist/rules-consent.d.ts +16 -3
- package/dist/rules-consent.js +8 -4
- package/dist/run-local.js +2 -0
- package/dist/runs.js +1 -1
- package/dist/sema-config-dir.d.ts +19 -0
- package/dist/sema-config-dir.js +33 -0
- package/dist/session-policy-wire.js +5 -4
- package/dist/spec-fields.d.ts +5 -4
- package/dist/task-settings.d.ts +3 -3
- package/dist/tool-approval.d.ts +7 -0
- package/dist/tool-approval.js +4 -1
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/engine-notice-wire.d.ts +6 -4
- package/dist/trace/engine-notice-wire.js +16 -9
- package/dist/trace/self-minted-wire.d.ts +78 -0
- package/dist/trace/self-minted-wire.js +32 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +56 -14
- package/package.json +3 -3
|
@@ -94,6 +94,6 @@ export function sendUserFileTool(deps) {
|
|
|
94
94
|
export const SEND_USER_FILE_TOOL_NAME = "SendUserFile";
|
|
95
95
|
const READONLY_BUILTIN_AGENT_TYPES = new Set(builtinAgentDefinitions().map((d) => d.name));
|
|
96
96
|
export function subagentSendUserFileExtraTools(deps) {
|
|
97
|
-
return (ctx) => (ctx.agentType !== undefined && READONLY_BUILTIN_AGENT_TYPES.has(ctx.agentType)
|
|
97
|
+
return { tools: [deps.spec], forSpawn: (ctx) => !(ctx.agentType !== undefined && READONLY_BUILTIN_AGENT_TYPES.has(ctx.agentType)) };
|
|
98
98
|
}
|
|
99
99
|
//# sourceMappingURL=send-user-file-tool.js.map
|
package/dist/config-catalog.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
|
|
2
|
+
import { GLOBAL_SCOPE } from "@sema-agent/settings-schema/api/scopes";
|
|
2
3
|
import { CENTER_CREDENTIAL_ENV, DOMAIN_SCHEMAS } from "@sema-agent/settings-schema";
|
|
3
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";
|
|
4
5
|
import { THINKING_FORMATS } from "./model-compat.js";
|
|
5
|
-
import { AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, CONFIG_PROVIDER_WORDS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
|
|
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";
|
|
6
7
|
import { webSearchProviderFromEnv, WEB_SEARCH_PROVIDERS } from "./plugins/web-search.js";
|
|
7
8
|
import { AUTONOMY_MODES } from "./runtime-governance.js";
|
|
8
9
|
import { REMOTE_EXEC_PROVIDER_WORDS } from "./execution-lane-caps.js";
|
|
@@ -93,8 +94,8 @@ export const CONFIG_CATALOG = [
|
|
|
93
94
|
r("CONFIG_BOOT_FETCH_BUDGET_MS", "integrations", "number", "boot 首拉配置的抢答窗(ms;过窗以 env/兜底起服,拉取转后台热应用)", { staticDefault: "1500", configKey: "configBootFetchBudgetMs" }),
|
|
94
95
|
r("CONFIG_CATALOG_ENABLED", "auth", "boolean", "本目录端点 GET /v1/config/catalog 的旋钮(operator-only 面;合规部署可关 ⇒ 路径 404 + 能力位翻假)", { staticDefault: "true", danger: SEC, configKey: "configCatalogEnabled" }),
|
|
95
96
|
r("CONFIG_LKG_DURABLE", "integrations", "boolean", "LKG 跨重启 handoff(严格 'true' 才开:重启先用 LKG 起服,tier 候选借此落地)", { staticDefault: "false" }),
|
|
96
|
-
r("CONFIG_LKG_ENABLED", "integrations", "boolean", "last-known-good 配置盘缓存读写开关(仅远程 provider 有意义)", { staticDefault: "true" }),
|
|
97
|
-
r("CONFIG_LKG_PATH", "integrations", "path", "LKG 落盘路径", { derivedDefaultNote: "缺席 ⇒ SEMA_CONFIG_DIR
|
|
97
|
+
r("CONFIG_LKG_ENABLED", "integrations", "boolean", "last-known-good 配置盘缓存读写开关(仅远程 provider 有意义)", { staticDefault: "true", configKey: "configLkgEnabled" }),
|
|
98
|
+
r("CONFIG_LKG_PATH", "integrations", "path", "LKG 落盘路径(设了 ⇒ 派生读拒行:这条路径与它的真实落点各一行,按路径段串匹配 —— 任意深度出现即拒、大小写不敏感,用户仓里同段串的路径一并读不到;空串拒启)", { derivedDefaultNote: "缺席 ⇒ 配置根(SEMA_CONFIG_DIR,缺省 ~/.sema)下按 worker + 凭证身份分箱派生", configKey: "configLkgPath" }),
|
|
98
99
|
r("CONFIG_LOCAL_DIR", "integrations", "path", "本地配置源根(config.d/<domain>.json);兼作数据根回落链一环", { configKey: "configLocalDir", danger: DATA }),
|
|
99
100
|
r("CONFIG_PROVIDER", "integrations", "enum", "配置源双模选择(闭集,逐字比较;认不出的词 ⇒ 拒启,S-841)", { enumValues: CONFIG_PROVIDER_WORDS, derivedDefaultNote: "缺席:SEMA_REGISTRY_URL 在场 ⇒ remote,否则纯 env(无管道)", configKey: "configProvider" }),
|
|
100
101
|
r("CONFIG_REQUIRE_ROSTER", "integrations", "boolean", "远程 registry 非 dryRun 时布防 ready 门:roster 未落地 ⇒ 计费提交 503 + /health ready:false(严格 'true')", { staticDefault: "false" }),
|
|
@@ -127,7 +128,7 @@ export const CONFIG_CATALOG = [
|
|
|
127
128
|
r("DOCKER_PIDS_LIMIT", "orchestration", "number", "local-docker fork-bomb 背栓(--pids-limit;非法值回 adapter 默认 512+warn)", { danger: SEC, derivedDefaultNote: "缺席 ⇒ adapter 默认 512" }),
|
|
128
129
|
r("DOCKER_SANDBOX_ENV", "orchestration", "secret", "local-docker 车道每条 docker exec 以 -e 注入的本进程 env 点名表(NAME,NAME;点到的键从 worker env 原样取,值不进 spec/center/日志;与 LSP_ENV_ALLOW / E2B_SANDBOX_ENV 同一只解析器与「点名放行」谓词:形错 / 点名名单型旋钮拒启且不回显值;本进程环境里没有的名按序号 warn、不转发;只在本车道被选中时求值;原文按凭据类对待:只回在场位、不进任何子进程)", { danger: SEC }),
|
|
129
130
|
r("DRAIN_GRACE_MS", "limitsHttp", "number", "SIGTERM 优雅排空窗(ms;期间新计费提交 503+Retry-After)。出厂默认是**服务器部署**的取值;壳自 spawn 的引擎会被壳注入更短的窗 ⇒ 排壳形关机预算时以本行的 effectiveValue(= 本进程实际生效值)为准,不照抄 staticDefault", { staticDefault: String(10 * 60 * 1000), configKey: "drainGraceMs", center: cLimits("drainGraceMs") }),
|
|
130
|
-
r("DRAIN_SECOND_SIGNAL_MIN_GAP_MS", "limitsHttp", "number", `排空期「第二发信号 = 硬停」的最小间隔(ms;锚 =
|
|
131
|
+
r("DRAIN_SECOND_SIGNAL_MIN_GAP_MS", "limitsHttp", "number", `排空期「第二发信号 = 硬停」的最小间隔(ms;锚 = 本次排空的第一发停止信号 —— SIGTERM / SIGHUP 升级进的排空,入口那一发就是它;排空口 / 租约被拒 / 附着租约自退进的排空,到达的第一发被吸收、不缩短期限。SIGTERM / SIGINT 与锚相隔不足 ⇒ 同一拍的重复投递,一行 drain_duplicate_signal_ignored 后忽略、排空继续;0 = 关,恒按第二发硬停;须为 [${DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS.minMs}, ${DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS.maxMs}] 内十进制整数(上界 = DRAIN_GRACE_MS 地板),小数 / 负数 / 越界拒启)`, { staticDefault: String(DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS), configKey: "drainSecondSignalMinGapMs" }),
|
|
131
132
|
r("DURABLE_APPROVAL", "approval", "boolean", "durable suspend/resume HITL(需 SQL 店;决定 checkpointStore 与 /v1/approvals 面的存在性)", { danger: SEC, derivedDefaultNote: "posture 三态(S-178):显式词恒赢,缺席 = 单用户(REQUIRE_PRINCIPAL≠true)且有耐久店 ⇒ on / 多租户或无耐久店 ⇒ off;生效来源见 GET /v1/diagnostics/wiring 的 serverGates.durableApproval.source", configKey: "durableApproval" }),
|
|
132
133
|
r("E2B_ALLOW_NET", "orchestration", "boolean", "e2b 沙箱出网许可", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 不铸键(adapter 默认允许)" }),
|
|
133
134
|
r("E2B_API_KEY", "orchestration", "secret", "E2B 车道 API 凭据(REMOTE_EXEC=e2b 必需件)", { danger: [...SEC, ...BILL] }),
|
|
@@ -147,8 +148,11 @@ export const CONFIG_CATALOG = [
|
|
|
147
148
|
r("EXPERIMENTAL_OBSERVER_AGENTS", "orchestration", "boolean", "observer 开闸线(实验面 ship-dark;多租户只认 center caps,env 被忽略)", { staticDefault: "false", configKey: "experimentalObserverAgents" }),
|
|
148
149
|
r("FILE_HISTORY_CAPTURE", "store", "boolean", "文件历史**捕获**总开关(三条车道同义)。关 = 不再捕获任何改动文件的首次快照(落 core 的部署捕获位 `RunnerDeps.fileHistoryCapture: \"off\"`,能力词 `fileHistoryCapture` 翻 off);S-724(7.104.0)起**只关捕获**:历史店照接,已存历史照常 restoreFiles / rewindFilesTo / 2c session-sync。关掉 ≠ 删掉:已捕获的历史不被批量删除,仍按会话删除与留存规则清理 —— 要清存量走 DELETE 会话或留存旋钮;没有「整库历史不可用」的旋钮", { staticDefault: "true", danger: DATA, configKey: "fileHistoryCapture" }),
|
|
149
150
|
r("FILE_HISTORY_RETENTION_KEEP", "store", "number", "per-file rewind 历史的保留上限(与 core FileStorageBackend 同域键形,三条车道一套语义)", { danger: DATA, derivedDefaultNote: "缺席 ⇒ core 缺省 100" }),
|
|
150
|
-
r("FLEET_ADVERTISE_ADDRESS", "integrations", "url", "fleet 面向 center 公告的本 worker 可达地址(中心车道配齐 + 本键 ⇒
|
|
151
|
+
r("FLEET_ADVERTISE_ADDRESS", "integrations", "url", "fleet 面向 center 公告的本 worker 可达地址(中心车道配齐 + 本键 ⇒ 公告环;无条件解析,非 http(s) 拒启,空串 = 未设。用量环不看本键,S-866 ④)", { configKey: "fleetAdvertiseAddress" }),
|
|
152
|
+
r("FLEET_GENERATION", "integrations", "string", "fleet 成员的代际(配置渲染器写;引擎回声到心跳与 /health 的 generation)。缺席 ⇔ 非 fleet 成员(不续服务租约、不受围栏);在场必须是规范十进制串(\"0\" / \"7\";空串、前导零、符号、空白拒启)", { danger: SEC, configKey: "fleetGeneration" }),
|
|
151
153
|
r("FLEET_LEASE_FAIL_CLOSED", "integrations", "boolean", "租约连接级失败(中心鉴权拒 / 凭证不可读 / 不可达 / 5xx / 超时 / 坏体)时计费入口拒 503 fleet.lease_unavailable(true)还是放行(false);409 not-lease 与耗尽 429 不受它管;词表外拒启", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 按部署姿态派生:REQUIRE_PRINCIPAL=true ⇒ true(多租:额度是安全轴);单用户 ⇒ false(AP 姿态)。显式值压过派生。生效值见 caps centerWiring.failMode", configKey: "fleetLeaseFailClosed" }),
|
|
154
|
+
r("FLEET_LEASE_OUTAGE_MAX_MS", "integrations", "number", "服务租约中心不可达的容忍上限 T_max(ms):租约过期满这么久 ⇒ 新计费提交 503 state.lease_expired(取消 / HITL 应答 / 续跑 / 读面照放行,在飞不动,/readyz 不翻);只在 fleet 成员 ∧ 中心宣告 servingLease 能力位时生效;< 60000(租约 TTL 地板)或非整数拒启", { danger: SEC, staticDefault: String(FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS), configKey: "fleetLeaseOutageMaxMs" }),
|
|
155
|
+
r("FLEET_SCOPE", "integrations", "string", "本引擎所在的租户 scope(配置渲染器为多租引擎写;唯一读者 = 排空口 POST /v1/admin/drain 的身份比对,请求不带 scope 也读成缺省 scope)。在场必须是 scope id 形(小写字母 / 数字 / 内部连字符,1–64;空串拒启);与 FLEET_GENERATION 不联动(成员缺席 ⇒ 缺省 scope)。续租体与心跳不带它(scope 由中心凭证绑定)", { danger: SEC, staticDefault: GLOBAL_SCOPE, configKey: "fleetScope" }),
|
|
152
156
|
r("FORK_ENABLED", "orchestration", "boolean", "CC /fork(core Fork 工具)装配开关", { derivedDefaultNote: "posture 三态:缺席 = 单用户 ⇒ on / 多租户 ⇒ off(per-principal entitlement 另判)", configKey: "forkEnabled" }),
|
|
153
157
|
r("GATEWAY_MAX_RETRIES", "modelPlane", "number", "openai 兼容腿每次调用重试上限", { derivedDefaultNote: "缺席 ⇒ 不传引擎,引擎默认当家" }),
|
|
154
158
|
r("GIT_API_BASEURL", "integrations", "url", "repo 工具的 Git host API base", { configKey: "gitApiBaseUrl" }),
|
|
@@ -391,7 +395,7 @@ export const CONFIG_CATALOG = [
|
|
|
391
395
|
r("SELF_ORCHESTRATION_MODELS", "orchestration", "csv", "workflow agent({modelName}) 显式词表(恒赢;缺席单用户=目录键全集/多租户 fail-closed)", { configKey: "selfOrchestrationModels" }),
|
|
392
396
|
r("SELF_ORCHESTRATION_WORKER_ISOLATION", "orchestration", "boolean", "workflow 脚本跑 worker_thread(堆帽+terminate 的 DoS 隔离)", { staticDefault: "false", danger: SEC, configKey: "selfOrchestrationWorkerIsolation" }),
|
|
393
397
|
r("SEMA_APT_MIRROR", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 apt 源地址(注入沙箱 pkg-source hook)"),
|
|
394
|
-
r("SEMA_CONFIG_DIR", "integrations", "path", "~/.sema 配置目录覆写(LKG/skill 缓存/scheduler
|
|
398
|
+
r("SEMA_CONFIG_DIR", "integrations", "path", "~/.sema 配置目录覆写(LKG/skill 缓存/scheduler 店同门;设了 ⇒ 起服时建出目录,交引擎在根下同护配置文件,能不能用由引擎判;空串拒启)", { derivedDefaultNote: "缺席 ⇒ ~/.sema", configKey: "agentConfigDir" }),
|
|
395
399
|
r("SEMA_FAILOPEN_PROBE", "observability", "path", "F 类 fail-open 命中的探针文件路径(在场时逐次追加 fail-open 行;缺席只走计数+一次性 warn)"),
|
|
396
400
|
r("SEMA_NODE_MIRROR", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 node 分发镜像地址"),
|
|
397
401
|
r("SEMA_NPM_REGISTRY", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 npm registry 地址"),
|
|
@@ -399,9 +403,11 @@ export const CONFIG_CATALOG = [
|
|
|
399
403
|
r("SEMA_PIP_INDEX", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 pip index 地址"),
|
|
400
404
|
r("SEMA_REGISTRY_ADMIN_TOKEN", "integrations", "secret", "写 center 的管理凭证(仅 boot 上报执行公钥用;刻意不入 ServiceConfig —— 不进 configHash/日志面)", { danger: SEC, scope: "bootstrap-only" }),
|
|
401
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}`),
|
|
402
407
|
r(CENTER_CREDENTIAL_ENV.token, "integrations", "secret", `center 拉取凭据(值形;与 ${CENTER_CREDENTIAL_ENV.tokenFile} 二选一,都设=拒启)`, { danger: SEC }),
|
|
403
408
|
r(CENTER_CREDENTIAL_ENV.tokenFile, "integrations", "path", `center 拉取凭据文件(每条中心请求都重读 ⇒ 轮换 / 换账号不重启;与 ${CENTER_CREDENTIAL_ENV.token} 二选一,都设=拒启;内容两种类:裸行 = 静态 bearer / JSON 信封 user-session = 人的登录会话(可续签,S-698);文件不可读 / 为空 = 响亮 + 健康态 unreadable,不当作无凭证;内容读不成 / 多租户读到信封 = 拒启)`, { danger: SEC }),
|
|
404
|
-
r("SEMA_REGISTRY_URL", "integrations", "url",
|
|
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}`),
|
|
405
411
|
r("SEMA_REGISTRY_WORKER", "integrations", "string", "本 worker 在 center 的名字(per-worker effective)"),
|
|
406
412
|
r("SEMA_UV_INDEX", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 uv index 地址"),
|
|
407
413
|
r("SEMA_UV_PY_MIRROR", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 uv python 分发镜像地址"),
|
|
@@ -8,13 +8,15 @@ export declare const SETTINGS_SCHEMA_MAJOR: string;
|
|
|
8
8
|
/** 一个中心端点 = 基址 + 凭证来源(`ServiceConfig.configCenter` 结构上满足)。 */
|
|
9
9
|
export interface CenterEndpoint {
|
|
10
10
|
readonly baseUrl: string;
|
|
11
|
+
/** 活性组的基址(env `SEMA_REGISTRY_LIVE_URL` 的回声;缺席 ⇒ 活性组也打 {@link baseUrl})。「缺席 = 主基址」这条规则只在 {@link centerFetch} 读。 */
|
|
12
|
+
readonly liveBaseUrl?: string | undefined;
|
|
11
13
|
readonly credential: CenterCredentialSource;
|
|
12
14
|
}
|
|
13
15
|
/**
|
|
14
16
|
* S-866 ④ —— 「中心车道配齐」的**唯一**谓词:基址 ∧ **声明了**凭证(S-651:按声明,不按此刻读不读得出)∧ worker。
|
|
15
17
|
*
|
|
16
18
|
* 三处同调、不许各抄一份门:租约管理器(`fleet-lease.ts` `createFleetLeaseFromEnv`)、fleet 客户端的用量环
|
|
17
|
-
* (`fleet-client.ts` `
|
|
19
|
+
* (`fleet-client.ts` `startFleetClientFromConfig`)、用量累加器(`boot/budget-tracing.ts`)。公告环另要一件可达性声明
|
|
18
20
|
* `FLEET_ADVERTISE_ADDRESS` —— 那是公告环自己的第四件,不在本谓词里:用量是出站上报,与「中心能不能打到我」无关
|
|
19
21
|
* (7.105.0 及以前累加器与用量环都挂在公告地址上,不公告的引擎于是从不报用量,租约的已耗量只剩中心自己的估计)。
|
|
20
22
|
*/
|
|
@@ -47,6 +49,8 @@ export type CenterRoute = {
|
|
|
47
49
|
readonly kind: "fleet-instances";
|
|
48
50
|
} | {
|
|
49
51
|
readonly kind: "fleet-usage";
|
|
52
|
+
} | {
|
|
53
|
+
readonly kind: "fleet-serving-lease";
|
|
50
54
|
} | {
|
|
51
55
|
readonly kind: "execution-keys";
|
|
52
56
|
};
|
|
@@ -68,6 +72,17 @@ export interface CenterRequestInit {
|
|
|
68
72
|
readonly timeoutMs: number;
|
|
69
73
|
readonly fetchImpl?: typeof fetch;
|
|
70
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* 「后面要拼路径的基址」的**唯一**判官 —— 中心基址三席(`SEMA_REGISTRY_URL` / `SEMA_REGISTRY_LIVE_URL` / `SEMA_REGISTRY_WEB_URL`)与
|
|
77
|
+
* 本实例的公告地址(`FLEET_ADVERTISE_ADDRESS`,中心往它后面拼 `/v1/...`)同读:可解析的 URL,scheme 是 http / https(Node 的 fetch 认
|
|
78
|
+
* `file://` / `data:`,配错的地址不许变成读本地文件),**不带** userinfo / query / fragment(凭据不进一个会被记日志、转述、拼路径的值;
|
|
79
|
+
* query / fragment 挂在基址上,一拼路径就落到错的位置:`http://x?a=1` + `/api/…` = `http://x?a=1/api/…`)。起服时 `config.ts` 判(拒启),
|
|
80
|
+
* 请求期 {@link centerFetch} 对同一份基址再判一次(自拼 `ServiceConfig` 的嵌入方也吃同一条规则)。合格 ⇒ `undefined`;否则交回**点名规则**
|
|
81
|
+
* 的短语(只回显 scheme,不回显值)。
|
|
82
|
+
*/
|
|
83
|
+
/** 判官「scheme 不是 http / https」那条规则的不变句 —— 拒句 = `<席> ${PATH_BASE_SCHEME_RULE} (got "<scheme>")`(只回显 scheme)。钉读这一只,不手抄。 */
|
|
84
|
+
export declare const PATH_BASE_SCHEME_RULE = "scheme must be http or https";
|
|
85
|
+
export declare function pathBaseUrlFlaw(raw: string): string | undefined;
|
|
71
86
|
/**
|
|
72
87
|
* S-698 —— 凭证续签:`POST {issuer}/api/v1/auth/token/refresh`,form `refresh_token=…`(settings-schema `AUTH_V1_PATHS.tokenRefresh`)。
|
|
73
88
|
* · 以 `issuer` 为根(可带路径前缀;同源与否由续签器先判,本函数只装配);query / fragment 不带过去。
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import settingsSchemaPackage from "@sema-agent/settings-schema/package.json" with { type: "json" };
|
|
2
2
|
import { AUTH_V1_PATHS } from "@sema-agent/settings-schema/api/auth";
|
|
3
|
+
import { FLEET_LIVE_PATHS, FLEET_PATHS } from "@sema-agent/settings-schema/fleet";
|
|
3
4
|
import { CenterCredentialUnavailableError } from "../center-credential.js";
|
|
4
5
|
export const SCHEMA_MAJOR_HEADER = "x-sema-schema-major";
|
|
5
6
|
export const CENTER_PULL_TIMEOUT_MS = 8000;
|
|
@@ -43,15 +44,41 @@ function routePath(route, kind) {
|
|
|
43
44
|
case "prompt-blob":
|
|
44
45
|
return `${contentRoot(kind)}/prompts/blobs/${encodeURIComponent(route.digest)}`;
|
|
45
46
|
case "fleet-leases":
|
|
46
|
-
return
|
|
47
|
+
return FLEET_PATHS.quotaLeases;
|
|
47
48
|
case "fleet-instances":
|
|
48
|
-
return
|
|
49
|
+
return FLEET_PATHS.instances;
|
|
49
50
|
case "fleet-usage":
|
|
50
|
-
return
|
|
51
|
+
return FLEET_PATHS.usage;
|
|
52
|
+
case "fleet-serving-lease":
|
|
53
|
+
return FLEET_PATHS.servingLease;
|
|
51
54
|
case "execution-keys":
|
|
52
55
|
return "/api/config/execution-keys";
|
|
53
56
|
}
|
|
54
57
|
}
|
|
58
|
+
export const PATH_BASE_SCHEME_RULE = "scheme must be http or https";
|
|
59
|
+
export function pathBaseUrlFlaw(raw) {
|
|
60
|
+
let parsed;
|
|
61
|
+
try {
|
|
62
|
+
parsed = new URL(raw);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
parsed = undefined;
|
|
66
|
+
}
|
|
67
|
+
if (parsed === undefined)
|
|
68
|
+
return "is not a parseable URL (no scheme? it must look like http(s)://host[:port][/path])";
|
|
69
|
+
const broken = [
|
|
70
|
+
...(parsed.protocol !== "http:" && parsed.protocol !== "https:" ? [`${PATH_BASE_SCHEME_RULE} (got "${parsed.protocol.replace(/:$/, "")}")`] : []),
|
|
71
|
+
...(parsed.username !== "" || parsed.password !== "" ? ["must not carry userinfo (credentials never ride in a base URL: it is logged, reported and has paths appended)"] : []),
|
|
72
|
+
...(parsed.search !== "" || raw.includes("?") ? ["must not carry a query (request paths are appended to this base)"] : []),
|
|
73
|
+
...(parsed.hash !== "" || raw.includes("#") ? ["must not carry a fragment (request paths are appended to this base)"] : []),
|
|
74
|
+
];
|
|
75
|
+
return broken.length === 0 ? undefined : broken.join("; ");
|
|
76
|
+
}
|
|
77
|
+
function assertPathBaseUrl(raw, what) {
|
|
78
|
+
const flaw = pathBaseUrlFlaw(raw);
|
|
79
|
+
if (flaw !== undefined)
|
|
80
|
+
throw new Error(`${what} ${flaw}`);
|
|
81
|
+
}
|
|
55
82
|
function assertHttpScheme(url, what) {
|
|
56
83
|
const scheme = url.protocol;
|
|
57
84
|
if (scheme !== "http:" && scheme !== "https:")
|
|
@@ -72,7 +99,7 @@ export async function centerTokenRefresh(issuer, refreshToken, init) {
|
|
|
72
99
|
});
|
|
73
100
|
}
|
|
74
101
|
export async function centerFetch(center, route, init) {
|
|
75
|
-
|
|
102
|
+
assertPathBaseUrl(center.baseUrl, "SEMA_REGISTRY_URL");
|
|
76
103
|
const cred = center.credential.read();
|
|
77
104
|
if (!cred.ok)
|
|
78
105
|
throw new CenterCredentialUnavailableError(cred.reason);
|
|
@@ -83,7 +110,11 @@ export async function centerFetch(center, route, init) {
|
|
|
83
110
|
headers["content-type"] = "application/json";
|
|
84
111
|
if (init.etag)
|
|
85
112
|
headers["if-none-match"] = init.etag;
|
|
86
|
-
const
|
|
113
|
+
const path = routePath(route, cred.kind);
|
|
114
|
+
const liveBase = FLEET_LIVE_PATHS.includes(path) ? center.liveBaseUrl : undefined;
|
|
115
|
+
if (liveBase !== undefined)
|
|
116
|
+
assertPathBaseUrl(liveBase, "SEMA_REGISTRY_LIVE_URL");
|
|
117
|
+
const url = `${(liveBase ?? center.baseUrl).replace(/\/+$/, "")}${path}`;
|
|
87
118
|
const res = await (init.fetchImpl ?? fetch)(url, {
|
|
88
119
|
method: init.method ?? "GET",
|
|
89
120
|
headers,
|
package/dist/config-lkg.d.ts
CHANGED
|
@@ -11,11 +11,37 @@ export interface PersistedLkg {
|
|
|
11
11
|
/** skill 正文的 content-addressed 盘缓存目录(LKG 的姊妹面):LKG 只含 skills MANIFEST(正文按
|
|
12
12
|
* hash 另拉)——黑洞中心下 LKG-boot 会在 skill 正文拉取上再挂数秒(live 实测 +5.3s)。正文按
|
|
13
13
|
* `<dir>/<sha-hex>` 落盘,读侧**必验 hash**(content-addressed 的全部意义;坏文件=忽略重拉)。 */
|
|
14
|
-
export declare function defaultSkillCacheDir(
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
export declare function defaultSkillCacheDir(config: {
|
|
15
|
+
readonly agentConfigDir?: string;
|
|
16
|
+
}): string;
|
|
17
|
+
/** 默认路径(scheduler store 同门:配置根 = 配置席 `agentConfigDir` → ~/.sema,{@link configRootOf}),按「worker + 凭证身份」分箱。`CONFIG_LKG_PATH`
|
|
18
|
+
* 覆写(`config.configLkgPath`,读法 {@link lkgPathOverrideOf};单一路径 ⇒ 不分箱,信封的 worker + identity 核仍守读回);`CONFIG_LKG_ENABLED=false` 关闭(读写双关)。
|
|
17
19
|
* 🪦 正名前的 `CONFIG_LKG_DISABLED` 自 server 3.0.0 起是**fail-loud 墓碑**——设了(任何值)即拒启并指路新名,不再被读取。 */
|
|
18
|
-
export declare function defaultLkgPath(
|
|
20
|
+
export declare function defaultLkgPath(config: {
|
|
21
|
+
readonly agentConfigDir?: string;
|
|
22
|
+
}, worker: string | undefined, identity: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* `CONFIG_LKG_PATH`(LKG 落盘路径覆写)的**唯一读法**:没设 ⇒ `undefined`(LKG 落在配置根下按箱派生,{@link defaultLkgPath});设了 ⇒ 绝对路径
|
|
25
|
+
* (相对值按进程 cwd 解,与文件被打开时同一个解法);空串 ⇒ 拒(起服拒):空串解出来是进程工作目录本身 —— 一条落盘路径不该是一个目录,
|
|
26
|
+
* 由它派生的读拒行({@link lkgReadDenyRows})会把整个工作目录读拒掉。`loadConfig` 调它铸 `configLkgPath`,读者只读那一席。
|
|
27
|
+
*/
|
|
28
|
+
export declare function lkgPathOverrideOf(raw: string | undefined): string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* LKG(含凭据)实际所在位置的**读拒行** —— 一条规则:读拒从 LKG 落在哪派生,不写死位置。
|
|
31
|
+
* · 没覆写 ⇒ LKG 落在配置根下(`config-lkg-<箱>.json`):引擎内建表的 `~/.sema/config-lkg-*.json` 行在家目录与声明的根(`agentConfigDir`,
|
|
32
|
+
* 根经链接时引擎连目标一起守)两处守它 ⇒ 不另加行(加了就是同一个文件两只判据,`READ_DENY_BUILTIN_EXCLUDE` 删那一行也删不掉它);
|
|
33
|
+
* · 覆写 ⇒ LKG 在配置根外,引擎那一行够不着 ⇒ 这里派生行:它的**两种拼法**各一行 —— 覆写写下的绝对路径,与文件真正落的地方(路径经符号链接的
|
|
34
|
+
* 目录时,写进去的文件在链接目标下;只守拼写的话读真实路径就绕过了 —— 车内复审 [high])。两者相同 ⇒ 一行。大小写不敏感(与内建行同读法)。
|
|
35
|
+
* 拒句与内建行同一句(`sensitive-path read deny list (pattern …)`)。
|
|
36
|
+
* **行的射程**(引擎读拒表的读法,不是本函数选的):行是路径段串 —— 引擎去掉开头的 `/`、不锚到根,这串段在任意深度出现即拒(大小写不敏感),
|
|
37
|
+
* 所以挡的不止 LKG 那一个文件:用户仓里同段串的路径(`/app/config-lkg.json` ⇒ `…/app/config-lkg.json`)一并读不到、Grep 结果里也不出现。
|
|
38
|
+
* 挡多了优于不挡;引擎提供锚定单文件的行之前如此(随包文案 MIGRATION / 目录行同句)。
|
|
39
|
+
* 读者:部署姿态座席族(`boot/runner-deps.ts` `buildDeploymentPostureSeats` 的 `readDenyPatterns` 席 = 运维的行 ∪ 本函数的行)—— 每次铸席现解一次链接。
|
|
40
|
+
*/
|
|
41
|
+
export declare function lkgReadDenyRows(configLkgPath: string | undefined): {
|
|
42
|
+
pattern: string;
|
|
43
|
+
caseSensitive: boolean;
|
|
44
|
+
}[];
|
|
19
45
|
/** 原子落盘一个干净候选(tmp+rename,0600)。BEST-EFFORT:失败=一条 warn 的素材(调用方发),绝不
|
|
20
46
|
* 影响在飞的应用链——LKG 缓存丢失的代价只是下次冷启动回到 env 兜底。 */
|
|
21
47
|
export declare function saveLkg(path: string, worker: string | undefined, identity: string, effective: Record<string, unknown>, etag: string | undefined): Promise<void>;
|
|
@@ -31,7 +57,9 @@ export declare function loadLkg(path: string, worker: string | undefined, identi
|
|
|
31
57
|
reason: string;
|
|
32
58
|
}>;
|
|
33
59
|
/** 7.98.0 及以前的默认 LKG 路径(按当时的写法逐字保留 —— 这是盘上已有文件的名字,不是现行分箱规则)。 */
|
|
34
|
-
export declare function legacyV1LkgPath(
|
|
60
|
+
export declare function legacyV1LkgPath(config: {
|
|
61
|
+
readonly agentConfigDir?: string;
|
|
62
|
+
}, worker: string | undefined): string;
|
|
35
63
|
export type LegacyLkgMigration = {
|
|
36
64
|
readonly outcome: "none";
|
|
37
65
|
}
|
package/dist/config-lkg.js
CHANGED
|
@@ -1,15 +1,60 @@
|
|
|
1
|
-
import { promises as fs } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
import { join, dirname } from "node:path";
|
|
1
|
+
import { promises as fs, lstatSync, realpathSync } from "node:fs";
|
|
2
|
+
import { basename, join, dirname, resolve } from "node:path";
|
|
4
3
|
import { randomBytes } from "node:crypto";
|
|
5
4
|
import { validatePromptsDomain } from "./prompts-domain-validate.js";
|
|
6
5
|
import { centerStateBin, pullTokenIdentity } from "./center-credential.js";
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import { configRootOf } from "./sema-config-dir.js";
|
|
7
|
+
import { recordFailOpen } from "./observability/fail-open.js";
|
|
8
|
+
export function defaultSkillCacheDir(config) {
|
|
9
|
+
return join(configRootOf(config), "skill-cache");
|
|
9
10
|
}
|
|
10
|
-
export function defaultLkgPath(worker, identity) {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
export function defaultLkgPath(config, worker, identity) {
|
|
12
|
+
return join(configRootOf(config), `config-lkg-${centerStateBin(worker, identity)}.json`);
|
|
13
|
+
}
|
|
14
|
+
export function lkgPathOverrideOf(raw) {
|
|
15
|
+
if (raw === undefined)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (raw === "") {
|
|
18
|
+
throw new Error('env CONFIG_LKG_PATH="" is empty — refusing to start: an empty path resolves to the working directory itself, which is no file the last-known-good ' +
|
|
19
|
+
"config can be kept in. Unset the variable to keep the LKG below the configuration root (SEMA_CONFIG_DIR, default ~/.sema), or set it to the file path you mean.");
|
|
20
|
+
}
|
|
21
|
+
return resolve(raw);
|
|
22
|
+
}
|
|
23
|
+
export function lkgReadDenyRows(configLkgPath) {
|
|
24
|
+
if (configLkgPath === undefined)
|
|
25
|
+
return [];
|
|
26
|
+
return [...new Set([configLkgPath, landedPathOf(configLkgPath)])].map((pattern) => ({ pattern, caseSensitive: false }));
|
|
27
|
+
}
|
|
28
|
+
function landedPathOf(p) {
|
|
29
|
+
const rest = [];
|
|
30
|
+
let landed = p;
|
|
31
|
+
let unresolved;
|
|
32
|
+
for (let head = p;; head = dirname(head)) {
|
|
33
|
+
try {
|
|
34
|
+
landed = join(realpathSync(head), ...rest);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
if (unresolved === undefined && head !== p && segmentPresent(head))
|
|
39
|
+
unresolved = head;
|
|
40
|
+
if (dirname(head) === head)
|
|
41
|
+
break;
|
|
42
|
+
rest.unshift(basename(head));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (unresolved !== undefined)
|
|
46
|
+
recordFailOpen("server.config-lkg.landed-path-unresolved", `segment=${unresolved}`);
|
|
47
|
+
return landed;
|
|
48
|
+
}
|
|
49
|
+
function segmentPresent(p) {
|
|
50
|
+
try {
|
|
51
|
+
lstatSync(p);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
const code = e.code;
|
|
56
|
+
return code !== "ENOENT" && code !== "ENOTDIR";
|
|
57
|
+
}
|
|
13
58
|
}
|
|
14
59
|
export async function saveLkg(path, worker, identity, effective, etag) {
|
|
15
60
|
const body = { formatVersion: 2, savedAt: new Date().toISOString(), worker: worker ?? "global", identity, ...(etag !== undefined ? { etag } : {}), effective };
|
|
@@ -79,10 +124,9 @@ function lkgPayloadProblem(effective, etag) {
|
|
|
79
124
|
return "prompts face fails re-validation";
|
|
80
125
|
return undefined;
|
|
81
126
|
}
|
|
82
|
-
export function legacyV1LkgPath(worker) {
|
|
83
|
-
const dir = process.env.SEMA_CONFIG_DIR ?? join(homedir(), ".sema");
|
|
127
|
+
export function legacyV1LkgPath(config, worker) {
|
|
84
128
|
const key = (worker ?? "global").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 64);
|
|
85
|
-
return join(
|
|
129
|
+
return join(configRootOf(config), `config-lkg-${key}.json`);
|
|
86
130
|
}
|
|
87
131
|
export async function migrateV1Lkg(o) {
|
|
88
132
|
const refuse = (why) => ({
|
package/dist/config-types.d.ts
CHANGED
|
@@ -121,6 +121,16 @@ export declare const DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS: {
|
|
|
121
121
|
readonly maxMs: 10000;
|
|
122
122
|
};
|
|
123
123
|
export declare const DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS = 250;
|
|
124
|
+
/**
|
|
125
|
+
* 本仓读一个租户 scope 的**唯一**读法:给了就是它,缺席 = settings-schema 的缺省 scope(`GLOBAL_SCOPE`;缺省值只在契约包里写一处)。
|
|
126
|
+
* 引擎自己的 scope(`config.ts` 解析 env `FLEET_SCOPE`)与排空请求的 `scope`(`fleet/drain-entry.ts`)走同一只 —— 两侧缺席的读法不会分家。
|
|
127
|
+
* 住在本叶(而不在 `fleet/drain-entry.ts`):`config.ts` 读它,而 drain-entry 值引 `plugins/store-contracts.ts`,config 不值引 plugins 层。
|
|
128
|
+
*/
|
|
129
|
+
export declare function fleetScopeOf(raw: string | undefined): string;
|
|
130
|
+
/** 中心基址三席(`SEMA_REGISTRY_URL` / `SEMA_REGISTRY_LIVE_URL` / `SEMA_REGISTRY_WEB_URL`)的形句 —— 判官 `config-center/center-request.ts` `pathBaseUrlFlaw` 一只(公告地址同判),配置目录三行同读这一句。 */
|
|
131
|
+
export declare const CENTER_BASE_URL_RULE = "\u8BBE\u4E86\u5C31\u5FC5\u987B\u662F\u53EF\u89E3\u6790\u7684 http(s) URL,\u4E14\u4E0D\u5E26\u51ED\u636E / query / fragment(\u57FA\u5740\u540E\u9762\u8981\u62FC\u8DEF\u5F84);\u574F\u5F62\u62D2\u542F(\u6CA1\u63A5\u4E2D\u5FC3\u4E5F\u62D2)";
|
|
132
|
+
/** 编排器片 2:`FLEET_LEASE_OUTAGE_MAX_MS` 的缺省(24 h)—— 中心 / 库故障、中心降级在今天都靠 LKG 无限期容忍,10 分钟级的窗会把它们变成数据面停服。 */
|
|
133
|
+
export declare const FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS = 86400000;
|
|
124
134
|
/**
|
|
125
135
|
* S-753(core 7.33.0 #1121)—— `MODEL_FIRST_BYTE_TIMEOUT_MS`(→ core `BrainTimeoutConfig.firstByteTimeoutMs`,`fetch()` → 响应头
|
|
126
136
|
* 的等待)的**合法域**,闭区间、整数 ms。解析器(`config.ts`)与目录行(`config-catalog.ts`)共读这一处。
|
|
@@ -1994,6 +2004,16 @@ export interface ServiceConfigFlat {
|
|
|
1994
2004
|
* `READ_DENY_BUILTIN_EXCLUDE`=逗号分隔;不在表里的行名 / 空串**启动期响亮拒**。
|
|
1995
2005
|
* 🔴 不变量(判据帖 G5b):删内建**绝不**削弱任务自己显式声明的 deny —— 那是另一层(additions)。 */
|
|
1996
2006
|
readDenyBuiltinExclude?: readonly string[];
|
|
2007
|
+
/** core 7.46.0 #1419:部署**声明**的配置根(`SEMA_CONFIG_DIR`,绝对路径;起服的供给动作建出目录,能不能用由引擎判)—— 交引擎 `RunnerDeps.agentConfigDir`
|
|
2008
|
+
* 与守卫集 `createSensitivePathPolicy` 的同名入参:内建读拒表里以 `.sema` 开头的行在这个根下同样拒,写进根下要问一次。缺席 = 没声明
|
|
2009
|
+
* ⇒ 引擎读字面 `~/.sema`(与 LKG / skill 缓存 / scheduler 店的缺省落点同一处)。读法单一属主 `sema-config-dir.ts`。 */
|
|
2010
|
+
agentConfigDir?: string;
|
|
2011
|
+
/** LKG 落盘路径的覆写(`CONFIG_LKG_PATH`,绝对路径;单一读法 `config-lkg.ts` `lkgPathOverrideOf`)。缺席 = LKG 落在配置根下按箱派生。
|
|
2012
|
+
* 读者:LKG 的落盘 / 读回(`boot/config-center.ts`)与读拒行(部署姿态座席族 —— LKG 在哪,读拒就守哪,`lkgReadDenyRows`)。 */
|
|
2013
|
+
configLkgPath?: string;
|
|
2014
|
+
/** 远程配置的 LKG(last-known-good)盘缓存读写开关(`CONFIG_LKG_ENABLED`,缺省开;只对远程 provider 有意义)。单一读法:`loadConfig` 铸这一席
|
|
2015
|
+
* (同时登记极性行),`boot/config-center.ts` 的两个读点(boot 的 LKG 来源判定、skill 正文缓存目录)只读它 —— 起服之后改环境变量不影响。 */
|
|
2016
|
+
configLkgEnabled: boolean;
|
|
1997
2017
|
/** Structured log level. */
|
|
1998
2018
|
logLevel: "debug" | "info" | "warn" | "error";
|
|
1999
2019
|
/** Per-principal request cap per minute on task/run endpoints. 0 disables. */
|
|
@@ -2083,9 +2103,15 @@ export interface ServiceConfigFlat {
|
|
|
2083
2103
|
* `credential`(S-651,BREAKING:取代 7.98.0 及以前的 `token: string` 启动快照):凭证来源活对象
|
|
2084
2104
|
* (`center-credential.ts`),每条出站请求经它取一次值 —— `SEMA_REGISTRY_TOKEN`(值)或
|
|
2085
2105
|
* `SEMA_REGISTRY_TOKEN_FILE`(每次读文件,轮换 / 换账号不重启)二选一(两键名与判官的属主 = settings-schema
|
|
2086
|
-
* `CENTER_CREDENTIAL_ENV` / `judgeCenterCredentialDeclaration`,S-742)。
|
|
2106
|
+
* `CENTER_CREDENTIAL_ENV` / `judgeCenterCredentialDeclaration`,S-742)。
|
|
2107
|
+
* `liveBaseUrl`(编排器片 2,env `SEMA_REGISTRY_LIVE_URL`):fleet 活性组(心跳 / 注销、服务租约 —— settings-schema `FLEET_LIVE_PATHS`)的基址,
|
|
2108
|
+
* 给「管理面在就绪门后、活性面另走一条不看就绪的入口」的部署;缺席 ⇒ 活性组也打 `baseUrl`(这条缺省只在 `centerFetch` 读)。
|
|
2109
|
+
* `webBaseUrl`(7.121.0,env `SEMA_REGISTRY_WEB_URL`):这个中心的 **web 面固定基址**,运维声明、引擎原样转述到 `/health.center.webBaseUrl`
|
|
2110
|
+
* (壳按它拼会话深链;路径怎么拼归壳)。起服时判成「可拼路径的基址」:http(s)、不带凭据 / query / fragment,否则拒启;缺席即缺席,引擎不拼不猜。 */
|
|
2087
2111
|
configCenter?: {
|
|
2088
2112
|
baseUrl: string;
|
|
2113
|
+
liveBaseUrl?: string;
|
|
2114
|
+
webBaseUrl?: string;
|
|
2089
2115
|
credential: CenterCredentialSource;
|
|
2090
2116
|
dryRun: boolean;
|
|
2091
2117
|
worker?: string;
|
|
@@ -2096,6 +2122,23 @@ export interface ServiceConfigFlat {
|
|
|
2096
2122
|
* 显式值压过派生(部署级旋钮无条件,与 `READ_FACE` 缺省按 posture 派生 S-167 同形)。只在装了租约管理器的部署上有意义
|
|
2097
2123
|
* (`lease:"absent"` 的部署不参与);生效值上 caps `centerWiring.failMode`。 */
|
|
2098
2124
|
fleetLeaseFailClosed: boolean;
|
|
2125
|
+
/** 编排器片 2:fleet 成员的**代际**(env `FLEET_GENERATION`,settings-schema `FLEET_GENERATION_ENV`;配置渲染器是唯一写手)。
|
|
2126
|
+
* 缺席 ⇔ 非 fleet 成员(单机形:不续服务租约、不受围栏)。在场则必须是规范十进制串(`"0"`、`"7"`;不许前导零 / 符号 / 空白 / 空串),
|
|
2127
|
+
* 否则拒启 —— 「设了但读不成代际」是渲染器或手配的事故,不是「非成员」。引擎把它原样回声到心跳与 `/health`(`generation`)。 */
|
|
2128
|
+
fleetGeneration?: string;
|
|
2129
|
+
/** 编排器片 2:本引擎所在的**租户 scope**(env `FLEET_SCOPE`,配置渲染器写)。`loadConfig` 解析一次:缺席 ⇒ 缺省 scope(settings-schema
|
|
2130
|
+
* `GLOBAL_SCOPE`,经本文件的 `fleetScopeOf` —— 本仓唯一读法);在场必须是 settings-schema `ScopeId` 形,否则拒启。
|
|
2131
|
+
* 唯一读者 = 排空口 `POST /v1/admin/drain` 的身份比对;续租体与心跳不带它(那两处的 scope 由中心凭证绑定)。 */
|
|
2132
|
+
fleetScope: string;
|
|
2133
|
+
/** `FLEET_ADVERTISE_ADDRESS`:本实例对中心公告的可达地址(运维声明;k8s pod IP / docker alias / 127.0.0.1:port 只有部署环境自己知道)。
|
|
2134
|
+
* `loadConfig` **无条件**解析一次:未设 / 空串 ⇒ 缺席(公告环不装);在场必须是可解析的 http(s) URL,否则拒启。读者:fleet 公告环、
|
|
2135
|
+
* 围栏判官 `fleetFenceLaneOf`、`/health.center.sessionsVisible`、启动日志 —— 都读这一席,不读 env。 */
|
|
2136
|
+
fleetAdvertiseAddress?: string;
|
|
2137
|
+
/** 编排器片 2:服务租约**中心不可达的容忍上限** `T_max`(ms;env `FLEET_LEASE_OUTAGE_MAX_MS`)。租约过期(中心不可达 / 答 unknown)之后照常服务,
|
|
2138
|
+
* 直到过期满这么久 ⇒ 新计费提交 503 `state.lease_expired`(取消 / HITL 应答 / 续跑 / 读面照放行),在飞不动、不自停;中心恢复授予即恢复。
|
|
2139
|
+
* 缺省 86 400 000(24 h);地板 = 服务租约 TTL 地板(settings-schema `SERVING_LEASE_TTL_FLOOR_MS`,60 s);不许 0 / 无穷,坏值拒启。
|
|
2140
|
+
* 只在租约生效时有意义(fleet 成员 ∧ 中心宣告了 `servingLease` 能力位)。 */
|
|
2141
|
+
fleetLeaseOutageMaxMs: number;
|
|
2099
2142
|
/** S-698(design-325 §2):中心凭证**续签提前量**(ms)—— `SEMA_REGISTRY_TOKEN_FILE` 指向人的登录会话信封
|
|
2100
2143
|
* (`kind:"user-session"`)时,访问令牌离到期 ≤ 本值即由引擎续签并原子改写同一文件(`center-credential-renewer.ts`)。
|
|
2101
2144
|
* 缺省 5 min;`0` = 关续签、只读信封(壳负责续);其余必须是整数毫秒且落在 `CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS`(一次续签请求的
|
|
@@ -2127,13 +2170,13 @@ export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled"
|
|
|
2127
2170
|
* config 这一层它只是一句声明。 */
|
|
2128
2171
|
export type ServiceAuthConfig = Pick<ServiceConfigFlat, "authToken" | "authTokens" | "allowUnauthedWrites" | "corsOrigins" | "principalHeader" | "requirePrincipal" | "autonomy" | "commandPolicy" | "compliancePosture" | "lockedConfigKeys" | "configCatalogEnabled" | "retentionPolicy" | "retentionSweep" | "operatorPrincipals" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "authBridgeIssuer" | "principalJwtMaxTtlSec" | "bindHost" | "bindHostSource">;
|
|
2129
2172
|
/** 组:orchestration(编排 + 执行车道 + 沙箱面 + workflow/后台 agent 存留)。 */
|
|
2130
|
-
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "remoteExecHomeDir" | "requireExecutionLane" | "worktreeIsolation" | "leaderEnabled" | "leaderFanoutEnabled" | "routerEnabled" | "selfOrchestrationEnabled" | "selfOrchestrationModels" | "selfOrchestrationWorkerIsolation" | "forkEnabled" | "experimentalObserverAgents" | "schedulerEnabled" | "schedulerSessionWakeup" | "schedulerSessionLifetime" | "schedulerStorePath" | "planModeEnabled" | "hooksTimeoutMs" | "workflowRunStoreBackend" | "workflowOrphanGraceMs" | "workflowJournalRetentionMs" | "workflowRunRetentionMs" | "usageWindows" | "workflowAgentsReadOnly" | "workflowSizeGuideline" | "backgroundAgentRetentionMs" | "backgroundAgentStaleRunningMs" | "backgroundAgentParkClaimStaleMs" | "delegationEntryCaps" | "rosterRetentionMs" | "leaderRunStaleMs" | "scratchpadSweepTtlMs" | "sandboxPkgSource" | "sessionAutoTitle" | "sessionAutoTitleSource" | "selectEnvironmentTool" | "envFactsEnabled" | "toolDeferLongtail" | "lspEnabled" | "lspHostEnabled" | "lspEnvAllow" | "imageBakes" | "readFace" | "readFaceSource" | "taskWriteFaceEscalation" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude">;
|
|
2173
|
+
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "remoteExecHomeDir" | "requireExecutionLane" | "worktreeIsolation" | "leaderEnabled" | "leaderFanoutEnabled" | "routerEnabled" | "selfOrchestrationEnabled" | "selfOrchestrationModels" | "selfOrchestrationWorkerIsolation" | "forkEnabled" | "experimentalObserverAgents" | "schedulerEnabled" | "schedulerSessionWakeup" | "schedulerSessionLifetime" | "schedulerStorePath" | "planModeEnabled" | "hooksTimeoutMs" | "workflowRunStoreBackend" | "workflowOrphanGraceMs" | "workflowJournalRetentionMs" | "workflowRunRetentionMs" | "usageWindows" | "workflowAgentsReadOnly" | "workflowSizeGuideline" | "backgroundAgentRetentionMs" | "backgroundAgentStaleRunningMs" | "backgroundAgentParkClaimStaleMs" | "delegationEntryCaps" | "rosterRetentionMs" | "leaderRunStaleMs" | "scratchpadSweepTtlMs" | "sandboxPkgSource" | "sessionAutoTitle" | "sessionAutoTitleSource" | "selectEnvironmentTool" | "envFactsEnabled" | "toolDeferLongtail" | "lspEnabled" | "lspHostEnabled" | "lspEnvAllow" | "imageBakes" | "readFace" | "readFaceSource" | "taskWriteFaceEscalation" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "agentConfigDir" | "configLkgPath" | "configLkgEnabled">;
|
|
2131
2174
|
/** 组:limitsHttp(HTTP 面 + 各类上限/配额/回收窗)。 */
|
|
2132
2175
|
export type ServiceLimitsHttpConfig = Pick<ServiceConfigFlat, "port" | "attachmentOrphanGraceMs" | "workspaceFileMaxBytes" | "attachmentMaxBytes" | "attachmentMimeAllowlist" | "attachmentUnboundTtlMs" | "infraCostRates" | "drainGraceMs" | "drainSecondSignalMinGapMs" | "sighupIdleGraceMs" | "parentPid" | "engineAutoExit" | "engineLingerMs" | "engineOrphanLingerMs" | "rateLimitPerMin" | "maxTaskCostUsd" | "maxTaskTokens" | "maxPrincipalCostUsd" | "costQuotaWindowSec" | "reapIntervalSec" | "runStaleSec" | "toolResultTtlSec">;
|
|
2133
2176
|
/** 组:observability(可观测)。 */
|
|
2134
2177
|
export type ServiceObservabilityConfig = Pick<ServiceConfigFlat, "metricsToken" | "traceToken" | "toolTrace" | "traceThinking" | "logLevel" | "otel">;
|
|
2135
2178
|
/** 组:integrations(外部集成)。`mcpServers` / `a2aPeers` 由 sema-registry 适配器填(无 env 腿),归本组。 */
|
|
2136
|
-
export type ServiceIntegrationsConfig = Pick<ServiceConfigFlat, "pluginsAllowHosts" | "mcpServers" | "a2aPeers" | "a2aServe" | "configBootFetchBudgetMs" | "gitApiBaseUrl" | "gitApiToken" | "gitApiKind" | "defaultScenario" | "skillsDir" | "centerCredentialRenewLeadMs" | "configCenter" | "fleetLeaseFailClosed" | "configProvider" | "configLocalDir">;
|
|
2179
|
+
export type ServiceIntegrationsConfig = Pick<ServiceConfigFlat, "pluginsAllowHosts" | "mcpServers" | "a2aPeers" | "a2aServe" | "configBootFetchBudgetMs" | "gitApiBaseUrl" | "gitApiToken" | "gitApiKind" | "defaultScenario" | "skillsDir" | "centerCredentialRenewLeadMs" | "configCenter" | "fleetLeaseFailClosed" | "fleetGeneration" | "fleetScope" | "fleetAdvertiseAddress" | "fleetLeaseOutageMaxMs" | "configProvider" | "configLocalDir">;
|
|
2137
2180
|
/** 九个组槽。每组恒在场(`loadConfig` / `attachConfigGroups` 装好才交出配置),故不可选——新代码写
|
|
2138
2181
|
* `config.modelPlane.model` 不需要 `?.`(可选组会把 `Model` 污染成 `Model | undefined`)。 */
|
|
2139
2182
|
export interface ServiceConfigGroups {
|
package/dist/config-types.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DRAIN_GRACE_FLOOR_MS } from "@sema-agent/settings-schema/fleet";
|
|
2
|
+
import { GLOBAL_SCOPE } from "@sema-agent/settings-schema/api/scopes";
|
|
2
3
|
export const TASK_WRITE_FACE_ESCALATIONS = ["allow", "refuse"];
|
|
3
4
|
export const RETENTION_MODES = ["audit-only", "enforce"];
|
|
4
5
|
export const CONFIG_PROVIDER_WORDS = ["local", "remote"];
|
|
@@ -8,6 +9,11 @@ export const AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS = { minMs: 1_000, maxMs: MAX_TI
|
|
|
8
9
|
export { DRAIN_GRACE_FLOOR_MS };
|
|
9
10
|
export const DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS = { minMs: 0, maxMs: DRAIN_GRACE_FLOOR_MS };
|
|
10
11
|
export const DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS = 250;
|
|
12
|
+
export function fleetScopeOf(raw) {
|
|
13
|
+
return raw ?? GLOBAL_SCOPE;
|
|
14
|
+
}
|
|
15
|
+
export const CENTER_BASE_URL_RULE = "设了就必须是可解析的 http(s) URL,且不带凭据 / query / fragment(基址后面要拼路径);坏形拒启(没接中心也拒)";
|
|
16
|
+
export const FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS = 86_400_000;
|
|
11
17
|
export const FIRST_BYTE_TIMEOUT_BOUNDS = { minMs: 0, maxMs: MAX_TIMER_DELAY_MS };
|
|
12
18
|
export const RESILIENCE_DEFAULTS = {
|
|
13
19
|
firstTokenTimeoutMs: { env: "MODEL_FIRST_TOKEN_TIMEOUT_MS", value: 600_000 },
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Model, type PathRuleBases, type PermissionRule } from "@sema-agent/core";
|
|
2
|
+
import { type CenterEndpoint } from "./config-center/center-request.js";
|
|
2
3
|
import { type Autonomy } from "./runtime-governance.js";
|
|
3
4
|
import { type RunnerEnvNameAllowlistKnob, type ServerEnvNameAllowlistKnob } from "./env-name-allowlist-knobs.js";
|
|
4
5
|
import type { ServiceConfig, ServiceConfigFlat, ServiceConfigGroups } from "./config-types.js";
|
|
@@ -243,10 +244,7 @@ export declare function configKnobTable(): ConfigKnobRecord[];
|
|
|
243
244
|
/** [ref] B4 ②:D 族四枚(`PROJECT_MEMORY` / `CONFIG_LKG` / `HOST_BG` / `HOST_EXEC_SPOOL`)的正名 —— 缺省开,读点只读正名。
|
|
244
245
|
* 3.0.0 之前它们叫 `X_DISABLED`(读点上的双重否定);那四个旧负名是墓碑,在 `RETIRED_ENVS` 里(S-588 起在场即拒启,不论正名在不在)。
|
|
245
246
|
*
|
|
246
|
-
*
|
|
247
|
-
* rather than lifted onto `ServiceConfig`, so this stays a pure env knob; `loadConfig` pre-registers it (below)
|
|
248
|
-
* so its polarity row appears at boot with everything else. */
|
|
249
|
-
export declare const configLkgEnabled: () => boolean;
|
|
247
|
+
* 其中 `CONFIG_LKG_ENABLED`(远程配置的 LKG 盘缓存)已升成配置席 `configLkgEnabled`,由 `loadConfig` 一次铸出;这里只剩另外两个懒读孪生。 */
|
|
250
248
|
/** The `host` exec lane's background-shell capability kill switch (read per capability probe in remote-env-host.ts). */
|
|
251
249
|
export declare const hostBackgroundShellEnabled: () => boolean;
|
|
252
250
|
/** The `host` exec lane's spool-file stdio form; false ⇒ the pre-1.226 pipe fallback (read per exec). */
|
|
@@ -402,6 +400,37 @@ export declare const LIMITS_HTTP_FLOORS: {
|
|
|
402
400
|
readonly attachmentUnboundTtlMs: 60000;
|
|
403
401
|
readonly drainGraceMs: 10000;
|
|
404
402
|
};
|
|
403
|
+
/**
|
|
404
|
+
* 编排器片 2 —— fleet 成员面 env 的拒启码(拼法随 `config.*` 家族,按**规则**命名)。拒启异常把码挂在 `code` 上(`boot/fatal-exit.ts` 按
|
|
405
|
+
* `<name> [<code>]: <message>` 印进 fatal 标题),句子本身不再重复方括号。闭集:加一种 = 加一个键。
|
|
406
|
+
*/
|
|
407
|
+
export declare const FLEET_ENV_REFUSAL_CODES: {
|
|
408
|
+
readonly generation_invalid: "config.fleet_generation_invalid";
|
|
409
|
+
readonly scope_invalid: "config.fleet_scope_invalid";
|
|
410
|
+
readonly advertise_address_invalid: "config.fleet_advertise_address_invalid";
|
|
411
|
+
readonly member_unfenced: "config.fleet_member_unfenced";
|
|
412
|
+
};
|
|
413
|
+
export type FleetEnvRefusalCode = (typeof FLEET_ENV_REFUSAL_CODES)[keyof typeof FLEET_ENV_REFUSAL_CODES];
|
|
414
|
+
/** 一只 fleet 成员的围栏车道:续租打的中心端点(车道对象原样,含活性基址)+ 代际。 */
|
|
415
|
+
export interface FleetFenceLane {
|
|
416
|
+
readonly center: CenterEndpoint & {
|
|
417
|
+
readonly worker: string;
|
|
418
|
+
};
|
|
419
|
+
readonly generation: string;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* 编排器片 2 规则 0(成员 ⇒ 围栏车道必须齐)的**唯一**判官:非成员(`fleetGeneration` 缺席)⇒ `undefined`;成员 ∧ 中心车道齐
|
|
423
|
+
* (`centerLaneConfigured`)∧ 公告地址在 ⇒ 车道;成员而缺任一件 ⇒ 拒启(点名缺的键)。`loadConfig` 在 integrations 域里调一次(拒启早于
|
|
424
|
+
* 任何装配);装配层(第 10 段)对同一份配置再调一次取车道值 —— 同一只判官,过了 `loadConfig` 的配置在那里不会再拒。
|
|
425
|
+
* 读成「非成员」或「装不上就算了」会让一只本该受围栏的实例悄悄脱离围栏:能力位只在心跳答复里,不公告就永远听不到。
|
|
426
|
+
*/
|
|
427
|
+
export declare function fleetFenceLaneOf(cfg: {
|
|
428
|
+
readonly fleetGeneration?: string | undefined;
|
|
429
|
+
readonly configCenter?: (CenterEndpoint & {
|
|
430
|
+
readonly worker?: string | undefined;
|
|
431
|
+
}) | undefined;
|
|
432
|
+
readonly fleetAdvertiseAddress?: string | undefined;
|
|
433
|
+
}): FleetFenceLane | undefined;
|
|
405
434
|
/** 组名 → 该组取景的平铺键(introspection 面:测试用它钉「每个平铺键恰好被一组取景」)。 */
|
|
406
435
|
export declare const CONFIG_GROUP_KEYS: {
|
|
407
436
|
readonly [G in keyof ServiceConfigGroups]: readonly (keyof ServiceConfigFlat)[];
|