@sema-agent/server 7.135.0 → 7.137.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 +237 -2
- package/USAGE.md +146 -41
- package/deploy/sema-up/README.md +15 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
- package/dist/approval-card.d.ts +60 -4
- package/dist/approval-card.js +18 -1
- package/dist/approval-content-kind.d.ts +7 -0
- package/dist/approval-content-kind.js +8 -1
- package/dist/approval-gate-currency.d.ts +48 -6
- package/dist/approval-gate-currency.js +16 -9
- package/dist/approval-hmac.d.ts +13 -1
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.js +3 -3
- package/dist/auth-bridge.d.ts +34 -5
- package/dist/auth-bridge.js +22 -5
- package/dist/boot/budget-tracing.d.ts +1 -2
- package/dist/boot/budget-tracing.js +2 -2
- package/dist/boot/execution-env.d.ts +24 -1
- package/dist/boot/execution-env.js +69 -6
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +15 -1
- package/dist/boot/resolve-spec.d.ts +4 -0
- package/dist/boot/resolve-spec.js +24 -22
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +4 -2
- package/dist/boot/stage-05-execution-env.js +11 -2
- package/dist/boot/stage-06-runners.d.ts +4 -2
- package/dist/boot/stage-06-runners.js +5 -1
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-08-reapers.js +2 -1
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +2 -3
- package/dist/boot/web-admin.js +10 -17
- package/dist/brain.js +12 -3
- package/dist/broker/broker.d.ts +89 -0
- package/dist/broker/broker.js +287 -0
- package/dist/broker/constants.d.ts +39 -0
- package/dist/broker/constants.js +15 -0
- package/dist/broker/http1.d.ts +111 -0
- package/dist/broker/http1.js +256 -0
- package/dist/broker/lease-book.d.ts +87 -0
- package/dist/broker/lease-book.js +142 -0
- package/dist/broker/lease-file.d.ts +60 -0
- package/dist/broker/lease-file.js +127 -0
- package/dist/broker/main.d.ts +2 -0
- package/dist/broker/main.js +20 -0
- package/dist/budget.d.ts +3 -3
- package/dist/budget.js +4 -0
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/engine-capabilities.js +1 -0
- package/dist/capabilities/mcp-probe.d.ts +5 -4
- package/dist/capabilities/scenarios.d.ts +23 -8
- package/dist/capabilities/scenarios.js +13 -8
- package/dist/capabilities/skills.d.ts +28 -5
- package/dist/capabilities/skills.js +8 -0
- package/dist/capability-facts.d.ts +3 -7
- package/dist/capability-facts.js +3 -5
- package/dist/center-credential-renewer.js +5 -1
- package/dist/center-credential.d.ts +18 -4
- package/dist/center-credential.js +18 -3
- package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
- package/dist/checkpoint-ctx-seal-m28.js +1 -1
- package/dist/checkpoint-identity.d.ts +3 -3
- package/dist/config-catalog.js +21 -16
- package/dist/config-d-migration.js +1 -1
- package/dist/config-invariants.d.ts +1 -1
- package/dist/config-invariants.js +8 -0
- package/dist/config-lkg.d.ts +0 -38
- package/dist/config-lkg.js +1 -83
- package/dist/config-types.d.ts +21 -4
- package/dist/config.d.ts +1 -1
- package/dist/config.js +60 -16
- package/dist/deployment-governance.d.ts +2 -2
- package/dist/deployment-governance.js +7 -9
- package/dist/deployment-shape.d.ts +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/elicitation.d.ts +8 -7
- package/dist/elicitation.js +9 -8
- package/dist/file-history-disclosure.d.ts +8 -4
- package/dist/file-history-disclosure.js +24 -6
- package/dist/fleet-client.d.ts +32 -1
- package/dist/fleet-client.js +24 -4
- package/dist/fleet-lease.d.ts +7 -3
- package/dist/fleet-lease.js +31 -23
- package/dist/hooks/classic-stations.d.ts +11 -54
- package/dist/hooks/hook-llm.js +14 -8
- package/dist/hooks/hook-runner.d.ts +4 -4
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/admission.js +10 -7
- package/dist/http/approval-sweeps.js +4 -3
- package/dist/http/caller-trust.d.ts +2 -1
- package/dist/http/caller-trust.js +1 -3
- package/dist/http/checkpoint-ctx.d.ts +76 -16
- package/dist/http/checkpoint-ctx.js +45 -1
- package/dist/http/dispatch.js +24 -19
- package/dist/http/durable-run-leg.d.ts +5 -1
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/health-self-report.d.ts +2 -2
- package/dist/http/mcp-remote-text.d.ts +5 -0
- package/dist/http/mcp-remote-text.js +7 -0
- package/dist/http/principal-gate.d.ts +15 -10
- package/dist/http/principal-gate.js +48 -9
- package/dist/http/resume-ctx-read.d.ts +2 -16
- package/dist/http/resume-ctx-read.js +2 -3
- package/dist/http/resume-legs.d.ts +2 -20
- package/dist/http/resume-legs.js +52 -38
- package/dist/http/route-ctx.d.ts +8 -2
- package/dist/http/routes/approvals-assistant.d.ts +0 -2
- package/dist/http/routes/approvals-assistant.js +65 -16
- package/dist/http/routes/capabilities.js +11 -1
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +5 -7
- package/dist/http/routes/runs.js +4 -3
- package/dist/http/routes/session-sync.js +70 -12
- package/dist/http/routes/sessions.js +20 -15
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +4 -0
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +6 -1
- package/dist/http/submit-ctx.js +1 -1
- package/dist/http/wire-types.d.ts +57 -30
- package/dist/identity-source.d.ts +65 -0
- package/dist/identity-source.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/memory-store-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.d.ts +2 -0
- package/dist/model-entry-refusal.js +11 -0
- package/dist/model-route-gap.d.ts +49 -4
- package/dist/model-route-gap.js +34 -1
- package/dist/model-select.d.ts +6 -17
- package/dist/model-select.js +0 -3
- package/dist/namespaced-rule-name.d.ts +1 -1
- package/dist/object-store-buckets.d.ts +13 -0
- package/dist/object-store-buckets.js +3 -0
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/metrics.js +2 -2
- package/dist/observability/principal-context.d.ts +4 -1
- package/dist/observability/principal-context.js +6 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/operator-ask.d.ts +2 -2
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +2 -2
- package/dist/org-memory-admission.js +39 -2
- package/dist/parked-decide.d.ts +4 -3
- package/dist/parked-decide.js +29 -9
- package/dist/peer-directory.d.ts +2 -1
- package/dist/peer-directory.js +7 -5
- package/dist/permission-rule-list.d.ts +2 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -0
- package/dist/plugins/blob-backend.js +2 -1
- package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
- package/dist/plugins/checkpoint-index-columns.js +28 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
- package/dist/plugins/checkpoint-store-sql.js +64 -35
- package/dist/plugins/file-history-store-sql.d.ts +6 -0
- package/dist/plugins/file-history-store-sql.js +37 -37
- package/dist/plugins/file-run-store.js +8 -2
- package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
- package/dist/plugins/k8s-exec-protocol.js +116 -28
- package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
- package/dist/plugins/k8s-pod-delete-failure.js +17 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
- package/dist/plugins/k8s-pod-reaper.js +122 -0
- package/dist/plugins/local-checkpoint-store.d.ts +3 -6
- package/dist/plugins/local-checkpoint-store.js +7 -6
- package/dist/plugins/memory-run-store.js +2 -0
- package/dist/plugins/one-time-migrations.d.ts +49 -37
- package/dist/plugins/one-time-migrations.js +87 -128
- package/dist/plugins/permission-rule-store-file.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
- package/dist/plugins/permission-rule-store-sql.js +16 -17
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/pg-session-storage.js +2 -2
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +4 -0
- package/dist/plugins/remote-env-e2b.d.ts +3 -0
- package/dist/plugins/remote-env-e2b.js +1 -0
- package/dist/plugins/remote-env-file-error.d.ts +8 -1
- package/dist/plugins/remote-env-file-error.js +7 -1
- package/dist/plugins/remote-env-host.d.ts +7 -1
- package/dist/plugins/remote-env-host.js +7 -1
- package/dist/plugins/remote-env-k8s.d.ts +154 -14
- package/dist/plugins/remote-env-k8s.js +300 -21
- package/dist/plugins/remote-env-local-docker.d.ts +3 -0
- package/dist/plugins/remote-env-local-docker.js +1 -0
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/run-store-sql.js +10 -4
- package/dist/plugins/s3-answer.d.ts +15 -0
- package/dist/plugins/s3-answer.js +5 -0
- package/dist/plugins/session-ownership.d.ts +17 -3
- package/dist/plugins/session-ownership.js +19 -5
- package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
- package/dist/plugins/snapshot-bucket-presence.js +35 -7
- package/dist/plugins/sql-errors.d.ts +60 -0
- package/dist/plugins/sql-errors.js +64 -1
- package/dist/plugins/store-contracts.d.ts +10 -1
- package/dist/plugins/store-fault.d.ts +4 -0
- package/dist/plugins/store-fault.js +10 -0
- package/dist/plugins/tidb-pool.js +8 -1
- package/dist/plugins/tidb-session-store.js +2 -2
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -2
- package/dist/request-declaration-refusal.d.ts +94 -0
- package/dist/request-declaration-refusal.js +78 -0
- package/dist/request-image-urls.d.ts +6 -0
- package/dist/request-image-urls.js +27 -0
- package/dist/request-key-closure.d.ts +5 -0
- package/dist/request-key-closure.js +5 -2
- package/dist/request-value-closure.d.ts +31 -2
- package/dist/request-value-closure.js +7 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +2 -2
- package/dist/rules-consent.js +7 -4
- package/dist/run-local.d.ts +2 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +1 -1
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sandbox-git-lease.d.ts +49 -0
- package/dist/sandbox-git-lease.js +60 -0
- package/dist/sandbox-git-plane.d.ts +84 -0
- package/dist/sandbox-git-plane.js +105 -0
- package/dist/sandbox-pkg-source.d.ts +28 -1
- package/dist/sandbox-pkg-source.js +1 -2
- package/dist/sealed-key.d.ts +19 -0
- package/dist/sealed-key.js +47 -4
- package/dist/security.d.ts +16 -4
- package/dist/security.js +8 -3
- package/dist/server-secret-env.d.ts +6 -0
- package/dist/server-secret-env.js +14 -0
- package/dist/session-error-wire.d.ts +14 -6
- package/dist/session-error-wire.js +2 -1
- package/dist/session-incarnation.d.ts +16 -5
- package/dist/session-incarnation.js +7 -4
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-policy-wire.js +2 -2
- package/dist/session-row.d.ts +3 -17
- package/dist/session-row.js +4 -0
- package/dist/session-sync.d.ts +15 -3
- package/dist/session-sync.js +5 -2
- package/dist/spec-fields.d.ts +43 -13
- package/dist/spec-fields.js +33 -12
- package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
- package/dist/stored-request-prompt-profile-m30.js +25 -0
- package/dist/task-a2a.d.ts +52 -81
- package/dist/task-a2a.js +219 -89
- package/dist/task-mcp.d.ts +7 -15
- package/dist/task-mcp.js +16 -51
- package/dist/task-settings.d.ts +2 -2
- package/dist/tool-approval.d.ts +35 -12
- package/dist/tool-approval.js +59 -39
- package/dist/tool-rule-names.d.ts +5 -0
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.js +37 -13
- package/dist/trace/ledger-sink.d.ts +4 -5
- package/dist/trace/ledger-sink.js +3 -7
- package/dist/trace/project.d.ts +37 -3
- package/dist/trace/project.js +20 -6
- package/dist/trace/redact.d.ts +40 -12
- package/dist/trace/redact.js +332 -128
- package/dist/trace/self-minted-wire.d.ts +641 -0
- package/dist/trace/self-minted-wire.js +201 -0
- package/dist/trace/wire-projection-faces.d.ts +5 -3
- package/dist/trace/wire-projection-faces.js +7 -3
- package/dist/url-credential-refusal.d.ts +52 -0
- package/dist/url-credential-refusal.js +14 -0
- package/dist/url-refusal.d.ts +13 -4
- package/dist/url-refusal.js +8 -2
- package/dist/web-admin-client/apply-effective.js +0 -1
- package/dist/web-admin-client/center-request.d.ts +20 -2
- package/dist/web-admin-client/center-request.js +40 -5
- package/dist/web-admin-client/http-client.js +6 -6
- package/dist/web-admin-client/types.d.ts +5 -5
- package/dist/workspace-space.d.ts +27 -0
- package/dist/workspace-space.js +26 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
- package/package.json +4 -3
- package/scripts/drain-check-local.mjs +2 -1
- package/scripts/drain-check.mysql.sql +3 -1
- package/scripts/drain-check.pg.sql +3 -1
- package/skills/schedule.md +1 -1
- package/wire-changes/server@7.104.0.json +134 -0
- package/wire-changes/server@7.105.0.json +53 -0
- package/wire-changes/server@7.106.0.json +475 -0
- package/wire-changes/server@7.107.0.json +64 -0
- package/wire-changes/server@7.108.0.json +153 -0
- package/wire-changes/server@7.109.0.json +46 -0
- package/wire-changes/server@7.110.0.json +90 -0
- package/wire-changes/server@7.111.0.json +86 -0
- package/wire-changes/server@7.112.0.json +60 -0
- package/wire-changes/server@7.113.0.json +26 -0
- package/wire-changes/server@7.114.0.json +26 -0
- package/wire-changes/server@7.115.0.json +97 -0
- package/wire-changes/server@7.116.0.json +218 -0
- package/wire-changes/server@7.116.1.json +26 -0
- package/wire-changes/server@7.117.0.json +65 -0
- package/wire-changes/server@7.118.0.json +129 -0
- package/wire-changes/server@7.119.0.json +65 -0
- package/wire-changes/server@7.120.0.json +89 -0
- package/wire-changes/server@7.121.0.json +219 -0
- package/wire-changes/server@7.122.0.json +54 -0
- package/wire-changes/server@7.123.0.json +26 -0
- package/wire-changes/server@7.124.0.json +336 -0
- package/wire-changes/server@7.124.1.json +26 -0
- package/wire-changes/server@7.125.0.json +111 -0
- package/wire-changes/server@7.126.0.json +285 -0
- package/wire-changes/server@7.127.0.json +77 -0
- package/wire-changes/server@7.127.1.json +26 -0
- package/wire-changes/server@7.127.2.json +53 -0
- package/wire-changes/server@7.128.0.json +26 -0
- package/wire-changes/server@7.129.0.json +106 -0
- package/wire-changes/server@7.129.1.json +26 -0
- package/wire-changes/server@7.130.0.json +26 -0
- package/wire-changes/server@7.131.0.json +118 -0
- package/wire-changes/server@7.132.0.json +226 -0
- package/wire-changes/server@7.133.0.json +1155 -0
- package/wire-changes/server@7.134.0.json +107 -0
- package/wire-changes/server@7.135.0.json +174 -0
- package/wire-changes/server@7.136.0.json +448 -0
- package/wire-changes/server@7.137.0.json +825 -0
- package/dist/url-credential.d.ts +0 -52
- package/dist/url-credential.js +0 -49
|
@@ -1,11 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「谁也不指的串」的描述句 —— 拒句里「给的是什么」那一格的串形(从不回显值:一个谁也不指的串可能就是一段被粘进来的凭据)。
|
|
3
|
+
* 本模块的属主拒与鉴权边界的主体拒(`security.ts` `assertPrincipalShape` 的 `principal_not_a_name`)读这同一句:判官同一只,描述也同一句。
|
|
4
|
+
*/
|
|
5
|
+
export declare const NOT_A_PRINCIPAL_NAME = "a string that names nobody (empty, blank, or holding a control, separator or invisible format character)";
|
|
6
|
+
/**
|
|
7
|
+
* 「这个值能不能作一只会话行的属主格」—— core 7.56.0 [ref] 起只有一只判官 `principalNameOf`:主体名(有可见内容、不含控制 / 分隔 / 非连接符格式字符,
|
|
8
|
+
* 原样保存不规整)或 `null`(匿名)。空串、空白串、带不可见字符的串与一切非串都不是属主(它们谁也不指 —— 读成匿名会放行每一位主体)。
|
|
9
|
+
*
|
|
10
|
+
* 边界同判官:HTTP 请求带来的主体在鉴权边界已经过同一只 `principalNameOf`(`security.ts` `principalShapeProblem` 的 `not_a_name`,7.137.0)⇒
|
|
11
|
+
* 经过边界的请求到不了这里的拒臂;这里是纵深(嵌入方直调店、别的写者),规则只有引擎那一只,不在本仓另写。
|
|
12
|
+
*/
|
|
13
|
+
export declare function isSessionOwnerValue(owner: unknown): owner is string | null;
|
|
1
14
|
/**
|
|
2
15
|
* fork 拷贝的属主 —— core `SessionStore.fork` 契约的唯一读法:
|
|
3
|
-
*
|
|
16
|
+
* 显式主体名 ⇒ 该名;无参 / 显式 `null` ⇒ 源会话记下的属主(`undefined` = 源会话没记,拷贝也不记)。
|
|
17
|
+
* 显式给了却不是主体名(空串 / 空白 / 带不可见字符,core 7.56.0 [ref])⇒ 响亮拒(`SessionError("storage")`,不留行)—— 与 `register` 首写同一只判官。
|
|
4
18
|
*/
|
|
5
19
|
export declare function forkOwnerOf(requested: string | null | undefined, sourceOwner: string | null | undefined): string | null | undefined;
|
|
6
20
|
/**
|
|
7
|
-
* `register` 首写的 owner 守卫 —— core `SessionStore.register` 契约的唯一读法(三只店同调):首写的 owner
|
|
8
|
-
* 「首写」由调用方判(已记下属主的会话上的调用照旧静默不改,不经这里)。
|
|
21
|
+
* `register` 首写的 owner 守卫 —— core `SessionStore.register` 契约的唯一读法(三只店同调):首写的 owner 既不是主体名也不是 `null` ⇒ 响亮拒(不留行);
|
|
22
|
+
* 「首写」由调用方判(已记下属主的会话上的调用照旧静默不改,不经这里)。判官 = {@link isSessionOwnerValue}(core 7.56.0 [ref] `principalNameOf`)。
|
|
9
23
|
*/
|
|
10
24
|
export declare function assertFirstOwnerWritable(sessionId: string, owner: unknown): asserts owner is string | null;
|
|
11
25
|
//# sourceMappingURL=session-ownership.d.ts.map
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import { SessionError } from "@sema-agent/core";
|
|
1
|
+
import { SessionError, principalNameOf } from "@sema-agent/core";
|
|
2
|
+
export const NOT_A_PRINCIPAL_NAME = "a string that names nobody (empty, blank, or holding a control, separator or invisible format character)";
|
|
3
|
+
function ownerValueFault(owner) {
|
|
4
|
+
if (typeof owner === "string")
|
|
5
|
+
return NOT_A_PRINCIPAL_NAME;
|
|
6
|
+
return owner === undefined ? "undefined" : typeof owner;
|
|
7
|
+
}
|
|
8
|
+
export function isSessionOwnerValue(owner) {
|
|
9
|
+
return owner === null || principalNameOf(owner) !== undefined;
|
|
10
|
+
}
|
|
2
11
|
export function forkOwnerOf(requested, sourceOwner) {
|
|
3
|
-
|
|
12
|
+
if (requested === undefined || requested === null)
|
|
13
|
+
return sourceOwner;
|
|
14
|
+
if (principalNameOf(requested) === undefined) {
|
|
15
|
+
throw new SessionError("storage", `fork: session owner must be a principal name or null, got ${ownerValueFault(requested)} — refusing, nothing written`);
|
|
16
|
+
}
|
|
17
|
+
return requested;
|
|
4
18
|
}
|
|
5
19
|
export function assertFirstOwnerWritable(sessionId, owner) {
|
|
6
|
-
if (
|
|
20
|
+
if (isSessionOwnerValue(owner))
|
|
7
21
|
return;
|
|
8
|
-
throw new SessionError("storage", `register(${sessionId}): a first ownership write must carry a principal
|
|
9
|
-
"a
|
|
22
|
+
throw new SessionError("storage", `register(${sessionId}): a first ownership write must carry a principal name or null (anonymous), got ${ownerValueFault(owner)} — ` +
|
|
23
|
+
"a value that names nobody would leave the session claimable by anyone or ownable by no one; refusing, nothing written");
|
|
10
24
|
}
|
|
11
25
|
//# sourceMappingURL=session-ownership.js.map
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* `SESSION_SNAPSHOT_BUCKET`(缺省 `session-snapshots`)是 snapshot-BLOB offload 与 SendUserFile 私有 presigned 轨 / 沙箱 staging 的桶。server 不建桶
|
|
6
6
|
* (`docs/DEPLOY-PREREQS.md`「对象存储桶」),sema-up 的建桶清单又漏过它 ⇒ 起服绿,之后每一次对象写、每一拍孤儿 sweep 的列举(每分钟)都是
|
|
7
7
|
* 404 NoSuchBucket —— 运维读到的是一串「失败的请求」,不是「桶没建」。
|
|
8
|
+
* `WORKSPACE_SNAPSHOT_BUCKET`(缺省 `workspaces`,k8s 工作区快照桶)同病且更隐:起服不核它 ⇒ 缺桶 / 无权限时每条 run 读 `latest` 得 404,
|
|
9
|
+
* 被读成「这个空间头一回」,从空目录起;run 末的保存虽失败,报的却是一条不点名桶的 curl 404,没有一行说「桶没建」。
|
|
8
10
|
*
|
|
9
11
|
* ## 形(一条规则,不加特例)
|
|
10
12
|
* · 核的是 {@link MinioBlobBackend.probeBucket}(presigned 列举 `max-keys=1`);**只有** 404 + `NoSuchBucket` 是「缺」,其余证明不了的形(连不上 / 403 / 5xx / 没有
|
|
@@ -13,24 +15,57 @@
|
|
|
13
15
|
* · 缺 = 配置好的店资源还没就绪,与「库连不上」同一类处境:在 `STORE_LOCK_WAIT_MS` 窗内重试({@link openWithinWait},同一只窗、同一只节拍、同一条窗尾规则 —— 编排里
|
|
14
16
|
* 建桶晚于 server 起是常态),到窗时手上有缺桶的证据 ⇒ 拒启,拒句点名桶名、`MINIO_ENDPOINT` 的显示安全形(`buildDisplaySafeUrl` 的产物:userinfo 落地、
|
|
15
17
|
* query 值成记号,不带密钥)与出路;探针从头到尾没答过(每一次都挂到窗尾)⇒ 同样是「证明不了」,走上面那只兜底口、照常起服。
|
|
16
|
-
* ·
|
|
18
|
+
* · 核的桶是一张角色表({@link SNAPSHOT_BUCKET_ROLES},闭集两行)里的两只,**同一只探针、同一条规则、同一只兜底口**:
|
|
19
|
+
* `session` = `config.snapshotBlobStore.bucket`(`SESSION_SNAPSHOT_BUCKET`;SendUserFile 的私桶缺省同一只);
|
|
20
|
+
* `workspace` = k8s 车道的 `config.remoteExec.s3Snapshot.bucket`(`WORKSPACE_SNAPSHOT_BUCKET`,缺省 `workspaces`)—— 车道没开 / 没配对象存储 ⇒ 没有这只桶,不核。
|
|
21
|
+
* 拒句的旋钮名跟着桶走(缺的是哪只桶就只点哪只的旋钮)。公桶 `sema-public` 是运维另配的匿名桶,不在本核之内。
|
|
22
|
+
* · 运行期的对应臂:工作区 `latest` 指针读答 404 `NoSuchBucket`(同一条「桶不在」判据 `isNoSuchBucketAnswer`)⇒ 那条 run 响亮拒绝,不再读成「空间头一回」
|
|
23
|
+
* (`remote-env-k8s.ts` `readPointer`)—— 起服核之后桶被删 / 起服核证明不了的部署,在运行期也有一道。
|
|
17
24
|
*/
|
|
18
25
|
import type { ServiceConfig } from "../config-types.js";
|
|
19
26
|
import { type StoreOpenWaitClock } from "./store-open-wait.js";
|
|
20
27
|
type SnapshotBlobStoreConfig = NonNullable<ServiceConfig["snapshotBlobStore"]>;
|
|
28
|
+
/** 被核的桶的角色(闭集):`session` = 快照 blob / SendUserFile 私桶;`workspace` = k8s 工作区快照桶。 */
|
|
29
|
+
export type SnapshotBucketRole = "session" | "workspace";
|
|
30
|
+
/** 两只被核的桶的角色表 —— 拒句、`snapshot_bucket_wait` 披露与 {@link ensureSnapshotBuckets} 都读这一张(缺一行 = 编译错)。 */
|
|
31
|
+
export declare const SNAPSHOT_BUCKET_ROLES: {
|
|
32
|
+
readonly session: {
|
|
33
|
+
readonly noun: "snapshot bucket";
|
|
34
|
+
readonly knob: "SESSION_SNAPSHOT_BUCKET";
|
|
35
|
+
readonly defaultName: "session-snapshots";
|
|
36
|
+
readonly consequence: "every snapshot-offload write and SendUserFile private-track request would answer 404 NoSuchBucket (and the orphan sweep would log one every minute, forever)";
|
|
37
|
+
readonly createHint: "; this version's sema-up scripts create it — re-run them on an existing sema-up deployment";
|
|
38
|
+
};
|
|
39
|
+
readonly workspace: {
|
|
40
|
+
readonly noun: "workspace snapshot bucket";
|
|
41
|
+
readonly knob: "WORKSPACE_SNAPSHOT_BUCKET";
|
|
42
|
+
readonly defaultName: "workspaces";
|
|
43
|
+
readonly consequence: "no workspace snapshot could be saved or restored (every run would start from an empty /workspace and every run-end save would fail on a bare 404), and no line would say the bucket is missing";
|
|
44
|
+
readonly createHint: "";
|
|
45
|
+
};
|
|
46
|
+
};
|
|
21
47
|
/** boot 期单次探针的上界(ms):黑洞端点(丢包不拒连)不许把起服拖到对象存储 IO 的 120s;到点 ⇒ `unknown`(证明不了缺桶 ⇒ 不拒、报一次)。 */
|
|
22
48
|
export declare const SNAPSHOT_BUCKET_PROBE_TIMEOUT_MS = 10000;
|
|
23
49
|
/** 缺桶的拒启句(唯一铸点)。 */
|
|
24
50
|
/** @param endpoint `MINIO_ENDPOINT` 的**显示安全形**(调用方过 {@link buildDisplaySafeUrl};本函数不碰原串)。 */
|
|
25
|
-
export declare function buildSnapshotBucketRefusal(bucket: string, endpoint: string, waitedMs: number): string;
|
|
51
|
+
export declare function buildSnapshotBucketRefusal(bucket: string, endpoint: string, waitedMs: number, role?: SnapshotBucketRole): string;
|
|
26
52
|
/** 探针到窗尾一次都没答过时,兜底口 detail 里的成因(证明不了缺桶的一形)。 */
|
|
27
53
|
export declare const SNAPSHOT_BUCKET_PROBE_PENDING = "probe still pending when the window ended";
|
|
28
54
|
/**
|
|
29
|
-
* boot
|
|
55
|
+
* boot 期核一次**一只**快照桶。缺桶 ⇒ 窗内重试、到窗拒启;证明不了 ⇒ 兜底口留痕、照常起服。
|
|
30
56
|
* @param waitMs `config.storeLockWaitMs`。`0` ⇒ 只核一次(缺就当场拒)。
|
|
57
|
+
* @param role 这只桶的角色({@link SNAPSHOT_BUCKET_ROLES}):决定拒句点名哪根旋钮;缺省 `session`。
|
|
31
58
|
*/
|
|
32
59
|
export declare function ensureSnapshotBucket(store: SnapshotBlobStoreConfig, waitMs: number, logger: {
|
|
33
60
|
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
34
|
-
}, clock?: StoreOpenWaitClock): Promise<void>;
|
|
61
|
+
}, clock?: StoreOpenWaitClock, role?: SnapshotBucketRole): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* boot 期核**这份配置要用的全部**快照桶(`boot/stores.ts`,排在打开任何店之前):角色表的每一行,配置里有这只桶才核 —— 会话快照桶 ⇐ 配了对象存储
|
|
64
|
+
* (`snapshotBlobStore`);工作区快照桶 ⇐ k8s 车道配了对象存储(`remoteExec.s3Snapshot`)。依角色表顺序逐只核,每只各有自己的 `STORE_LOCK_WAIT_MS` 窗
|
|
65
|
+
* (缺的桶才占窗;在场的桶一次探针就过),第一只缺的桶即拒启。
|
|
66
|
+
*/
|
|
67
|
+
export declare function ensureSnapshotBuckets(config: Pick<ServiceConfig, "snapshotBlobStore" | "remoteExec" | "storeLockWaitMs">, logger: {
|
|
68
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
69
|
+
}): Promise<void>;
|
|
35
70
|
export {};
|
|
36
71
|
//# sourceMappingURL=snapshot-bucket-presence.d.ts.map
|
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
import { buildDisplaySafeUrl } from "../trace/redact.js";
|
|
2
2
|
import { MinioBlobBackend } from "./blob-backend.js";
|
|
3
3
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
4
|
+
import { SESSION_SNAPSHOT_BUCKET_DEFAULT, WORKSPACE_SNAPSHOT_BUCKET_DEFAULT } from "../object-store-buckets.js";
|
|
4
5
|
import { isStoreOpenWindowExpired, openWithinWait } from "./store-open-wait.js";
|
|
6
|
+
export const SNAPSHOT_BUCKET_ROLES = {
|
|
7
|
+
session: {
|
|
8
|
+
noun: "snapshot bucket",
|
|
9
|
+
knob: "SESSION_SNAPSHOT_BUCKET",
|
|
10
|
+
defaultName: SESSION_SNAPSHOT_BUCKET_DEFAULT,
|
|
11
|
+
consequence: "every snapshot-offload write and SendUserFile private-track request would answer 404 NoSuchBucket (and the orphan sweep would log one every minute, forever)",
|
|
12
|
+
createHint: "; this version's sema-up scripts create it — re-run them on an existing sema-up deployment",
|
|
13
|
+
},
|
|
14
|
+
workspace: {
|
|
15
|
+
noun: "workspace snapshot bucket",
|
|
16
|
+
knob: "WORKSPACE_SNAPSHOT_BUCKET",
|
|
17
|
+
defaultName: WORKSPACE_SNAPSHOT_BUCKET_DEFAULT,
|
|
18
|
+
consequence: "no workspace snapshot could be saved or restored (every run would start from an empty /workspace and every run-end save would fail on a bare 404), and no line would say the bucket is missing",
|
|
19
|
+
createHint: "",
|
|
20
|
+
},
|
|
21
|
+
};
|
|
5
22
|
export const SNAPSHOT_BUCKET_PROBE_TIMEOUT_MS = 10_000;
|
|
6
23
|
class SnapshotBucketMissing extends Error {
|
|
7
24
|
constructor() {
|
|
8
25
|
super("snapshot bucket missing");
|
|
9
26
|
}
|
|
10
27
|
}
|
|
11
|
-
export function buildSnapshotBucketRefusal(bucket, endpoint, waitedMs) {
|
|
28
|
+
export function buildSnapshotBucketRefusal(bucket, endpoint, waitedMs, role = "session") {
|
|
29
|
+
const r = SNAPSHOT_BUCKET_ROLES[role];
|
|
12
30
|
const waited = waitedMs > 0 ? `after retrying for ${waitedMs}ms (STORE_LOCK_WAIT_MS)` : "on the first check (STORE_LOCK_WAIT_MS=0: no wait)";
|
|
13
|
-
return (`Object storage is configured (MINIO_ENDPOINT → ${endpoint}) but the
|
|
14
|
-
`the server never creates buckets, so
|
|
15
|
-
`Next: create the bucket (mc mb <alias>/${bucket}
|
|
31
|
+
return (`Object storage is configured (MINIO_ENDPOINT → ${endpoint}) but the ${r.noun} "${bucket}" (${r.knob}; default ${r.defaultName}) does not exist there ${waited} — refusing to boot: ` +
|
|
32
|
+
`the server never creates buckets, so ${r.consequence}. ` +
|
|
33
|
+
`Next: create the bucket (mc mb <alias>/${bucket}${r.createHint}), or set ${r.knob} to a bucket that exists.`);
|
|
16
34
|
}
|
|
17
35
|
export const SNAPSHOT_BUCKET_PROBE_PENDING = "probe still pending when the window ended";
|
|
18
|
-
export async function ensureSnapshotBucket(store, waitMs, logger, clock) {
|
|
36
|
+
export async function ensureSnapshotBucket(store, waitMs, logger, clock, role = "session") {
|
|
19
37
|
const probe = new MinioBlobBackend({ endpoint: store.endpoint, bucket: store.bucket, accessKey: store.accessKey, secretKey: store.secretKey, ...(store.region ? { region: store.region } : {}), ...(store.keyPrefix ? { keyPrefix: store.keyPrefix } : {}) });
|
|
20
38
|
let verdict;
|
|
21
39
|
try {
|
|
@@ -24,11 +42,11 @@ export async function ensureSnapshotBucket(store, waitMs, logger, clock) {
|
|
|
24
42
|
if (r.state === "missing")
|
|
25
43
|
throw new SnapshotBucketMissing();
|
|
26
44
|
return r;
|
|
27
|
-
}, waitMs, (w) => logger.warn("snapshot_bucket_wait", { bucket: store.bucket, endpoint: buildDisplaySafeUrl(store.endpoint), waitMs: w.waitMs, error: w.error }), clock);
|
|
45
|
+
}, waitMs, (w) => logger.warn("snapshot_bucket_wait", { role, bucket: store.bucket, endpoint: buildDisplaySafeUrl(store.endpoint), waitMs: w.waitMs, error: w.error }), clock);
|
|
28
46
|
}
|
|
29
47
|
catch (err) {
|
|
30
48
|
if (err instanceof SnapshotBucketMissing)
|
|
31
|
-
throw new Error(buildSnapshotBucketRefusal(store.bucket, buildDisplaySafeUrl(store.endpoint), waitMs), { cause: err });
|
|
49
|
+
throw new Error(buildSnapshotBucketRefusal(store.bucket, buildDisplaySafeUrl(store.endpoint), waitMs, role), { cause: err });
|
|
32
50
|
if (!isStoreOpenWindowExpired(err))
|
|
33
51
|
throw err;
|
|
34
52
|
verdict = { state: "unknown", detail: SNAPSHOT_BUCKET_PROBE_PENDING };
|
|
@@ -37,4 +55,14 @@ export async function ensureSnapshotBucket(store, waitMs, logger, clock) {
|
|
|
37
55
|
recordFailOpen("server.snapshot-bucket.presence-unproven", `bucket=${store.bucket} endpoint=${buildDisplaySafeUrl(store.endpoint)} ${verdict.detail}`);
|
|
38
56
|
}
|
|
39
57
|
}
|
|
58
|
+
export async function ensureSnapshotBuckets(config, logger) {
|
|
59
|
+
const wanted = [
|
|
60
|
+
["session", config.snapshotBlobStore],
|
|
61
|
+
["workspace", config.remoteExec?.provider === "k8s" ? config.remoteExec.s3Snapshot : undefined],
|
|
62
|
+
];
|
|
63
|
+
for (const [role, store] of wanted) {
|
|
64
|
+
if (store)
|
|
65
|
+
await ensureSnapshotBucket(store, config.storeLockWaitMs, logger, undefined, role);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
40
68
|
//# sourceMappingURL=snapshot-bucket-presence.js.map
|
|
@@ -129,11 +129,13 @@ export declare function rowTooLargeWallOf(dialect: SqlDialect, err: unknown): Ro
|
|
|
129
129
|
* (`ErrEntryTooLarge`,事务层的语句错;真库读数:同池上 8025 写之后的读照常 —— `checkpoint-row-size-db-integration` ②③④)。
|
|
130
130
|
* 断连形(`ECONNRESET` / `EPIPE` / `PROTOCOL_CONNECTION_LOST`)也不在表里:那是 mysql2 的**致命错误**,连接对象自己已经 `_removeFromPool`。
|
|
131
131
|
* PostgreSQL 没有对应面:node-pg 的 `pool.query` 对任何错误都丢弃那条 client。
|
|
132
|
+
* `statementRefusal`:这一行的错说的是**这一条语句**被拒(连接坏只是后果)—— 同一句话换一条连接重试仍被拒。{@link isConnectionFaultError} 读同一张表,认其余各行为连接类故障,唯独不认带这个标记的行。
|
|
132
133
|
* 新增一行 = 改表 + 在 `test/db-closed-connection-never-pooled.test.ts` 的码表里加一格。
|
|
133
134
|
*/
|
|
134
135
|
export declare const MYSQL_CONNECTION_UNUSABLE_ERRORS: ReadonlyArray<{
|
|
135
136
|
readonly errno: number;
|
|
136
137
|
readonly code: string;
|
|
138
|
+
readonly statementRefusal?: true;
|
|
137
139
|
}>;
|
|
138
140
|
/**
|
|
139
141
|
* 「抛出这个错误的那条连接还能不能回池」的单一判据:{@link MYSQL_CONNECTION_UNUSABLE_ERRORS} 里的错 ⇒ **不能** —— 持连接的一方(`sql-driver.ts` 的 `leaseOf`)
|
|
@@ -141,6 +143,64 @@ export declare const MYSQL_CONNECTION_UNUSABLE_ERRORS: ReadonlyArray<{
|
|
|
141
143
|
* 认不出的一律不是(连接照常归还;断连形由 mysql2 自己清出池)。只管 MySQL 协议的错误形:PG 的错误没有这两个键,恒 false。
|
|
142
144
|
*/
|
|
143
145
|
export declare function isConnectionUnusableError(err: unknown): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* **连接类故障**的识别表(闭集;逐行登记「为什么」)—— 「到库的那条连接坏了 / 库此刻不接」:与这一条语句的内容无关、什么都不改稍后再试可能成。
|
|
148
|
+
* 读者(整仓一只判官 `plugins/store-fault.ts` 的 `storeFaultClassOf`,三处同读):审批队列读口、文件历史店的折点(`permanence: "transient"`)、会话同步拉取口(`503` + `permanence: "transient"`)。
|
|
149
|
+
*
|
|
150
|
+
* 识别集 = 三张表的并集,**没有第四处**:
|
|
151
|
+
* · 本表(下):套接字层与协议层的断连 / 拒连 / 连接数满 / 库的关停与只读切换,按 `code` 登记(MySQL 协议的数字 errno 在场的并登 —— 同本文件其余谓词的姿势:`errno` 与 `code` 是同一件事的两种写法);
|
|
152
|
+
* · {@link MYSQL_CONNECTION_UNUSABLE_ERRORS}(「这条连接不许再回池」的 MySQL 协议错):整表读入,**唯独不认带 `statementRefusal` 的行**(1153:拒的是这一条语句,不是连接);
|
|
153
|
+
* · {@link CONNECTION_FAULT_DRIVER_SENTENCES}:驱动自己抛的**无 `code` 的裸 `Error`**(node-pg 的断连在这一形),按整句相等认。
|
|
154
|
+
*
|
|
155
|
+
* **不在识别集里**(认不出一律不是 —— 错判成连接类的代价是调用方被劝「再试」去撞一个注定失败的语句):
|
|
156
|
+
* · 语句级的错:唯一键冲突、CHECK 被拒、缺列、语法错、行 / 包超墙(1153 / 8025 / `54000`);缺表与权限拒也不是连接类 —— 它们是**部署错配类**(判官的另一个词,识别表 {@link DEPLOYMENT_FAULT_ERRORS},与本表互斥);
|
|
157
|
+
* · 死锁(引擎的仲裁产物,重试的前提在调用方的整事务重来,见 {@link isDeadlockError})与锁等待 / 语句超时(延迟信号,重试只把长等待乘上次数);
|
|
158
|
+
* · 认证类拒连(MySQL 1045、PG `28xxx`、`08004`:凭据 / 白名单要人去改)与协议违例(PG `08P01`、mysql2 `PROTOCOL_ERROR`:我方的码病);
|
|
159
|
+
* · TiDB 自己的忙 / 区域不可用(9001 / 9005 一族)—— 候真库逐码读数再登,今天不在表里(登记在案的未收项);
|
|
160
|
+
* · 池已被我方关闭(`Pool is closed.` / `Cannot use a pool after calling end on the pool`):那是本进程在收摊,不是库的事。
|
|
161
|
+
* 只看被抛出的**这一只**对象(不沿 `cause` 链往下找):店层把驱动错包进自己的类时,包它的那一层要不要继承连接类,归那一层的契约说,不在这里猜。
|
|
162
|
+
*/
|
|
163
|
+
export declare const CONNECTION_FAULT_ERRORS: ReadonlyArray<{
|
|
164
|
+
readonly code: string;
|
|
165
|
+
readonly errno?: number;
|
|
166
|
+
readonly why: string;
|
|
167
|
+
}>;
|
|
168
|
+
/**
|
|
169
|
+
* 驱动**自己抛的、不带 `code` 的裸 `Error`** 的整句 —— 连接类故障的第三种归因键。node-pg 的断连 / 池连接超时就是这一形(`new Error('Connection terminated unexpectedly')`,
|
|
170
|
+
* 没有任何结构化字段可读),mysql2 的「连接已关闭」同形。**整句相等**才认(不是 `includes`):一句夹着用户路径 / 数据的散文伪造不出驱动句;且只认 `code` 缺席的 `Error`。
|
|
171
|
+
* 每一行的 `source` 是它在驱动源码里的出处文件,`test/store-fault-judge.test.ts` 读已装驱动的源码核句子仍在(驱动升级改了句子 ⇒ 红,而不是判官悄悄静音)。
|
|
172
|
+
*/
|
|
173
|
+
export declare const CONNECTION_FAULT_DRIVER_SENTENCES: ReadonlyArray<{
|
|
174
|
+
readonly sentence: string;
|
|
175
|
+
readonly source: string;
|
|
176
|
+
readonly why: string;
|
|
177
|
+
}>;
|
|
178
|
+
/**
|
|
179
|
+
* 「这个错误**是**连接类故障吗」—— {@link CONNECTION_FAULT_ERRORS} 的唯一判据(识别集与排除项见该表头注)。纯谓词、不抛;认不出的一律不是。
|
|
180
|
+
* 归因键三种:`code`(字符串)、`errno`(数字)、无 `code` 的驱动裸 `Error` 的整句 —— 只读被抛出的这一只对象身上的结构化字段 / 整句,**不读散文里的在册词**。
|
|
181
|
+
*/
|
|
182
|
+
export declare function isConnectionFaultError(err: unknown): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* **部署错配类故障**的识别表(闭集;逐行登记「为什么」)—— 到库的连接是好的、语句也是对的,是这套库的形与本部署的要求不符:**重试注定失败**,修表 / 授权之前什么都不会变
|
|
185
|
+
* (与连接类互为反面:那一类是「什么都不改稍后再试可能成」)。读者:整仓一只判官 `plugins/store-fault.ts` 的 `storeFaultClassOf` 的 `deployment` 一词 ——
|
|
186
|
+
* 文件历史店的折点与会话同步拉取口(`503 state.file_history_unreadable`)据它判 `permanence: "terminal"`;审批队列读口并入「此刻读不出」的 503;各读者各自表态,**不在自己那里写码**。
|
|
187
|
+
*
|
|
188
|
+
* 识别集 = 本表一张,按归因键登记:`code`(字符串)与 MySQL 协议的数字 `errno`(两键是同一件事的两种写法,代理 / 序列化往返可能只留一个;同本文件其余谓词的姿势)。
|
|
189
|
+
* **与连接类互斥**:本表的任何一码都不在 {@link CONNECTION_FAULT_ERRORS} / {@link MYSQL_CONNECTION_UNUSABLE_ERRORS} 的识别集里,反之亦然(`test/store-fault-judge.test.ts` 钉两个方向)—— 同一只错只落一个词。
|
|
190
|
+
*
|
|
191
|
+
* **不在表里**(认不出一律不是;本批只收缺表与权限拒两形,其余是登记在案的未收项,不是「已裁定不判」):缺列(1054 / `42703`:升级前置断言 `assert*Schema` 族有自己的判据与出路)、
|
|
192
|
+
* 认证类拒连(MySQL 1045、PG `28xxx`:凭据 / 白名单)、库级与列级的权限拒(MySQL 1044 / 1143)、TiDB 自己的忙 / 区域不可用一族(同连接类表的登记)。
|
|
193
|
+
*/
|
|
194
|
+
export declare const DEPLOYMENT_FAULT_ERRORS: ReadonlyArray<{
|
|
195
|
+
readonly code: string;
|
|
196
|
+
readonly errno?: number;
|
|
197
|
+
readonly why: string;
|
|
198
|
+
}>;
|
|
199
|
+
/**
|
|
200
|
+
* 「这个错误**是**部署错配类故障吗」—— {@link DEPLOYMENT_FAULT_ERRORS} 的唯一判据(识别集与排除项见该表头注)。纯谓词、不抛;认不出的一律不是。
|
|
201
|
+
* 只读被抛出的**这一只**对象身上的结构化字段(`code` 字符串 / `errno` 数字),不沿 `cause` 链找、不读散文里的在册词。
|
|
202
|
+
*/
|
|
203
|
+
export declare function isDeploymentFaultError(err: unknown): boolean;
|
|
144
204
|
/**
|
|
145
205
|
* 检查点店的两笔写(续跑输入 `putCtx` / 停驻行 `put`)被引擎以「一行装不下」拒 ⇒ **这一只**带码错误(码 `store.row_too_large`),不以裸驱动错上抛
|
|
146
206
|
* (其余店写今天仍是各自的驱动错 —— 未收,登记在案)。
|
|
@@ -64,7 +64,7 @@ export function rowTooLargeWallOf(dialect, err) {
|
|
|
64
64
|
export const MYSQL_CONNECTION_UNUSABLE_ERRORS = [
|
|
65
65
|
{ errno: 1053, code: "ER_SERVER_SHUTDOWN" },
|
|
66
66
|
{ errno: 1152, code: "ER_ABORTING_CONNECTION" },
|
|
67
|
-
{ errno: 1153, code: "ER_NET_PACKET_TOO_LARGE" },
|
|
67
|
+
{ errno: 1153, code: "ER_NET_PACKET_TOO_LARGE", statementRefusal: true },
|
|
68
68
|
{ errno: 1158, code: "ER_NET_READ_ERROR" },
|
|
69
69
|
{ errno: 1159, code: "ER_NET_READ_INTERRUPTED" },
|
|
70
70
|
{ errno: 1160, code: "ER_NET_ERROR_ON_WRITE" },
|
|
@@ -82,6 +82,69 @@ export function isConnectionUnusableError(err) {
|
|
|
82
82
|
const code = keyOf(err, "code");
|
|
83
83
|
return (typeof errno === "number" && UNUSABLE_ERRNOS.has(errno)) || (typeof code === "string" && UNUSABLE_CODES.has(code));
|
|
84
84
|
}
|
|
85
|
+
export const CONNECTION_FAULT_ERRORS = [
|
|
86
|
+
{ code: "ECONNRESET", why: "对端重置了连接(库重启 / 代理断流 / 空闲连接被回收)" },
|
|
87
|
+
{ code: "ECONNREFUSED", why: "库此刻不接连接(进程没起 / 端口没开 / 滚动重启中)" },
|
|
88
|
+
{ code: "ECONNABORTED", why: "连接在建立或使用中被本机协议栈中止" },
|
|
89
|
+
{ code: "EPIPE", why: "写一条对端已经关掉的连接" },
|
|
90
|
+
{ code: "ETIMEDOUT", why: "连接 / 套接字超时(mysql2 的 `connectTimeout` 同答此码)" },
|
|
91
|
+
{ code: "EHOSTUNREACH", why: "到库的主机不可达(路由 / 节点切换)" },
|
|
92
|
+
{ code: "ENETUNREACH", why: "到库的网络不可达" },
|
|
93
|
+
{ code: "EAI_AGAIN", why: "DNS 临时失败(「稍后再试」是它自己的定义)" },
|
|
94
|
+
{ code: "PROTOCOL_CONNECTION_LOST", why: "mysql2:服务端关了连接(`Connection lost: The server closed the connection.`,带 `fatal: true`)" },
|
|
95
|
+
{ code: "ER_CON_COUNT_ERROR", errno: 1040, why: "连接数满(`Too many connections`)—— 别人放连接之后就好" },
|
|
96
|
+
{ code: "08000", why: "PG connection_exception" },
|
|
97
|
+
{ code: "08001", why: "PG sqlclient_unable_to_establish_sqlconnection" },
|
|
98
|
+
{ code: "08003", why: "PG connection_does_not_exist" },
|
|
99
|
+
{ code: "08006", why: "PG connection_failure" },
|
|
100
|
+
{ code: "57P01", why: "PG admin_shutdown(库被管理员关停 / 重启)" },
|
|
101
|
+
{ code: "57P02", why: "PG crash_shutdown" },
|
|
102
|
+
{ code: "57P03", why: "PG cannot_connect_now(启动 / 恢复中)" },
|
|
103
|
+
{ code: "53300", why: "PG too_many_connections" },
|
|
104
|
+
{ code: "25006", why: "PG read_only_sql_transaction(故障切换后落在只读节点)" },
|
|
105
|
+
];
|
|
106
|
+
export const CONNECTION_FAULT_DRIVER_SENTENCES = [
|
|
107
|
+
{ sentence: "Connection terminated unexpectedly", source: "pg/lib/client.js", why: "node-pg:连接在使用中被对端关掉" },
|
|
108
|
+
{ sentence: "Client has encountered a connection error and is not queryable", source: "pg/lib/client.js", why: "node-pg:这条 client 的连接已出错,后续语句直接拒" },
|
|
109
|
+
{ sentence: "Connection terminated due to connection timeout", source: "pg-pool/index.js", why: "node-pg 池:建连超过 `connectionTimeoutMillis`" },
|
|
110
|
+
{ sentence: "timeout exceeded when trying to connect", source: "pg-pool/index.js", why: "node-pg 池:排队等连接超过 `connectionTimeoutMillis`" },
|
|
111
|
+
{ sentence: "Can't add new command when connection is in closed state", source: "mysql2/lib/base/connection.js", why: "mysql2:往一条已被对端关掉的连接上发语句(带 `fatal: true`、无 `code`)" },
|
|
112
|
+
];
|
|
113
|
+
const CONNECTION_FAULT_CODES = new Set([
|
|
114
|
+
...CONNECTION_FAULT_ERRORS.map((r) => r.code),
|
|
115
|
+
...MYSQL_CONNECTION_UNUSABLE_ERRORS.filter((r) => r.statementRefusal !== true).map((r) => r.code),
|
|
116
|
+
]);
|
|
117
|
+
const CONNECTION_FAULT_ERRNOS = new Set([
|
|
118
|
+
...CONNECTION_FAULT_ERRORS.flatMap((r) => (r.errno === undefined ? [] : [r.errno])),
|
|
119
|
+
...MYSQL_CONNECTION_UNUSABLE_ERRORS.filter((r) => r.statementRefusal !== true).map((r) => r.errno),
|
|
120
|
+
]);
|
|
121
|
+
const CONNECTION_FAULT_SENTENCES = new Set(CONNECTION_FAULT_DRIVER_SENTENCES.map((r) => r.sentence));
|
|
122
|
+
export function isConnectionFaultError(err) {
|
|
123
|
+
if (err === null || typeof err !== "object")
|
|
124
|
+
return false;
|
|
125
|
+
const code = keyOf(err, "code");
|
|
126
|
+
const errno = keyOf(err, "errno");
|
|
127
|
+
if (typeof code === "string" && CONNECTION_FAULT_CODES.has(code))
|
|
128
|
+
return true;
|
|
129
|
+
if (typeof errno === "number" && CONNECTION_FAULT_ERRNOS.has(errno))
|
|
130
|
+
return true;
|
|
131
|
+
return err instanceof Error && code === undefined && CONNECTION_FAULT_SENTENCES.has(err.message);
|
|
132
|
+
}
|
|
133
|
+
export const DEPLOYMENT_FAULT_ERRORS = [
|
|
134
|
+
{ code: "42P01", why: "PG undefined_table(缺表:库没按本版的表结构建好 / 迁移没跑 —— 重试不会让表长出来)" },
|
|
135
|
+
{ code: "42501", why: "PG insufficient_privilege(权限拒:库账号缺这张表 / 这类语句的授权 —— 授权之前重试注定失败)" },
|
|
136
|
+
{ code: "ER_NO_SUCH_TABLE", errno: 1146, why: "MySQL 协议(MySQL / MariaDB / TiDB)缺表:同 PG `42P01`" },
|
|
137
|
+
{ code: "ER_TABLEACCESS_DENIED_ERROR", errno: 1142, why: "MySQL 协议表级权限拒:库账号对这张表缺这类语句的授权,同 PG `42501`" },
|
|
138
|
+
];
|
|
139
|
+
const DEPLOYMENT_FAULT_CODES = new Set(DEPLOYMENT_FAULT_ERRORS.map((r) => r.code));
|
|
140
|
+
const DEPLOYMENT_FAULT_ERRNOS = new Set(DEPLOYMENT_FAULT_ERRORS.flatMap((r) => (r.errno === undefined ? [] : [r.errno])));
|
|
141
|
+
export function isDeploymentFaultError(err) {
|
|
142
|
+
if (err === null || typeof err !== "object")
|
|
143
|
+
return false;
|
|
144
|
+
const code = keyOf(err, "code");
|
|
145
|
+
const errno = keyOf(err, "errno");
|
|
146
|
+
return (typeof code === "string" && DEPLOYMENT_FAULT_CODES.has(code)) || (typeof errno === "number" && DEPLOYMENT_FAULT_ERRNOS.has(errno));
|
|
147
|
+
}
|
|
85
148
|
export class StoreRowTooLargeError extends Error {
|
|
86
149
|
row;
|
|
87
150
|
wall;
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* code should import from this file.
|
|
20
20
|
*/
|
|
21
21
|
import type { PeerSessionTempo, TaskResult, TaskStatus } from "@sema-agent/core";
|
|
22
|
+
import type { RunIdentity } from "../identity-source.js";
|
|
22
23
|
/**
|
|
23
24
|
* **File 店族的 boot 期拒启基类**([ref];把 `RunStoreStrictHydrateError` 的那条论证升成一条族规)。
|
|
24
25
|
*
|
|
@@ -212,8 +213,13 @@ export interface RunRecord {
|
|
|
212
213
|
* (MCP façade / portal). null when the caller didn't supply one. */
|
|
213
214
|
jobId: string | null;
|
|
214
215
|
/** Which SYSTEM's credential submitted the run (oa/cc-mcp/portal…), derived at the auth gate — never
|
|
215
|
-
* self-declared. null = legacy single-token deployment (authenticated, no system identity).
|
|
216
|
+
* self-declared. null = legacy single-token deployment (authenticated, no system identity).
|
|
217
|
+
* **归因用,不是认证来源**:任何凭据判都不读它(认证来源 = {@link RunRecord.identity});系统名是运维写的任意串,`"sso"` 也只是归因值。 */
|
|
216
218
|
source: string | null;
|
|
219
|
+
/** 🔴 **run 行身份元组**(三列 `identity_source` / `identity_scope` / `identity_principal_inc`):受理时在凭据门上铸、建行时写入、之后只读 ——
|
|
220
|
+
* 续跑认领 / 挂起 / 终局 / 重启都不改它,没有按当前状态重算的读者。`null` = 这一行没有「属主在受理时验过的凭据」的记录(三列之前的行 /
|
|
221
|
+
* 无鉴权写面的受理 / 别人替属主按下的那一跑),一律不合格。律与形的唯一属主 = `identity-source.ts`。不上 wire(run 读面是显式投影,不带它)。 */
|
|
222
|
+
identity: RunIdentity | null;
|
|
217
223
|
/** The replica currently HOLDING this run — written at createRun and TAKEN OVER by markResuming ([ref]: a
|
|
218
224
|
* cross-replica resume moves the seat, so a reader comparing it with its own instanceId is asking "is the
|
|
219
225
|
* live leg here NOW", not "who minted the row"). Lets a preempt/cancel/steer handler tell "this run is on
|
|
@@ -263,6 +269,9 @@ export interface RunIdemBinding {
|
|
|
263
269
|
export interface NewRunMeta {
|
|
264
270
|
jobId?: string | null;
|
|
265
271
|
source?: string | null;
|
|
272
|
+
/** 这一行的身份元组(`RunRecord.identity` 的写口)。缺席 = `null`(没有记录;不合格)。`src/http/**` 的每个建行点都显式写它(机器门
|
|
273
|
+
* `test/run-identity-tuple.test.ts`),取值只经凭据门铸下的那一份(`principal-gate.ts` 的 `runIdentityOf`)。 */
|
|
274
|
+
identity?: RunIdentity | null;
|
|
266
275
|
objectivePreview?: string | null;
|
|
267
276
|
idem?: RunIdemBinding | null;
|
|
268
277
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type StoreFaultClass = "corrupt" | "connection" | "deployment" | "unclassified";
|
|
2
|
+
/** 判官本体。纯函数、不抛;只看被抛出的这一只对象(不沿 `cause` 链找)。两张识别表互斥,判序不影响结果(`test/store-fault-judge.test.ts` 钉互斥)。 */
|
|
3
|
+
export declare function storeFaultClassOf(err: unknown): StoreFaultClass;
|
|
4
|
+
//# sourceMappingURL=store-fault.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { isConnectionFaultError, isDeploymentFaultError } from "./sql-errors.js";
|
|
2
|
+
import { StoreCorruptError } from "./sql-json-column.js";
|
|
3
|
+
export function storeFaultClassOf(err) {
|
|
4
|
+
if (err instanceof StoreCorruptError)
|
|
5
|
+
return "corrupt";
|
|
6
|
+
if (isConnectionFaultError(err))
|
|
7
|
+
return "connection";
|
|
8
|
+
return isDeploymentFaultError(err) ? "deployment" : "unclassified";
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=store-fault.js.map
|
|
@@ -7,6 +7,7 @@ import { TIDB_ADOPTION_LOG_STATEMENTS } from "./adoption-log-sql.js";
|
|
|
7
7
|
import { TIDB_PERMISSION_RULE_STATEMENTS } from "./permission-rule-store-sql.js";
|
|
8
8
|
import { TIDB_RETENTION_STATEMENTS } from "./retention-store-sql.js";
|
|
9
9
|
import { SESSION_META_INCARNATION_COLUMN } from "../session-incarnation.js";
|
|
10
|
+
import { RUN_IDENTITY_COLUMNS } from "../identity-source.js";
|
|
10
11
|
import { SESSION_RULES_COLUMN } from "./session-rules-column.js";
|
|
11
12
|
import { SESSION_TITLE_MAX_CHARS } from "../session-row.js";
|
|
12
13
|
import { PRINCIPAL_MAX_UTF8_BYTES } from "../security.js";
|
|
@@ -86,7 +87,7 @@ export const SCHEMA_STATEMENTS = [
|
|
|
86
87
|
placed_at DATETIME(3) NULL,
|
|
87
88
|
-- S-790 / S-506(design/330 §2):会话化身 —— 同一个会话 id 上**这一位住客**的不透明令牌。铸点只在新建行(register /
|
|
88
89
|
-- 无 id 创建 / fork / 导入新行),删除随行消失,同 id 重登记铸新的;'' = 没铸过,读口响亮拒。列定义的唯一属主 =
|
|
89
|
-
-- src/session-incarnation.ts 的 SESSION_META_INCARNATION_COLUMN(
|
|
90
|
+
-- src/session-incarnation.ts 的 SESSION_META_INCARNATION_COLUMN(存量库缺这一列 ⇒ 起服列册对账拒启,拒句里的 ALTER 由本建表语句生成)。
|
|
90
91
|
-- 不上 wire、不进模型上下文、日志只打前 8 位。
|
|
91
92
|
${SESSION_META_INCARNATION_COLUMN.tidb},
|
|
92
93
|
PRIMARY KEY (session_id),
|
|
@@ -155,6 +156,12 @@ export const SCHEMA_STATEMENTS = [
|
|
|
155
156
|
-- source (credential-derived system identity, clay 2026-06-12): which door submitted the run
|
|
156
157
|
-- (oa / cc-mcp / portal).
|
|
157
158
|
source VARCHAR(64) NULL,
|
|
159
|
+
-- identity_source / identity_scope / identity_principal_inc: the run's identity tuple, minted ONCE at the credential gate when the
|
|
160
|
+
-- run is accepted and never rewritten (no UPDATE touches them). NULL = no record of a credential the owner presented at acceptance
|
|
161
|
+
-- (rows from before these columns, unauthenticated-write acceptance, a run someone else accepted on the owner's behalf) ⇒ not
|
|
162
|
+
-- qualified. Column definitions' single owner = src/identity-source.ts RUN_IDENTITY_COLUMNS (the boot column probe derives its
|
|
163
|
+
-- ALTER suggestion from this statement). \`source\` above is attribution only, never an authentication source.
|
|
164
|
+
${RUN_IDENTITY_COLUMNS.tidb},
|
|
158
165
|
-- objective_preview: the list's "what is this task" glance — redacted + truncated at write.
|
|
159
166
|
objective_preview VARCHAR(160) NULL,
|
|
160
167
|
-- cancel_requested: durable cross-replica CANCEL flag. The running instance's heartbeat tick honors it
|
|
@@ -11,7 +11,7 @@ import { CLAIM_OWNERLESS_PLACED_SQL, claimOwnerlessParams, createPlacedSessionRo
|
|
|
11
11
|
import { createLogger } from "../observability/logger.js";
|
|
12
12
|
import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SQL_SESSION_PLACEMENTS, } from "./session-placement.js";
|
|
13
13
|
import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
|
|
14
|
-
import { assertFirstOwnerWritable, forkOwnerOf } from "./session-ownership.js";
|
|
14
|
+
import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue } from "./session-ownership.js";
|
|
15
15
|
import { forkCopyOf, forkPointOf } from "./session-fork.js";
|
|
16
16
|
export class TiDBSessionStore {
|
|
17
17
|
pool;
|
|
@@ -58,7 +58,7 @@ export class TiDBSessionStore {
|
|
|
58
58
|
return { session: new StoredSession(storage), sessionId };
|
|
59
59
|
}
|
|
60
60
|
async register(sessionId, owner) {
|
|
61
|
-
if (
|
|
61
|
+
if (!isSessionOwnerValue(owner)) {
|
|
62
62
|
if ((await this.headOf(sessionId)) !== undefined)
|
|
63
63
|
return;
|
|
64
64
|
assertFirstOwnerWritable(sessionId, owner);
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
* 验证搜索配对不对。挂哪儿(boot 期一次性 warn / `/health` 子字段 / 显式端点)未定 ——
|
|
55
55
|
* 注意 boot 期探活会给每次启动加一次出网,多租/离线部署要能关。
|
|
56
56
|
*/
|
|
57
|
-
import { type UrlCredentialFacts } from "../url-credential.js";
|
|
57
|
+
import { type UrlCredentialFacts } from "../url-credential-refusal.js";
|
|
58
58
|
/**
|
|
59
59
|
* Search providers this backend speaks —— **词表的唯一属主**(运行期与类型同一份)。
|
|
60
60
|
* Absent `WEB_SEARCH_PROVIDER` ⇒ no backend ⇒ WebSearch is not assembled.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { validateHeaderValue } from "node:http";
|
|
2
2
|
import { createSearxngSearchBackend } from "@sema-agent/core";
|
|
3
|
-
import { isHierarchicalUrl } from "@sema-agent/settings-schema";
|
|
3
|
+
import { findUrlCredential, findUrlUserinfo, isHierarchicalUrl } from "@sema-agent/settings-schema";
|
|
4
4
|
import { redactSecrets } from "../trace/redact.js";
|
|
5
|
-
import { buildRequestUrlCredentialRefusal,
|
|
5
|
+
import { buildRequestUrlCredentialRefusal, urlCredentialFacts } from "../url-credential-refusal.js";
|
|
6
6
|
export const WEB_SEARCH_PROVIDERS = ["brave", "tavily", "searxng"];
|
|
7
7
|
function isWebSearchProvider(word) {
|
|
8
8
|
return WEB_SEARCH_PROVIDERS.includes(word);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **请求体里「声明列表」(`mcpServers` · `a2aPeers`)这一类受理座的拒绝骨架** —— 列表级 / 条目级 / 键值级的拒形、拒句尾、撞帽机读位,在这里铸一处。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么在这里
|
|
5
|
+
* 两张列表是同一个问题的两个实例:调用方随请求带一份「我要挂这些外部服务」的声明,受理口要回答「这份声明能不能原样用」。答案的**形**一样 ——
|
|
6
|
+
* ① **列表**:在场却不是数组 / 超过挂载上限 / 某一条不是对象 ⇒ `400 request.field_invalid`(整份拒而不是挂前 N 条 / 摘掉读不出的那条:一份被悄悄截短的声明,在发送方那里读起来是「生效了」);
|
|
7
|
+
* 超上限用撞帽的唯一铸点({@link limitRefusal}),拒体带机读位一组键(`field` · `reason:"over_cap"` · `count` · `cap`);
|
|
8
|
+
* ② **键名**:条目层的词表外键 ⇒ `400 request.body_shape` + `unknownKeys`(句子由各座自己写 —— 受理键表与嵌套层是座位自己的事);
|
|
9
|
+
* ③ **键值**:每个受理键一只判官 ⇒ `400 request.field_invalid`,句子 `<坐标>.<键路径> (<subject> "<名>") is not a usable declaration — <判官原因句>. …`,不回显值;
|
|
10
|
+
* 判官答「URL 带凭据」时拒体另带机读位 `field` / `reason`({@link urlCredentialFacts})。
|
|
11
|
+
* 第一处坏形即答:先列表、后条目;条目里先键名、后键值。各座只写**自己的**两件事:受理键表 + 键值判官表(以及它自己的词表外键句)。拒形、句尾、撞帽句不在座位文件里手拼 ——
|
|
12
|
+
* 两份手拼的拒形迟早分岔(`a2aPeers` 上线时就是缺了这一整套,坏形换回 200 + 运维 warn,调用方以为声明生效了)。
|
|
13
|
+
*
|
|
14
|
+
* ## 射程
|
|
15
|
+
* 纯值域、与租户车道无关;只在 fresh 提交腿上问(`boot/resolve-spec.ts` 的 `opts.leg`):重放存量 body 的腿走宽容读器,一个 4xx 打在 resume 上会永久砖死一条 parked 任务。
|
|
16
|
+
*/
|
|
17
|
+
import { type UrlCredential } from "@sema-agent/settings-schema";
|
|
18
|
+
import { type LimitFacts } from "./limit-refusal.js";
|
|
19
|
+
import { type UrlCredentialFacts } from "./url-credential-refusal.js";
|
|
20
|
+
/** 一座声明列表的名字与上限、以及拒句里要用的称呼(座位的常量,铸点只读)。 */
|
|
21
|
+
export interface DeclarationSeat {
|
|
22
|
+
/** 请求体里这张列表的键,也是机读坐标的根(`mcpServers` | `a2aPeers`)。 */
|
|
23
|
+
readonly list: string;
|
|
24
|
+
/** 挂载上限(条数;超过 ⇒ 撞帽拒)。 */
|
|
25
|
+
readonly cap: number;
|
|
26
|
+
/** 一条声明所声明的东西(拒句里的单数名词:`server` | `peer`)。 */
|
|
27
|
+
readonly subject: string;
|
|
28
|
+
/** 「一条声明」的整称(`an MCP server declaration`)。 */
|
|
29
|
+
readonly declaration: string;
|
|
30
|
+
/** 「多条声明」的整称(`MCP server declarations`)。 */
|
|
31
|
+
readonly declarations: string;
|
|
32
|
+
}
|
|
33
|
+
/** 一次拒绝:码 + 句 + 词表外的键名表(只在闭集那一形上在场)+ 机读位(撞帽 / URL 带凭据两形上在场)。受理口(同步 400)与探测口(`POST …/probe`)原样转述同一份。 */
|
|
34
|
+
export interface DeclarationRefusal {
|
|
35
|
+
readonly errorCode: "request.body_shape" | "request.field_invalid";
|
|
36
|
+
readonly message: string;
|
|
37
|
+
/** 词表外的键,全路径形(已清洗 + 截长 + 排序 + 封顶)。 */
|
|
38
|
+
readonly unknownKeys?: readonly string[];
|
|
39
|
+
/** 撞帽:`field` · `reason:"over_cap"` · `count` · `cap`;URL 带凭据:`field` · `reason`(判官的拒因词)。 */
|
|
40
|
+
readonly facts?: UrlCredentialFacts | LimitFacts;
|
|
41
|
+
}
|
|
42
|
+
/** 一处键值坏形:键路径(相对条目)+ 判官的原因句;答「URL 带凭据」时另带判官的答案(⇒ 拒体带机读位)。 */
|
|
43
|
+
export interface DeclarationKeyIssue {
|
|
44
|
+
readonly path: string;
|
|
45
|
+
readonly reason: string;
|
|
46
|
+
readonly credential?: UrlCredential;
|
|
47
|
+
}
|
|
48
|
+
/** 受理键 → 判官。`at` 是这一条的体内坐标(`a2aPeers[2]`),座位需要写绝对坐标的出路句时用。缺席(`undefined`)恒合法 = 真「没声明」;在场的 `null` 不是缺席。 */
|
|
49
|
+
export type DeclarationKeyJudge<E extends Record<string, unknown> = Record<string, unknown>> = (entry: E, at: string) => DeclarationKeyIssue | undefined;
|
|
50
|
+
/** 读不出名字的条目用的占位(非空:一条连名字都读不出的条目仍然要被说出来)。 */
|
|
51
|
+
export declare const MALFORMED_NAME = "<malformed>";
|
|
52
|
+
/** 条目的**申报名**(拿不到名字时的占位)—— 拒句、丢弃点、通告同读一只。 */
|
|
53
|
+
export declare function declaredName(e: unknown): string;
|
|
54
|
+
/** `name` 键的判官(两座同读:申报名线长界与首尾空白的唯一判官在契约包 {@link isUsableWireName})。 */
|
|
55
|
+
export declare function declarationNameIssue(entry: Record<string, unknown>): DeclarationKeyIssue | undefined;
|
|
56
|
+
/** 列表级拒句的收尾句:说清「为什么整份被拒、而不是只按读得出的那部分办」与出路(空列表 = 什么都不声明)。 */
|
|
57
|
+
export declare const LIST_REFUSAL_TAIL: string;
|
|
58
|
+
/** 列表级的一处坏形 → 拒(码恒 `request.field_invalid`)。`head` 只说位置与形的**种类**,不回显值。 */
|
|
59
|
+
export declare function declarationListRefusal(head: string): DeclarationRefusal;
|
|
60
|
+
/**
|
|
61
|
+
* 一处键值坏形**怎么被说出来**:体内坐标(`<条目坐标>.<键路径>`)+ 判官的原因句(只剥控制与双向字符,不回显值)。受理口的拒句与读器的丢弃因(请求腿运维 warn 的 `field` / `reason`)读**这一只** ——
|
|
62
|
+
* 原因只来自判官表,组句只在这里(两边各写一份,迟早分岔)。
|
|
63
|
+
*/
|
|
64
|
+
export declare function declarationIssueFacts(at: string, issue: DeclarationKeyIssue): {
|
|
65
|
+
readonly field: string;
|
|
66
|
+
readonly reason: string;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* 列表条数撞帽**怎么被说出来**:坐标 = 列表键(`a2aPeers`)+ 撞帽铸点({@link limitRefusal})的头一句(`<列表> has N entries — at most M are accepted`)。受理口的撞帽拒句以同一句开头(接拒句尾),
|
|
70
|
+
* 读器的丢弃因读这一只 —— 与 {@link declarationIssueFacts} 同族:`reason` 只有一种内容 = 句子;机读词(`over_cap`)留在拒体的 `facts`,不进日志的 `reason`。
|
|
71
|
+
*/
|
|
72
|
+
export declare function declarationOverCapFacts(seat: DeclarationSeat, count: number): {
|
|
73
|
+
readonly field: string;
|
|
74
|
+
readonly reason: string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* 一条条目不是对象(`null` / 原始值 / 数组)的头一句:`<条目坐标> is <值的种类>, not <整称> (an object)`;是对象 ⇒ `undefined`。列表级拒({@link requestDeclarationRefusal})与读器的丢弃因读同一只,
|
|
78
|
+
* 「什么算对象」只在这里判。
|
|
79
|
+
*/
|
|
80
|
+
export declare function declarationEntryNotObject(seat: DeclarationSeat, entry: unknown, at: string): string | undefined;
|
|
81
|
+
/** 一处键值坏形 → 拒。**所有键同一个形**;条目名过唯一的回显清洗,读不出名字的条目用占位,坐标与原因句由 {@link declarationIssueFacts} 铸。 */
|
|
82
|
+
export declare function declarationFieldRefusal(seat: DeclarationSeat, entry: Record<string, unknown>, at: string, issue: DeclarationKeyIssue): DeclarationRefusal;
|
|
83
|
+
/** 一条条目的键值判官表按受理键序问一遍,第一处坏形即返回。 */
|
|
84
|
+
export declare function judgeDeclarationKeys<K extends string, E extends Record<string, unknown>>(seat: DeclarationSeat, entry: E, at: string, keys: readonly K[], judges: {
|
|
85
|
+
readonly [P in K]: DeclarationKeyJudge<E>;
|
|
86
|
+
}): DeclarationRefusal | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* 一张声明列表「能不能原样用」的回答者骨架:列表级(缺席 / `null` ⇒ 没声明;不是数组 / 超上限 / 条目不是对象)在这里,条目级交座位的 `judgeEntry`(词表外的键 → 键值)。
|
|
89
|
+
* `undefined` = 能原样用(或没声明;各门自己定缺席的意思)。
|
|
90
|
+
*/
|
|
91
|
+
export declare function requestDeclarationRefusal(seat: DeclarationSeat, list: unknown, judgeEntry: (entry: Record<string, unknown>, at: string) => DeclarationRefusal | undefined): DeclarationRefusal | undefined;
|
|
92
|
+
/** 受理口的转述:有答案 ⇒ 同步 400(码 / 句 / `unknownKeys` / 机读位原样);没有 ⇒ 什么也不做。 */
|
|
93
|
+
export declare function throwDeclarationRefusal(refusal: DeclarationRefusal | undefined): void;
|
|
94
|
+
//# sourceMappingURL=request-declaration-refusal.d.ts.map
|