@sema-agent/server 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/MIGRATION.md +74 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +391 -0
  5. package/dist/boot/budget-tracing.d.ts +48 -0
  6. package/dist/boot/budget-tracing.js +86 -0
  7. package/dist/boot/config-center.d.ts +62 -0
  8. package/dist/boot/config-center.js +1002 -0
  9. package/dist/boot/coordinators.d.ts +33 -0
  10. package/dist/boot/coordinators.js +97 -0
  11. package/dist/boot/execution-env.d.ts +26 -0
  12. package/dist/boot/execution-env.js +370 -0
  13. package/dist/boot/leader.d.ts +27 -0
  14. package/dist/boot/leader.js +81 -0
  15. package/dist/boot/reapers.d.ts +53 -0
  16. package/dist/boot/reapers.js +252 -0
  17. package/dist/boot/resolve-spec.d.ts +70 -0
  18. package/dist/boot/resolve-spec.js +1072 -0
  19. package/dist/boot/runner-deps.d.ts +101 -0
  20. package/dist/boot/runner-deps.js +343 -0
  21. package/dist/boot/runtime-caps.d.ts +21 -0
  22. package/dist/boot/runtime-caps.js +62 -0
  23. package/dist/boot/session-faces.d.ts +57 -0
  24. package/dist/boot/session-faces.js +157 -0
  25. package/dist/boot/shutdown.d.ts +50 -0
  26. package/dist/boot/shutdown.js +129 -0
  27. package/dist/boot/stores.d.ts +32 -0
  28. package/dist/boot/stores.js +361 -0
  29. package/dist/boot/workflow-orchestration.d.ts +46 -0
  30. package/dist/boot/workflow-orchestration.js +150 -0
  31. package/dist/capabilities/scenarios.d.ts +5 -3
  32. package/dist/capabilities/scenarios.js +5 -3
  33. package/dist/config-center/apply-effective.js +4 -3
  34. package/dist/config-lkg.d.ts +2 -1
  35. package/dist/config-lkg.js +2 -1
  36. package/dist/config-types.d.ts +47 -7
  37. package/dist/config.d.ts +30 -13
  38. package/dist/config.js +562 -387
  39. package/dist/hooks/hook-llm.js +9 -0
  40. package/dist/http/routes/approvals-assistant.js +1 -1
  41. package/dist/http/routes/attachments.js +2 -2
  42. package/dist/http/routes/memory-policy.js +3 -3
  43. package/dist/http/routes/runs.js +1 -1
  44. package/dist/http/routes/session-sync.js +2 -2
  45. package/dist/http/routes/sessions.js +2 -2
  46. package/dist/http/routes/tasks.js +2 -2
  47. package/dist/http/routes/trace-usage.js +2 -2
  48. package/dist/http/routes/workflows.js +3 -1
  49. package/dist/http/server.d.ts +1 -1
  50. package/dist/http/server.js +25 -5
  51. package/dist/http/sse-log.js +1 -1
  52. package/dist/main.js +164 -3799
  53. package/dist/model-select.d.ts +1 -1
  54. package/dist/model-select.js +1 -1
  55. package/dist/plugins/checkpoint-store-sql.d.ts +13 -5
  56. package/dist/plugins/checkpoint-store-sql.js +10 -3
  57. package/dist/plugins/local-checkpoint-store.js +8 -2
  58. package/dist/plugins/remote-env-host.d.ts +2 -1
  59. package/dist/run-local.js +2 -1
  60. package/dist/session-titler.d.ts +3 -1
  61. package/dist/session-titler.js +2 -2
  62. package/dist/trace/project.js +4 -1
  63. package/package.json +5 -3
@@ -11,7 +11,8 @@ export interface PersistedLkg {
11
11
  * `<dir>/<sha-hex>` 落盘,读侧**必验 hash**(content-addressed 的全部意义;坏文件=忽略重拉)。 */
12
12
  export declare function defaultSkillCacheDir(): string;
13
13
  /** 默认路径(scheduler store 同门:SEMA_CONFIG_DIR → ~/.sema)。`CONFIG_LKG_PATH` 覆写;
14
- * `CONFIG_LKG_ENABLED=false` 关闭(读写双关;design/158 B4 正名前叫 `CONFIG_LKG_DISABLED=true`,兼容期内仍认)。 */
14
+ * `CONFIG_LKG_ENABLED=false` 关闭(读写双关)。🪦 正名前的 `CONFIG_LKG_DISABLED` 自 server 3.0.0 起是
15
+ * **fail-loud 墓碑**——设了(任何值)即拒启并指路新名,不再被读取。 */
15
16
  export declare function defaultLkgPath(worker: string | undefined): string;
16
17
  /** 原子落盘一个干净候选(tmp+rename,0600)。BEST-EFFORT:失败=一条 warn 的素材(调用方发),绝不
17
18
  * 影响在飞的应用链——LKG 缓存丢失的代价只是下次冷启动回到 env 兜底。 */
@@ -30,7 +30,8 @@ export function defaultSkillCacheDir() {
30
30
  return join(process.env.SEMA_CONFIG_DIR ?? join(homedir(), ".sema"), "skill-cache");
31
31
  }
32
32
  /** 默认路径(scheduler store 同门:SEMA_CONFIG_DIR → ~/.sema)。`CONFIG_LKG_PATH` 覆写;
33
- * `CONFIG_LKG_ENABLED=false` 关闭(读写双关;design/158 B4 正名前叫 `CONFIG_LKG_DISABLED=true`,兼容期内仍认)。 */
33
+ * `CONFIG_LKG_ENABLED=false` 关闭(读写双关)。🪦 正名前的 `CONFIG_LKG_DISABLED` 自 server 3.0.0 起是
34
+ * **fail-loud 墓碑**——设了(任何值)即拒启并指路新名,不再被读取。 */
34
35
  export function defaultLkgPath(worker) {
35
36
  const dir = process.env.SEMA_CONFIG_DIR ?? join(homedir(), ".sema");
36
37
  const key = (worker ?? "global").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 64);
@@ -34,7 +34,7 @@ export interface ImageBakeConfig {
34
34
  * 2026-07-13, SWR 弃用): `docker.io/claybobby`(海外主源;CN 兜底=内网 Gitea registry)。 */
35
35
  registry: string | null;
36
36
  /** The build-host promoted fetch-once cache base injected as `--cache-base` (NEVER caller-settable — a foreign
37
- * origin would defeat the domestic-only iron rule). `BAKE_CACHE_BASE` (e.g. http://172.17.0.1:8099). */
37
+ * origin would defeat the domestic-only iron rule). `BAKE_CACHE_BASE` (e.g. `http://<build-host>:<port>`). */
38
38
  cacheBase: string | null;
39
39
  /** The pinned current sandbox-base ref injected as `--base-ref` when the caller omits `baseRef` (§P2.4c default).
40
40
  * When NULL the server falls back to the index's latest published `sandbox-base` digest (design §P2.4c
@@ -53,8 +53,10 @@ export interface ImageBakeConfig {
53
53
  submitRateMax: number;
54
54
  submitRateWindowSec: number;
55
55
  }
56
- /** Service configuration, read from environment. */
57
- export interface ServiceConfig {
56
+ /** Service configuration, read from environment — the **平铺面**(the ONE storage location for every
57
+ * field). `ServiceConfig` (bottom of this file) = this flat face **plus** nine read-only group views;
58
+ * fixtures that CONSTRUCT a config literal build this type and hand it to `attachConfigGroups`. */
59
+ export interface ServiceConfigFlat {
58
60
  port: number;
59
61
  /** OpenAI-compatible model gateway base URL (without /chat/completions). */
60
62
  gatewayBaseUrl: string;
@@ -573,8 +575,9 @@ export interface ServiceConfig {
573
575
  /** design/113 C4: host-lane project-memory injection (CLAUDE.md + git narrative). Default **ON** (subject to the
574
576
  * host-lane + single-user gate), so "open like CC" works without config. `PROJECT_MEMORY_ENABLED=false` opts out.
575
577
  * design/158 B4 (lens4 N8): this was `projectMemoryDisabled` — the ONE negative boolean on a ServiceConfig whose
576
- * other 16 flags are positive `*Enabled`/`*Allowed`, so every read site had to invert twice. The retired env name
577
- * `PROJECT_MEMORY_DISABLED` still works for one deprecation window (inverted, with a boot notice). */
578
+ * other 16 flags are positive `*Enabled`/`*Allowed`, so every read site had to invert twice. 🪦 server 3.0.0:
579
+ * the retired env name `PROJECT_MEMORY_DISABLED` is a **fail-loud tombstone** setting it (any value) refuses
580
+ * boot with a message naming this knob; it is no longer read. */
578
581
  projectMemoryEnabled: boolean;
579
582
  /** R7: override the shared scheduler store path. Default `SEMA_CONFIG_DIR`→`~/.sema/scheduled_tasks.json` (the
580
583
  * server writer + the shell daemon reader MUST agree). `SCHEDULER_STORE_PATH`. */
@@ -687,8 +690,9 @@ export interface ServiceConfig {
687
690
  * ON for the host lane (CC-parity); set `LSP_HOST_ENABLED=false` to opt out. (`REMOTE_EXEC=host` only — ssh/adb/
688
691
  * local-docker can't reach a local child process to the right filesystem.)
689
692
  * design/158 B4 (lens4 N7): this lane used to share `LSP_ENABLED` with `lspEnabled` above — one env name driving
690
- * two OPPOSITE defaults. It now has its own knob; `LSP_ENABLED=false` remains a back-compat host opt-out (with a
691
- * boot notice naming the replacement) and `LSP_HOST_ENABLED` wins whenever it is set. */
693
+ * two OPPOSITE defaults. It now has its own knob. 🪦 server 3.0.0: `LSP_ENABLED=false` (the pre-split host
694
+ * opt-out) is a **fail-loud tombstone** it refuses boot naming `LSP_HOST_ENABLED`; `LSP_ENABLED=true` keeps
695
+ * its own meaning (the sandbox lane's opt-in above) and is unaffected. */
692
696
  lspHostEnabled: boolean;
693
697
  /** Graceful drain: how long SIGTERM waits for in-flight turns (live streams + bg/resume legs on this
694
698
  * instance) to finish before the hard shutdown. During drain, new billable submissions get 503+Retry-After and
@@ -849,4 +853,40 @@ export interface ServiceConfig {
849
853
  /** Root dir holding `config.d/<domain>.json` for the local config source. Env: CONFIG_LOCAL_DIR. */
850
854
  configLocalDir?: string;
851
855
  }
856
+ /** 组:store(持久化)—— DB 引擎三态、session 后端、SQL coords、快照 BLOB / SendUserFile 对象存储。 */
857
+ export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "sessionCacheTtlSec" | "rewindSnapshotMaxMb" | "dbBackend" | "dbBackendExplicit" | "localDataRoot" | "tidb" | "pg" | "dbQueryTimeoutMs" | "snapshotBlobStore" | "snapshotBlobSqlMaxBytes" | "snapshotBlobAllowSql" | "sendUserFile">;
858
+ /** 组:modelPlane(模型面)—— 网关坐标、Anthropic 路线、韧性旋钮、主/廉价 model entry、role 表、降级梯。 */
859
+ export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "anthropic" | "resilience" | "model" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "cascadeLadder" | "degrade">;
860
+ /** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
861
+ * 就是本组的门状态,故进组;`parseApprovalDomain` 的返回类型相应是 `Omit<…, "directDoorActive">`。 */
862
+ export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalPollMs" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "askQuestionEnabled" | "toolApprovalEnabled" | "mcpElicitation" | "sensitiveWritePatterns" | "manualModeShellGate">;
863
+ /** 组:memory(记忆面 + TOC 同步腿)。 */
864
+ export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memorySync" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
865
+ /** 组:auth(鉴权 / 身份 / 治理棒)。`commandPolicy` 只有 sema-registry 腿(无 env 标量形),故 env 解析
866
+ * 函数不产出它,但它与 `autonomy` 是同一根治理棒的两半,归本组。 */
867
+ export type ServiceAuthConfig = Pick<ServiceConfigFlat, "authToken" | "authTokens" | "allowUnauthedWrites" | "corsOrigins" | "principalHeader" | "requirePrincipal" | "autonomy" | "commandPolicy" | "operatorPrincipals" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "principalJwtMaxTtlSec" | "bindHost">;
868
+ /** 组:orchestration(编排 + 执行车道 + 沙箱面 + workflow/后台 agent 存留)。 */
869
+ export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "worktreeIsolation" | "leaderEnabled" | "leaderFanoutEnabled" | "routerEnabled" | "selfOrchestrationEnabled" | "selfOrchestrationModels" | "selfOrchestrationWorkerIsolation" | "forkEnabled" | "experimentalObserverAgents" | "schedulerEnabled" | "schedulerSessionWakeup" | "schedulerStorePath" | "planModeEnabled" | "workflowRunStoreBackend" | "workflowOrphanGraceMs" | "workflowJournalRetentionMs" | "workflowRunRetentionMs" | "workflowAgentsReadOnly" | "workflowSizeGuideline" | "backgroundAgentRetentionMs" | "backgroundAgentStaleRunningMs" | "backgroundAgentParkClaimStaleMs" | "rosterRetentionMs" | "scratchpadSweepTtlMs" | "sandboxPkgSource" | "sessionAutoTitle" | "selectEnvironmentTool" | "envFactsEnabled" | "toolDeferLongtail" | "lspEnabled" | "lspHostEnabled" | "imageBakes">;
870
+ /** 组:limitsHttp(HTTP 面 + 各类上限/配额/回收窗)。 */
871
+ export type ServiceLimitsHttpConfig = Pick<ServiceConfigFlat, "port" | "attachmentOrphanGraceMs" | "workspaceFileMaxBytes" | "attachmentMaxBytes" | "attachmentMimeAllowlist" | "attachmentUnboundTtlMs" | "infraCostRates" | "drainGraceMs" | "sighupIdleGraceMs" | "rateLimitPerMin" | "maxTaskCostUsd" | "maxTaskTokens" | "maxPrincipalCostUsd" | "costQuotaWindowSec" | "reapIntervalSec" | "runStaleSec" | "toolResultTtlSec">;
872
+ /** 组:observability(可观测)。 */
873
+ export type ServiceObservabilityConfig = Pick<ServiceConfigFlat, "metricsToken" | "traceToken" | "toolTrace" | "traceThinking" | "logLevel" | "otel">;
874
+ /** 组:integrations(外部集成)。`mcpServers` 由 sema-registry 适配器填(无 env 腿),归本组。 */
875
+ export type ServiceIntegrationsConfig = Pick<ServiceConfigFlat, "pluginsAllowHosts" | "mcpServers" | "configBootFetchBudgetMs" | "gitApiBaseUrl" | "gitApiToken" | "defaultScenario" | "skillsDir" | "oaApiBaseUrl" | "oaServiceToken" | "oaIssue" | "configCenter" | "configProvider" | "configLocalDir">;
876
+ /** 九个组槽。每组恒在场(`loadConfig` / `attachConfigGroups` 装好才交出配置),故不可选——新代码写
877
+ * `config.modelPlane.model` 不需要 `?.`(可选组会把 `Model` 污染成 `Model | undefined`)。 */
878
+ export interface ServiceConfigGroups {
879
+ store: ServiceStoreConfig;
880
+ modelPlane: ServiceModelPlaneConfig;
881
+ approval: ServiceApprovalConfig;
882
+ memory: ServiceMemoryConfig;
883
+ auth: ServiceAuthConfig;
884
+ orchestration: ServiceOrchestrationConfig;
885
+ limitsHttp: ServiceLimitsHttpConfig;
886
+ observability: ServiceObservabilityConfig;
887
+ integrations: ServiceIntegrationsConfig;
888
+ }
889
+ /** Service configuration = 平铺面(唯一存储处,消费点原样) ∩ 九个组视图(additive,不可枚举)。 */
890
+ export interface ServiceConfig extends ServiceConfigFlat, ServiceConfigGroups {
891
+ }
852
892
  //# sourceMappingURL=config-types.d.ts.map
package/dist/config.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { type Model } from "@sema-agent/core";
2
2
  import type { Autonomy } from "./runtime-governance.js";
3
- import type { ServiceConfig } from "./config-types.js";
3
+ import type { ServiceConfig, ServiceConfigFlat, ServiceConfigGroups } from "./config-types.js";
4
4
  /** design/158 A1: the config TYPE face lives in the leaf module config-types.ts; re-exported here so
5
5
  * every existing `from "./config.js"` importer compiles unchanged (pure-type consumers should prefer
6
6
  * importing config-types.js directly — a type-only leaf, no loader baggage). */
7
- export type { ServiceConfig, ScopedMcpServer, ImageBakeConfig } from "./config-types.js";
7
+ export type { ServiceConfig, ScopedMcpServer, ImageBakeConfig, ServiceConfigFlat, ServiceConfigGroups, ServiceStoreConfig, ServiceModelPlaneConfig, ServiceApprovalConfig, ServiceMemoryConfig, ServiceAuthConfig, ServiceOrchestrationConfig, ServiceLimitsHttpConfig, ServiceObservabilityConfig, ServiceIntegrationsConfig, } from "./config-types.js";
8
8
  /** Parse the AUTONOMY env into a validated autonomy mode. Unset/empty → undefined (unmanaged → no extra
9
9
  * tightening). An UNKNOWN value FAILS at startup rather than silently becoming a no-op (a typo'd `AUTONOMY=readonly`
10
10
  * must not silently leave a deployment ungoverned — fail-loud, same discipline as numEnv). Exported so the HOT
@@ -43,33 +43,38 @@ export declare function logConfigDiagnostics(logger: {
43
43
  * Note the deliberate non-migrations: the posture family (`postureOn`) keeps its tri-state — its unset case is
44
44
  * neither `true` nor `false` but "single-user ∧ infra-ready" — and the sentinel-valued knobs (`MEMORY_ENGINE`,
45
45
  * `SANDBOX_PKG_SOURCE`, `MANUAL_MODE_SHELL_GATE`) are enums wearing a boolean's clothes, not booleans. */
46
- type ConfigKnobSource = "env" | "legacy-env" | "posture" | "default";
46
+ /** 复审 2026-07-29 B4:3.0.0 把负名 env 改成 fail-loud 墓碑之后,legacy-env 这个 source 取值**永不可达**——
47
+ * 留着它等于在类型上书面承诺"还有一条 legacy 取值路径"。同批摘掉的还有 `ConfigKnobRecord.legacyName`
48
+ * 与它在 boot 诊断行里的输出:运维在日志里看到 `legacyName: HOST_BG_DISABLED` 的合理结论是"旧名还能
49
+ * 用",而实际设了就拒启——诊断面撒的谎比没有诊断更贵(它会让人先去试那个必然拒启的开关)。 */
50
+ type ConfigKnobSource = "env" | "posture" | "default";
47
51
  export interface ConfigKnobRecord {
48
- /** The CANONICAL env name (the positive one a legacy alias appears in `legacyName`, never here). */
52
+ /** The env name. 3.0.0 起只有这一个名字 —— 负名别名已是墓碑(见 `boolEnvWithLegacyNegated`) */
49
53
  name: string;
50
54
  /** `opt-in` = default OFF, `opt-out` = default ON, `posture` = default derived from REQUIRE_PRINCIPAL ∧ infra. */
51
55
  polarity: "opt-in" | "opt-out" | "posture";
52
56
  value: boolean;
53
57
  source: ConfigKnobSource;
54
- /** Set when the value came from (or the knob still accepts) a deprecated negative-form alias. */
55
- legacyName?: string;
56
58
  }
57
59
  /** The registered boolean knobs, sorted by name — the data behind `logConfigDiagnostics`'s polarity table. */
58
60
  export declare function configKnobTable(): ConfigKnobRecord[];
59
- /** design/158 N8 (D-family) a knob whose CANONICAL name is positive (`X_ENABLED`, default ON) but which keeps
60
- * its retired negative name (`X_DISABLED`) working for one deprecation window.
61
+ /** design/158 N8 (D-family) + server 3.0.0 墓碑 —— a knob whose CANONICAL name is positive (`X_ENABLED`,
62
+ * default ON) and whose retired negative name (`X_DISABLED`) is now a **fail-loud tombstone**.
61
63
  *
62
64
  * Why the flip at all: `X_DISABLED !== "true"` is a double negative at the read site, so the four knobs that
63
65
  * used it (`PROJECT_MEMORY`, `CONFIG_LKG`, `HOST_BG`, `HOST_EXEC_SPOOL`) could not be read for their default
64
66
  * without inverting twice in your head, and `projectMemoryDisabled` was the ONE negative boolean on a
65
67
  * `ServiceConfig` that otherwise carries 16 positive `*Enabled`/`*Allowed` fields.
66
68
  *
67
- * Resolution order (new name wins, so there is never an ambiguous both-set state):
69
+ * 🔴 3.0.0([1989],clay 令「不做兼容」):旧名不再有兼容窗——**设了就拒启**。这个函数保留 legacy 形参
70
+ * 不是为了继续读它,而正是为了**认出它在场并拒绝启动**(把墓碑放在唯一知道这对名字关系的地方)。
71
+ *
72
+ * Resolution order:
68
73
  * 1. `X_ENABLED` set to a literal ⇒ that.
69
- * 2. `X_ENABLED` set to a non-literal ⇒ default + warning (the legacy name is NOT consulted — a typo in the
70
- * new name must not silently hand control back to the deprecated one).
71
- * 3. `X_ENABLED` unset ∧ `X_DISABLED` set ⇒ the legacy site's exact contract, `X_DISABLED !== "true"`, plus a
72
- * one-shot deprecation notice naming the replacement.
74
+ * 2. `X_ENABLED` set to a non-literal ⇒ default + warning (the tombstone is NOT consulted — a typo in the
75
+ * new name must not turn into a boot refusal about an env the operator never set).
76
+ * 3. `X_ENABLED` unset ∧ `X_DISABLED` set(**任何值**,含 `"false"`)⇒ throw,文案指路新名。选拒启不选
77
+ * 静默忽略:静默会让升级部署的旧开关名义在、实际归默认——比拒启危险,且下游冒烟测试测不出来。
73
78
  * 4. neither ⇒ default. */
74
79
  export declare function boolEnvWithLegacyNegated(name: string, legacyNegatedName: string, def: boolean): boolean;
75
80
  /** LKG (last-known-good) persistence of the remote effective-config fetch. Read LAZILY at both main.ts sites
@@ -85,6 +90,18 @@ export declare const hostExecSpoolEnabled: () => boolean;
85
90
  * is REMOVED (a spread-inherited value from a bigger-window base model would mis-trigger a small window).
86
91
  * Shared by the env lane (main + cheap models) and the center lane (sema-registry toModel). */
87
92
  export declare function applyAutoCompactWindow(m: Model, explicit?: number): void;
93
+ /** 组名 → 该组取景的平铺键(introspection 面:测试用它钉「每个平铺键恰好被一组取景」)。 */
94
+ export declare const CONFIG_GROUP_KEYS: {
95
+ readonly [G in keyof ServiceConfigGroups]: readonly (keyof ServiceConfigFlat)[];
96
+ };
97
+ /** 把九个组视图挂到平铺配置上(**就地**,返回的就是同一个对象——视图必须看着这一个存储处)。
98
+ *
99
+ * 组键是**不可枚举**的:`Object.keys(config)` / `JSON.stringify(config)` / `{...config}` 的平铺面因此
100
+ * 逐字节不变(既有消费点、既有 dump/审计面零影响),代价是 spread 克隆不带组视图——克隆后要用组形,
101
+ * 再过一次本函数(读一个缺席的组是当场 TypeError,不会静默给旧值)。
102
+ *
103
+ * `loadConfig` 是主调用点;测试夹具(test/helpers/make-config.ts)构造 `ServiceConfigFlat` 后同样过它。 */
104
+ export declare function attachConfigGroups(flat: ServiceConfigFlat): ServiceConfig;
88
105
  export declare function loadConfig(): ServiceConfig;
89
106
  /** 有效监听绑址([1934])。**显式 `BIND_HOST`/`HOST` 恒生效**(operator 保留在任何形下明示暴露的
90
107
  * 权利);未显式时:**无鉴权写面 ⇒ 127.0.0.1**,其余 ⇒ undefined(Node 默认全接口,既有部署零影响)。