@sema-agent/settings-schema 10.1.1 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +469 -1
- package/README.md +144 -66
- package/bin/settings-schema.mjs +10 -12
- package/conformance/VERSION +1 -1
- package/conformance/baseline.json +3 -1
- package/conformance/cases/c01-entry-salvage-then-typo/expected.json +3 -1
- package/conformance/cases/c02-hosts-clamp-stacked/expected.json +12 -5
- package/conformance/cases/c02-hosts-clamp-stacked/meta.json +1 -1
- package/conformance/cases/c03-literal-dotted-top-key/expected.json +22 -2
- package/conformance/cases/c04-all-bad-collection-not-salvaged/expected.json +3 -1
- package/conformance/cases/c06-legacy-governance-lift/expected.json +11 -101
- package/conformance/cases/c06-legacy-governance-lift/meta.json +1 -1
- package/conformance/cases/c07-hooks-is-not-a-config-domain/expected.json +3 -1
- package/conformance/cases/c08-limits-unknown-carried-64/expected.json +203 -2
- package/conformance/cases/c09-limits-unknown-carried-65/expected.json +204 -2
- package/conformance/cases/c10-record-key-secret-redacted-twice/expected.json +3 -1
- package/conformance/cases/c11-unicode-unknown-keys/expected.json +20 -2
- package/conformance/cases/c12-prototype-name-top-keys/expected.json +3 -1
- package/conformance/cases/c13-models-field-null/expected.json +3 -1
- package/conformance/cases/c14-models-field-empty-array/expected.json +6 -1
- package/conformance/cases/c15-models-field-number/expected.json +3 -1
- package/conformance/cases/c16-deep-nesting-205/expected.json +14 -2
- package/conformance/cases/c17-empty-domain-file/expected.json +3 -1
- package/conformance/cases/c18-wire-envelope-not-an-object/expected.json +3 -1
- package/conformance/cases/c19-wire-envelope-no-version/expected.json +3 -1
- package/conformance/cases/c20-governance-absent-lifts/expected.json +11 -86
- package/conformance/cases/c20-governance-absent-lifts/meta.json +1 -1
- package/conformance/cases/c21-governance-empty-object-wins/expected.json +6 -5
- package/conformance/cases/c21-governance-empty-object-wins/input.json +1 -4
- package/conformance/cases/c21-governance-empty-object-wins/meta.json +1 -1
- package/conformance/cases/c23-mixed-validity-models/expected.json +3 -1
- package/conformance/cases/c24-valid-entries-with-unknown-keys/expected.json +22 -2
- package/conformance/cases/c25-published-roundtrip-zero-warnings/expected.json +6 -1
- package/conformance/cases/c26-hosts-array-1024/expected.json +3 -1
- package/conformance/cases/c27-hosts-array-1025/expected.json +6 -7175
- package/conformance/cases/c27-hosts-array-1025/meta.json +1 -1
- package/conformance/cases/c28-large-document-over-1mb/expected.json +14 -2
- package/conformance/cases/c29-salvage-then-domain-refine-fails/expected.json +3 -1
- package/conformance/cases/c30-version-triple-a-first/expected.json +6 -1
- package/conformance/cases/c31-version-triple-b-changed/expected.json +6 -1
- package/conformance/cases/c32-version-triple-a-restored/expected.json +6 -1
- package/conformance/cases/c33-limits-nested-carried-key/expected.json +18 -2
- package/conformance/cases/c34-skills-content-vs-manifest/expected.json +19 -3
- package/conformance/cases/c35-portable-domain-file-is-null/expected.json +3 -1
- package/conformance/cases/c36-skills-entry-without-content/expected.json +6 -2
- package/conformance/cases/c37-skills-field-null/expected.json +6 -2
- package/conformance/cases/c38-remote-exec-is-a-non-domain-file/expected.json +3 -1
- package/conformance/cases/c39-hidden-file-in-config-d/expected.json +3 -1
- package/conformance/cases/c40-s712-identity-only-what-reads-out/expected.json +11 -4
- package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +9 -3
- package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +39 -2
- package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +4 -1
- package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +3 -1
- package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +19 -10
- package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/meta.json +1 -1
- package/conformance/cases/{c46-s811-host-name-url-userinfo-characterization → c46-s811-host-name-url-userinfo-withheld}/expected.json +12 -5
- package/conformance/cases/c46-s811-host-name-url-userinfo-withheld/meta.json +9 -0
- package/conformance/cases/{c47-s811-roles-key-url-userinfo-characterization → c47-s811-roles-key-url-userinfo-withheld}/expected.json +23 -3
- package/conformance/cases/{c47-s811-roles-key-url-userinfo-characterization → c47-s811-roles-key-url-userinfo-withheld}/input.json +6 -0
- package/conformance/cases/c47-s811-roles-key-url-userinfo-withheld/meta.json +10 -0
- package/conformance/cases/c48-s935-model-headers-transported/expected.json +6 -1
- package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +3 -1
- package/conformance/cases/c50-s784-lane-host-legal/expected.json +3 -1
- package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +3 -1
- package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +3 -1
- package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +3 -1
- package/conformance/cases/c54-s784-lane-adb-legal/expected.json +3 -1
- package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +3 -1
- package/conformance/cases/c56-s784-lane-device-legal/expected.json +3 -1
- package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +3 -1
- package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +3 -1
- package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +3 -1
- package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +3 -1
- package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +3 -1
- package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +3 -1
- package/conformance/cases/c63-s784-host-two-replicas/expected.json +3 -1
- package/conformance/cases/c64-s784-multitenant-local-store/expected.json +3 -1
- package/conformance/cases/c65-s784-pg-without-db/expected.json +3 -1
- package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +3 -1
- package/conformance/cases/c67-s784-retired-field-loud/expected.json +3 -1
- package/conformance/cases/c67-s784-retired-field-loud/meta.json +1 -1
- package/conformance/cases/c68-s784-device-local-store/expected.json +3 -1
- package/conformance/cases/c69-deep-encoded-token-past-every-reading/expected.json +3 -1
- package/conformance/cases/c70-rewritten-token-in-front-of-encoded-pem-header/expected.json +3 -1
- package/conformance/cases/c71-one-scanner-write-reads-display-forms/expected.json +119 -0
- package/conformance/cases/c71-one-scanner-write-reads-display-forms/input.json +24 -0
- package/conformance/cases/c71-one-scanner-write-reads-display-forms/meta.json +10 -0
- package/conformance/cases/c72-dangling-reference-write-seat-refuses/expected.json +111 -0
- package/conformance/cases/c72-dangling-reference-write-seat-refuses/input.json +26 -0
- package/conformance/cases/c72-dangling-reference-write-seat-refuses/meta.json +6 -0
- package/conformance/cases/cc01-mcp-toolfaces-non-object/expected.json +3 -1
- package/conformance/cases/cc02-mcp-source-key-is-transported/expected.json +3 -1
- package/conformance/cases/cc03-model-compat-unknown-subkey/expected.json +6 -1
- package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +11 -1
- package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +11 -1
- package/conformance/cases/cli03-credential-pasted-into-unknown-key/expected.json +16 -2
- package/conformance/cases/cli04-env-value-with-newline/expected.json +3 -1
- package/conformance/cases/cli05-long-and-prefix-colliding-names/expected.json +6 -1
- package/conformance/cases/t01-absent-domain-key-not-null/expected.json +3 -1
- package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +29 -2
- package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +3 -1
- package/conformance/cases/z01-every-domain-empty-object/expected.json +8 -4
- package/conformance/cases/z01-every-domain-empty-object/input.json +0 -1
- package/conformance/cases/z01-every-domain-empty-object/meta.json +1 -1
- package/conformance/cases/z02-every-domain-unknown-key/expected.json +163 -23
- package/conformance/cases/z02-every-domain-unknown-key/input.json +0 -3
- package/conformance/cases/z02-every-domain-unknown-key/meta.json +1 -1
- package/dist/api/auth-bridge.d.ts +51 -181
- package/dist/api/auth-bridge.js +58 -33
- package/dist/api/auth.d.ts +37 -146
- package/dist/api/auth.js +16 -26
- package/dist/api/feedback.d.ts +87 -0
- package/dist/api/feedback.js +66 -0
- package/dist/api/pull-token.d.ts +32 -0
- package/dist/api/pull-token.js +88 -0
- package/dist/api/scopes.d.ts +82 -174
- package/dist/api/scopes.js +23 -18
- package/dist/atomic-write.d.ts +14 -0
- package/dist/atomic-write.js +60 -0
- package/dist/bundle.d.ts +15 -7
- package/dist/bundle.js +28 -27
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +149 -80
- package/dist/config-d-file.d.ts +14 -0
- package/dist/config-d-file.js +67 -0
- package/dist/config-fns.d.ts +87 -186
- package/dist/config-fns.js +207 -262
- package/dist/conformance/contract-adapter.d.ts +9 -2
- package/dist/conformance/contract-adapter.js +15 -7
- package/dist/conformance/corpus.d.ts +3 -0
- package/dist/conformance/corpus.js +5 -0
- package/dist/conformance/derive.d.ts +3 -0
- package/dist/conformance/index.d.ts +2 -2
- package/dist/conformance/index.js +2 -2
- package/dist/conformance/run.js +67 -22
- package/dist/conformance/types.d.ts +31 -15
- package/dist/conformance/types.js +5 -0
- package/dist/conformance/warnings.d.ts +1 -1
- package/dist/conformance/warnings.js +1 -4
- package/dist/cross-domain.d.ts +66 -22
- package/dist/cross-domain.js +164 -93
- package/dist/cross-session.d.ts +29 -11
- package/dist/default-model.d.ts +43 -0
- package/dist/default-model.js +34 -0
- package/dist/execution-lane-index.d.ts +4 -8
- package/dist/execution-lane-index.js +4 -8
- package/dist/execution-lanes.d.ts +31 -8
- package/dist/execution-lanes.js +23 -5
- package/dist/file-edit.d.ts +3 -3
- package/dist/file-edit.js +11 -18
- package/dist/file-store.d.ts +7 -6
- package/dist/file-store.js +37 -50
- package/dist/fleet.d.ts +364 -762
- package/dist/fleet.js +277 -224
- package/dist/hooks.d.ts +1151 -4737
- package/dist/hooks.js +59 -15
- package/dist/index.d.ts +10 -4
- package/dist/index.js +13 -7
- package/dist/integer.d.ts +11 -0
- package/dist/integer.js +11 -0
- package/dist/issue-path.d.ts +10 -0
- package/dist/issue-path.js +10 -0
- package/dist/local-load.d.ts +1 -1
- package/dist/local-load.js +4 -14
- package/dist/loopback.d.ts +26 -0
- package/dist/loopback.js +54 -0
- package/dist/migration-receipt.d.ts +42 -0
- package/dist/migration-receipt.js +12 -0
- package/dist/one-time-migrations.d.ts +62 -0
- package/dist/one-time-migrations.js +828 -0
- package/dist/prompts-digest.d.ts +1 -1
- package/dist/prompts-digest.js +3 -2
- package/dist/reader.d.ts +2 -2
- package/dist/remote-exec.d.ts +45 -187
- package/dist/remote-exec.js +16 -10
- package/dist/resolve-roster.d.ts +18 -3
- package/dist/resolve-roster.js +28 -25
- package/dist/retired-envs.d.ts +24 -0
- package/dist/retired-envs.js +114 -0
- package/dist/retired-form.d.ts +31 -0
- package/dist/retired-form.js +28 -0
- package/dist/safety-merge-spec.d.ts +131 -191
- package/dist/safety-merge-spec.js +18 -14
- package/dist/scheduler-lifetime.d.ts +5 -0
- package/dist/scheduler-lifetime.js +7 -0
- package/dist/scheduler-store-migration.d.ts +14 -0
- package/dist/scheduler-store-migration.js +14 -0
- package/dist/scheduler-store-node.d.ts +28 -12
- package/dist/scheduler-store-node.js +32 -23
- package/dist/scheduler-store.d.ts +47 -14
- package/dist/scheduler-store.js +32 -61
- package/dist/secret-refs.d.ts +3 -3
- package/dist/secret-refs.js +1 -1
- package/dist/secret-scan.d.ts +14 -24
- package/dist/secret-scan.js +45 -23
- package/dist/sha256-core.d.ts +16 -0
- package/dist/sha256-core.js +138 -0
- package/dist/sha256.d.ts +6 -1
- package/dist/sha256.js +8 -98
- package/dist/skills-manifest.js +1 -1
- package/dist/types.d.ts +1509 -13536
- package/dist/types.js +526 -438
- package/dist/url-shape.d.ts +34 -0
- package/dist/url-shape.js +61 -0
- package/dist/web-admin-credential.d.ts +96 -0
- package/dist/{center-credential.js → web-admin-credential.js} +49 -46
- package/dist/web-admin-env.d.ts +33 -0
- package/dist/web-admin-env.js +33 -0
- package/dist/worker-row-verdict.d.ts +16 -0
- package/dist/worker-row-verdict.js +31 -0
- package/dist/workers-rows.d.ts +34 -0
- package/dist/workers-rows.js +42 -0
- package/dist/zod-unwrap.d.ts +11 -0
- package/dist/zod-unwrap.js +25 -0
- package/package.json +17 -9
- package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/meta.json +0 -6
- package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/meta.json +0 -6
- package/conformance/migrate-workers/m01-g5-k8s-env-lifted/expected.json +0 -49
- package/conformance/migrate-workers/m01-g5-k8s-env-lifted/input.json +0 -26
- package/conformance/migrate-workers/m01-g5-k8s-env-lifted/meta.json +0 -4
- package/conformance/migrate-workers/m02-g5-idempotent/expected.json +0 -38
- package/conformance/migrate-workers/m02-g5-idempotent/input.json +0 -33
- package/conformance/migrate-workers/m02-g5-idempotent/meta.json +0 -5
- package/conformance/migrate-workers/m03-g5-refused-no-lane/expected.json +0 -71
- package/conformance/migrate-workers/m03-g5-refused-no-lane/input.json +0 -42
- package/conformance/migrate-workers/m03-g5-refused-no-lane/meta.json +0 -4
- package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/expected.json +0 -52
- package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/input.json +0 -30
- package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/meta.json +0 -4
- package/conformance/migrate-workers/m05-row-verdict-reserved-env/expected.json +0 -82
- package/conformance/migrate-workers/m05-row-verdict-reserved-env/input.json +0 -68
- package/conformance/migrate-workers/m05-row-verdict-reserved-env/meta.json +0 -4
- package/dist/center-credential.d.ts +0 -116
- package/dist/issue-text.d.ts +0 -10
- package/dist/issue-text.js +0 -8
- package/dist/migrate-workers.d.ts +0 -71
- package/dist/migrate-workers.js +0 -351
- package/dist/migrate.d.ts +0 -255
- package/dist/migrate.js +0 -542
- /package/conformance/cases/{c46-s811-host-name-url-userinfo-characterization → c46-s811-host-name-url-userinfo-withheld}/input.json +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `api/feedback` —— 壳的 /bug 报告送到 web-admin 的 wire 契约:`POST {web-admin}` + {@link FEEDBACK_V1_PATH} 的请求体
|
|
3
|
+
* ({@link FeedbackSubmitRequest})、一行报告的回体({@link FeedbackWire})与列表回体。写方(壳)按请求体铸、读方(web-admin)按同一只
|
|
4
|
+
* schema 判;两个上限一处定义。
|
|
5
|
+
*
|
|
6
|
+
* · 身份(`principal`)**不是**体字段:它恒取访问令牌的主体;体里带了也被剥掉(默认 strip)。
|
|
7
|
+
* · `datetime` 是客户端自报的时刻,只作参考 —— 回体的 `createdAt` 由服务端盖。
|
|
8
|
+
* · 上界:`description`({@link FEEDBACK_DESCRIPTION_MAX_CHARS})与 `transcript` 的序列化长度({@link FEEDBACK_TRANSCRIPT_MAX_JSON_CHARS})。
|
|
9
|
+
* 其余附件格 —— `rawTranscriptJsonl` / `subagentTranscripts` / `lastApiRequest` / `errors` —— 本契约**不设上界**,整份请求体也没有;
|
|
10
|
+
* `transcript` 一格的上限不是存储的上界,读方要限总量须在自己的入口另设(与写方的长会话附件量一起定)。
|
|
11
|
+
*
|
|
12
|
+
* 纯 zod 契约,零 IO,browser-safe。
|
|
13
|
+
*/
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
/** 报告的端点,相对 web-admin 根。 */
|
|
16
|
+
export declare const FEEDBACK_V1_PATH = "/api/v1/feedback";
|
|
17
|
+
/** `description`(去首尾空白后)的上限 —— 报告常带粘贴的报错与栈,所以宽,但有界。 */
|
|
18
|
+
export declare const FEEDBACK_DESCRIPTION_MAX_CHARS = 20000;
|
|
19
|
+
/** `transcript` 的 JSON 序列化长度上限。 */
|
|
20
|
+
export declare const FEEDBACK_TRANSCRIPT_MAX_JSON_CHARS = 2000000;
|
|
21
|
+
/** /bug 报告的 POST 体(写方铸、读方判,同一只)。 */
|
|
22
|
+
export declare const FeedbackSubmitRequest: z.ZodObject<{
|
|
23
|
+
surface: z.ZodString;
|
|
24
|
+
description: z.ZodString;
|
|
25
|
+
platform: z.ZodString;
|
|
26
|
+
terminal: z.ZodString;
|
|
27
|
+
gitRepo: z.ZodBoolean;
|
|
28
|
+
version: z.ZodString;
|
|
29
|
+
message_count: z.ZodNumber;
|
|
30
|
+
datetime: z.ZodString;
|
|
31
|
+
latestAssistantMessageId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
32
|
+
transcript: z.ZodArray<z.ZodUnknown>;
|
|
33
|
+
errors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
34
|
+
error: z.ZodOptional<z.ZodString>;
|
|
35
|
+
timestamp: z.ZodOptional<z.ZodString>;
|
|
36
|
+
}, z.core.$strip>>>;
|
|
37
|
+
lastApiRequest: z.ZodOptional<z.ZodUnknown>;
|
|
38
|
+
subagentTranscripts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
39
|
+
rawTranscriptJsonl: z.ZodOptional<z.ZodString>;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
export type FeedbackSubmitRequest = z.infer<typeof FeedbackSubmitRequest>;
|
|
42
|
+
/** 一行报告的回体(`POST` 的 201 体、`GET` 列表的一员)。`principal` = 提交者令牌的主体;`createdAt` = 服务端时刻(epoch ms)。 */
|
|
43
|
+
export declare const FeedbackWire: z.ZodObject<{
|
|
44
|
+
id: z.ZodString;
|
|
45
|
+
principal: z.ZodString;
|
|
46
|
+
surface: z.ZodString;
|
|
47
|
+
description: z.ZodString;
|
|
48
|
+
platform: z.ZodString;
|
|
49
|
+
terminal: z.ZodString;
|
|
50
|
+
gitRepo: z.ZodBoolean;
|
|
51
|
+
version: z.ZodString;
|
|
52
|
+
messageCount: z.ZodNumber;
|
|
53
|
+
latestAssistantMessageId: z.ZodNullable<z.ZodString>;
|
|
54
|
+
transcript: z.ZodArray<z.ZodUnknown>;
|
|
55
|
+
errors: z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
56
|
+
error: z.ZodOptional<z.ZodString>;
|
|
57
|
+
timestamp: z.ZodOptional<z.ZodString>;
|
|
58
|
+
}, z.core.$strip>>>;
|
|
59
|
+
subagentTranscripts: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
60
|
+
hasRawTranscript: z.ZodBoolean;
|
|
61
|
+
createdAt: z.ZodNumber;
|
|
62
|
+
}, z.core.$strip>;
|
|
63
|
+
export type FeedbackWire = z.infer<typeof FeedbackWire>;
|
|
64
|
+
/** `GET` 的回体(管理员浏览队列)。 */
|
|
65
|
+
export declare const FeedbackListResponse: z.ZodObject<{
|
|
66
|
+
feedback: z.ZodArray<z.ZodObject<{
|
|
67
|
+
id: z.ZodString;
|
|
68
|
+
principal: z.ZodString;
|
|
69
|
+
surface: z.ZodString;
|
|
70
|
+
description: z.ZodString;
|
|
71
|
+
platform: z.ZodString;
|
|
72
|
+
terminal: z.ZodString;
|
|
73
|
+
gitRepo: z.ZodBoolean;
|
|
74
|
+
version: z.ZodString;
|
|
75
|
+
messageCount: z.ZodNumber;
|
|
76
|
+
latestAssistantMessageId: z.ZodNullable<z.ZodString>;
|
|
77
|
+
transcript: z.ZodArray<z.ZodUnknown>;
|
|
78
|
+
errors: z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
79
|
+
error: z.ZodOptional<z.ZodString>;
|
|
80
|
+
timestamp: z.ZodOptional<z.ZodString>;
|
|
81
|
+
}, z.core.$strip>>>;
|
|
82
|
+
subagentTranscripts: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
83
|
+
hasRawTranscript: z.ZodBoolean;
|
|
84
|
+
createdAt: z.ZodNumber;
|
|
85
|
+
}, z.core.$strip>>;
|
|
86
|
+
}, z.core.$strip>;
|
|
87
|
+
export type FeedbackListResponse = z.infer<typeof FeedbackListResponse>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `api/feedback` —— 壳的 /bug 报告送到 web-admin 的 wire 契约:`POST {web-admin}` + {@link FEEDBACK_V1_PATH} 的请求体
|
|
3
|
+
* ({@link FeedbackSubmitRequest})、一行报告的回体({@link FeedbackWire})与列表回体。写方(壳)按请求体铸、读方(web-admin)按同一只
|
|
4
|
+
* schema 判;两个上限一处定义。
|
|
5
|
+
*
|
|
6
|
+
* · 身份(`principal`)**不是**体字段:它恒取访问令牌的主体;体里带了也被剥掉(默认 strip)。
|
|
7
|
+
* · `datetime` 是客户端自报的时刻,只作参考 —— 回体的 `createdAt` 由服务端盖。
|
|
8
|
+
* · 上界:`description`({@link FEEDBACK_DESCRIPTION_MAX_CHARS})与 `transcript` 的序列化长度({@link FEEDBACK_TRANSCRIPT_MAX_JSON_CHARS})。
|
|
9
|
+
* 其余附件格 —— `rawTranscriptJsonl` / `subagentTranscripts` / `lastApiRequest` / `errors` —— 本契约**不设上界**,整份请求体也没有;
|
|
10
|
+
* `transcript` 一格的上限不是存储的上界,读方要限总量须在自己的入口另设(与写方的长会话附件量一起定)。
|
|
11
|
+
*
|
|
12
|
+
* 纯 zod 契约,零 IO,browser-safe。
|
|
13
|
+
*/
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { INTEGER } from "../integer.js";
|
|
16
|
+
/** 报告的端点,相对 web-admin 根。 */
|
|
17
|
+
export const FEEDBACK_V1_PATH = "/api/v1/feedback";
|
|
18
|
+
/** `description`(去首尾空白后)的上限 —— 报告常带粘贴的报错与栈,所以宽,但有界。 */
|
|
19
|
+
export const FEEDBACK_DESCRIPTION_MAX_CHARS = 20_000;
|
|
20
|
+
/** `transcript` 的 JSON 序列化长度上限。 */
|
|
21
|
+
export const FEEDBACK_TRANSCRIPT_MAX_JSON_CHARS = 2_000_000;
|
|
22
|
+
/** 壳报告的一条错误。 */
|
|
23
|
+
const FeedbackError = z.object({ error: z.string().optional(), timestamp: z.string().optional() });
|
|
24
|
+
/** /bug 报告的 POST 体(写方铸、读方判,同一只)。 */
|
|
25
|
+
export const FeedbackSubmitRequest = z.object({
|
|
26
|
+
/** 报告从哪个界面发起(壳恒写 `cli`)。 */
|
|
27
|
+
surface: z.string().min(1),
|
|
28
|
+
description: z.string().trim().min(1).max(FEEDBACK_DESCRIPTION_MAX_CHARS),
|
|
29
|
+
platform: z.string(),
|
|
30
|
+
terminal: z.string(),
|
|
31
|
+
/** 报告时是否在 git 仓里。 */
|
|
32
|
+
gitRepo: z.boolean(),
|
|
33
|
+
/** 发报告的壳的版本。 */
|
|
34
|
+
version: z.string(),
|
|
35
|
+
message_count: INTEGER.nonnegative(),
|
|
36
|
+
/** 客户端自报的时刻(参考,不信任 —— 回体的 `createdAt` 由服务端盖)。 */
|
|
37
|
+
datetime: z.string(),
|
|
38
|
+
latestAssistantMessageId: z.string().nullable().optional(),
|
|
39
|
+
transcript: z.array(z.unknown()).refine((t) => JSON.stringify(t).length <= FEEDBACK_TRANSCRIPT_MAX_JSON_CHARS, {
|
|
40
|
+
message: `transcript must serialize to at most ${FEEDBACK_TRANSCRIPT_MAX_JSON_CHARS} characters`,
|
|
41
|
+
}),
|
|
42
|
+
errors: z.array(FeedbackError).optional(),
|
|
43
|
+
lastApiRequest: z.unknown().optional(),
|
|
44
|
+
subagentTranscripts: z.record(z.string(), z.unknown()).optional(),
|
|
45
|
+
rawTranscriptJsonl: z.string().optional(),
|
|
46
|
+
});
|
|
47
|
+
/** 一行报告的回体(`POST` 的 201 体、`GET` 列表的一员)。`principal` = 提交者令牌的主体;`createdAt` = 服务端时刻(epoch ms)。 */
|
|
48
|
+
export const FeedbackWire = z.object({
|
|
49
|
+
id: z.string().min(1),
|
|
50
|
+
principal: z.string().min(1),
|
|
51
|
+
surface: z.string(),
|
|
52
|
+
description: z.string(),
|
|
53
|
+
platform: z.string(),
|
|
54
|
+
terminal: z.string(),
|
|
55
|
+
gitRepo: z.boolean(),
|
|
56
|
+
version: z.string(),
|
|
57
|
+
messageCount: INTEGER.nonnegative(),
|
|
58
|
+
latestAssistantMessageId: z.string().nullable(),
|
|
59
|
+
transcript: z.array(z.unknown()),
|
|
60
|
+
errors: z.array(FeedbackError).nullable(),
|
|
61
|
+
subagentTranscripts: z.record(z.string(), z.unknown()).nullable(),
|
|
62
|
+
hasRawTranscript: z.boolean(),
|
|
63
|
+
createdAt: INTEGER.nonnegative(),
|
|
64
|
+
});
|
|
65
|
+
/** `GET` 的回体(管理员浏览队列)。 */
|
|
66
|
+
export const FeedbackListResponse = z.object({ feedback: z.array(FeedbackWire) });
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** 一个令牌族:v1 / v2 两个前缀 + MAC 消息的标签。族是数据,铸法与验法只有一份。 */
|
|
2
|
+
export interface ScopedTokenFamily {
|
|
3
|
+
readonly v1Prefix: string;
|
|
4
|
+
readonly v2Prefix: string;
|
|
5
|
+
readonly macTag: string;
|
|
6
|
+
}
|
|
7
|
+
/** worker 拉取令牌族(`WEB_ADMIN_ENV.token` 的值)。 */
|
|
8
|
+
export declare const WORKER_PULL_TOKEN: {
|
|
9
|
+
readonly v1Prefix: "wpt_";
|
|
10
|
+
readonly v2Prefix: "wpt2_";
|
|
11
|
+
readonly macTag: "worker-pull";
|
|
12
|
+
};
|
|
13
|
+
/** 一枚令牌绑定的(worker, scope)。 */
|
|
14
|
+
export interface ScopedTokenBinding {
|
|
15
|
+
worker: string;
|
|
16
|
+
scope: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 铸一枚按 scope 绑定的令牌(见模块头的两种形)。铸不出 ⇒ **抛**:共享密钥为空、scope 不是 scope id、worker 名为空或带 `_`。
|
|
20
|
+
* `scope` 缺省 = {@link GLOBAL_SCOPE}(v1)。
|
|
21
|
+
*/
|
|
22
|
+
export declare function mintScopedToken(family: ScopedTokenFamily, worker: string, shared: string, scope?: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* 读一枚令牌**声称**的绑定 —— 不验真(验真用 {@link verifyScopedToken})。v1 ⇒ 缺省 scope;v2 ⇒ 帧里的 scope;不是这一族的前缀、
|
|
25
|
+
* 帧切不开(缺 worker / 缺 mac / v2 缺 scope)⇒ `undefined`。
|
|
26
|
+
*/
|
|
27
|
+
export declare function readScopedTokenBinding(family: ScopedTokenFamily, presented: string): ScopedTokenBinding | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* 验一枚令牌:认领 → 按认领重铸正本 → 常量时间比对。是正本 ⇒ 它绑定的(worker, scope);否则(认领不出、认领的 scope 不是
|
|
30
|
+
* scope id、共享密钥为空、MAC 不对、v2 冒充缺省 scope)⇒ `undefined`。从不抛。
|
|
31
|
+
*/
|
|
32
|
+
export declare function verifyScopedToken(family: ScopedTokenFamily, presented: string, shared: string): ScopedTokenBinding | undefined;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `api/pull-token` —— 按 scope 绑定的**令牌形与铸法**:web-admin 给每台 worker 铸一枚只够读它自己那份配置的拉取令牌
|
|
3
|
+
* (`WEB_ADMIN_ENV.token` 的值),收到时按同一条规则验。一条规则,**令牌族是数据**(前缀对 + MAC 标签):
|
|
4
|
+
*
|
|
5
|
+
* · 缺省 scope(`GLOBAL_SCOPE`)⇒ v1:`<v1Prefix><worker>_<mac>`,`mac` = HMAC-SHA256(共享密钥, `<macTag>:v1:<worker>`) 的小写 hex;
|
|
6
|
+
* · 其余 scope ⇒ v2:`<v2Prefix><scope>_<worker>_<mac>`,`mac` = HMAC-SHA256(共享密钥, `<macTag>:v2:<scope>:<worker>`)
|
|
7
|
+
* —— MAC 消息里带着 scope,别的 scope 里同名的 worker 拿不出它。
|
|
8
|
+
*
|
|
9
|
+
* 帧按 `_` 切:scope id 是 slug(`SCOPE_ID_REGEX`,无 `_`),worker 名同样不许带 `_` —— 铸不出一枚切得开的令牌就**抛**,
|
|
10
|
+
* 不铸一枚永远验不过的令牌。共享密钥为空同样抛(不拿空钥签)。hex + 前缀 ⇒ 令牌里没有 `,` / `=`(可以原样放进
|
|
11
|
+
* `SERVICE_AUTH_TOKENS` 一类的 CSV)。
|
|
12
|
+
*
|
|
13
|
+
* 验 = 认领(从帧里读出它**声称**的 worker / scope,不信它)→ 按认领重铸正本 → 常量时间比对。一枚 v2 令牌声称缺省 scope 永远
|
|
14
|
+
* 验不过(缺省 scope 的正本是 v1)。
|
|
15
|
+
*
|
|
16
|
+
* 引擎只把这枚令牌当不透明 bearer 带着(它不铸、不验);铸与验都在 web-admin。纯函数,零 IO,browser-safe(HMAC 是本包自带的
|
|
17
|
+
* 纯 JS 实现,与 `node:crypto` 逐字节同)。
|
|
18
|
+
*/
|
|
19
|
+
import { hmacSha256Hex } from "../sha256-core.js";
|
|
20
|
+
import { GLOBAL_SCOPE, SCOPE_ID_REGEX } from "./scopes.js";
|
|
21
|
+
/** worker 拉取令牌族(`WEB_ADMIN_ENV.token` 的值)。 */
|
|
22
|
+
export const WORKER_PULL_TOKEN = { v1Prefix: "wpt_", v2Prefix: "wpt2_", macTag: "worker-pull" };
|
|
23
|
+
/** worker 名能不能进帧:非空、不带 `_`(帧的分隔符)。 */
|
|
24
|
+
const framableWorker = (worker) => worker.length > 0 && !worker.includes("_");
|
|
25
|
+
/**
|
|
26
|
+
* 铸一枚按 scope 绑定的令牌(见模块头的两种形)。铸不出 ⇒ **抛**:共享密钥为空、scope 不是 scope id、worker 名为空或带 `_`。
|
|
27
|
+
* `scope` 缺省 = {@link GLOBAL_SCOPE}(v1)。
|
|
28
|
+
*/
|
|
29
|
+
export function mintScopedToken(family, worker, shared, scope = GLOBAL_SCOPE) {
|
|
30
|
+
if (shared.length === 0)
|
|
31
|
+
throw new Error(`cannot mint a ${family.macTag} token: the shared key is empty`);
|
|
32
|
+
if (!framableWorker(worker))
|
|
33
|
+
throw new Error(`cannot mint a ${family.macTag} token: worker ${JSON.stringify(worker)} is empty or contains "_" (the frame separator)`);
|
|
34
|
+
if (scope === GLOBAL_SCOPE)
|
|
35
|
+
return `${family.v1Prefix}${worker}_${hmacSha256Hex(shared, `${family.macTag}:v1:${worker}`)}`;
|
|
36
|
+
if (!SCOPE_ID_REGEX.test(scope))
|
|
37
|
+
throw new Error(`cannot mint a ${family.macTag} token: scope ${JSON.stringify(scope)} is not a scope id`);
|
|
38
|
+
return `${family.v2Prefix}${scope}_${worker}_${hmacSha256Hex(shared, `${family.macTag}:v2:${scope}:${worker}`)}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 读一枚令牌**声称**的绑定 —— 不验真(验真用 {@link verifyScopedToken})。v1 ⇒ 缺省 scope;v2 ⇒ 帧里的 scope;不是这一族的前缀、
|
|
42
|
+
* 帧切不开(缺 worker / 缺 mac / v2 缺 scope)⇒ `undefined`。
|
|
43
|
+
*/
|
|
44
|
+
export function readScopedTokenBinding(family, presented) {
|
|
45
|
+
// v2 先判:族的 v2 前缀可能以 v1 前缀开头(`wpt2_` 不以 `wpt_` 开头,但规则不该依赖具体拼法)。
|
|
46
|
+
if (presented.startsWith(family.v2Prefix)) {
|
|
47
|
+
const rest = presented.slice(family.v2Prefix.length);
|
|
48
|
+
const first = rest.indexOf("_");
|
|
49
|
+
const last = rest.lastIndexOf("_");
|
|
50
|
+
if (first <= 0 || last <= first + 1 || last === rest.length - 1)
|
|
51
|
+
return undefined;
|
|
52
|
+
return { scope: rest.slice(0, first), worker: rest.slice(first + 1, last) };
|
|
53
|
+
}
|
|
54
|
+
if (presented.startsWith(family.v1Prefix)) {
|
|
55
|
+
const rest = presented.slice(family.v1Prefix.length);
|
|
56
|
+
const sep = rest.lastIndexOf("_");
|
|
57
|
+
if (sep <= 0 || sep === rest.length - 1)
|
|
58
|
+
return undefined;
|
|
59
|
+
return { worker: rest.slice(0, sep), scope: GLOBAL_SCOPE };
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
/** 常量时间比对(长度先比 —— 正本的长度不是秘密:前缀 + 名 + 64 位 hex)。 */
|
|
64
|
+
function constantTimeEqual(a, b) {
|
|
65
|
+
if (a.length !== b.length)
|
|
66
|
+
return false;
|
|
67
|
+
let diff = 0;
|
|
68
|
+
for (let i = 0; i < a.length; i++)
|
|
69
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
70
|
+
return diff === 0;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 验一枚令牌:认领 → 按认领重铸正本 → 常量时间比对。是正本 ⇒ 它绑定的(worker, scope);否则(认领不出、认领的 scope 不是
|
|
74
|
+
* scope id、共享密钥为空、MAC 不对、v2 冒充缺省 scope)⇒ `undefined`。从不抛。
|
|
75
|
+
*/
|
|
76
|
+
export function verifyScopedToken(family, presented, shared) {
|
|
77
|
+
const claimed = readScopedTokenBinding(family, presented);
|
|
78
|
+
if (claimed === undefined)
|
|
79
|
+
return undefined;
|
|
80
|
+
let canonical;
|
|
81
|
+
try {
|
|
82
|
+
canonical = mintScopedToken(family, claimed.worker, shared, claimed.scope);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
return constantTimeEqual(presented, canonical) ? claimed : undefined;
|
|
88
|
+
}
|
package/dist/api/scopes.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `api/scopes` — the MULTI-TENANT SCOPE wire contract for
|
|
3
|
-
* /api/v1/auth/scope). Frozen here so
|
|
2
|
+
* `api/scopes` — the MULTI-TENANT SCOPE wire contract for web-admin (/api/v1/scopes* +
|
|
3
|
+
* /api/v1/auth/scope). Frozen here so web-admin (the implementation), the orchestrator, workers and
|
|
4
4
|
* the sema shell import ONE source of truth — same "契约先行" discipline as `api/auth`.
|
|
5
5
|
*
|
|
6
|
-
* THE SCOPE MODEL (normative —
|
|
6
|
+
* THE SCOPE MODEL (normative — web-admin implements EXACTLY this):
|
|
7
7
|
*
|
|
8
8
|
* - A **scope** is a tenant boundary: `{id, name, createdAt}`. `id` is a slug (SCOPE_ID_REGEX).
|
|
9
9
|
* The reserved id `"global"` (GLOBAL_SCOPE) is the DEFAULT scope: it always exists, is never
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
* and `viewer` in scope B.
|
|
14
14
|
* - The **instance-level** `users.role` is unchanged and orthogonal: an instance `admin` administers
|
|
15
15
|
* ALL scopes — inside any scope their effective role is always `"admin"` (see effectiveScopeRole).
|
|
16
|
-
* - The **access JWT**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* - The **access JWT** carries a `scope` claim (SCOPE_CLAIM) binding the token to its active scope —
|
|
17
|
+
* REQUIRED on read: a token without it is refused (readTokenScope ⇒ `scope_claim_missing`, answered 401);
|
|
18
|
+
* it is not read as `"global"`. The refresh token is NOT scope-bound; refresh re-mints per
|
|
19
|
+
* resolveTokenScope (always with the claim), and the client re-switches if it wants another scope.
|
|
20
20
|
*
|
|
21
21
|
* ENDPOINTS (paths in SCOPES_V1_PATHS / scopeMembersPath):
|
|
22
22
|
*
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
*
|
|
38
38
|
* WIRE CASING: /api/v1/auth/scope is part of the OAuth face — snake_case on the wire (access_token,
|
|
39
39
|
* expires_in), matching `api/auth`. The /api/v1/scopes management face uses this package's camelCase
|
|
40
|
-
* (createdAt, scopeId) — it is a
|
|
40
|
+
* (createdAt, scopeId) — it is a web-admin management API, not an OAuth endpoint.
|
|
41
41
|
*
|
|
42
42
|
* Pure zod contract + pure decision functions — zero IO, browser-safe. The decision functions
|
|
43
|
-
* (resolveTokenScope / canSwitchScope / effectiveScopeRole) are THE semantics:
|
|
43
|
+
* (resolveTokenScope / canSwitchScope / effectiveScopeRole) are THE semantics: web-admin
|
|
44
44
|
* implementation imports and calls these — it must not re-implement them.
|
|
45
45
|
*/
|
|
46
46
|
import { z } from "zod";
|
|
@@ -51,14 +51,15 @@ export declare const GLOBAL_SCOPE = "global";
|
|
|
51
51
|
export declare const RESERVED_SCOPE_IDS: readonly ["global"];
|
|
52
52
|
/** Slug shape for scope ids: lowercase alphanumeric + inner dashes, 1–64 chars, no leading/trailing dash. */
|
|
53
53
|
export declare const SCOPE_ID_REGEX: RegExp;
|
|
54
|
-
/** The scope endpoints, relative to
|
|
54
|
+
/** The scope endpoints, relative to web-admin origin. `members` needs the scope id — use scopeMembersPath. */
|
|
55
55
|
export declare const SCOPES_V1_PATHS: {
|
|
56
56
|
/** GET (list) + POST (create). */
|
|
57
57
|
readonly scopes: "/api/v1/scopes";
|
|
58
58
|
/** POST — mint a new access token bound to another scope. */
|
|
59
59
|
readonly authScope: "/api/v1/auth/scope";
|
|
60
60
|
};
|
|
61
|
-
/** The JWT claim carrying the token's active scope.
|
|
61
|
+
/** The JWT claim carrying the token's active scope. Every minted access token carries it; a token without it is refused on read
|
|
62
|
+
* ({@link readTokenScope}). */
|
|
62
63
|
export declare const SCOPE_CLAIM = "scope";
|
|
63
64
|
/** `/api/v1/scopes/{id}/members` for PUT (upsert) and DELETE (remove). */
|
|
64
65
|
export declare function scopeMembersPath(scopeId: string): string;
|
|
@@ -69,230 +70,129 @@ export declare function isReservedScopeId(id: string): boolean;
|
|
|
69
70
|
export declare const ScopeId: z.ZodString;
|
|
70
71
|
export type ScopeId = z.infer<typeof ScopeId>;
|
|
71
72
|
/** The per-scope role ladder = the existing instance RBAC ladder (viewer < editor < publisher < admin). */
|
|
72
|
-
export declare const ScopeRole: z.ZodEnum<
|
|
73
|
+
export declare const ScopeRole: z.ZodEnum<{
|
|
74
|
+
viewer: "viewer";
|
|
75
|
+
editor: "editor";
|
|
76
|
+
publisher: "publisher";
|
|
77
|
+
admin: "admin";
|
|
78
|
+
}>;
|
|
73
79
|
export type ScopeRole = UserRole;
|
|
74
80
|
/** The scope entity. */
|
|
75
81
|
export declare const Scope: z.ZodObject<{
|
|
76
82
|
id: z.ZodString;
|
|
77
|
-
/** Human display name (non-unique, non-secret). */
|
|
78
83
|
name: z.ZodString;
|
|
79
|
-
/** Creation time, epoch MILLISECONDS. The reserved "global" scope reports its provisioning time. */
|
|
80
84
|
createdAt: z.ZodNumber;
|
|
81
|
-
},
|
|
82
|
-
id: z.ZodString;
|
|
83
|
-
/** Human display name (non-unique, non-secret). */
|
|
84
|
-
name: z.ZodString;
|
|
85
|
-
/** Creation time, epoch MILLISECONDS. The reserved "global" scope reports its provisioning time. */
|
|
86
|
-
createdAt: z.ZodNumber;
|
|
87
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
88
|
-
id: z.ZodString;
|
|
89
|
-
/** Human display name (non-unique, non-secret). */
|
|
90
|
-
name: z.ZodString;
|
|
91
|
-
/** Creation time, epoch MILLISECONDS. The reserved "global" scope reports its provisioning time. */
|
|
92
|
-
createdAt: z.ZodNumber;
|
|
93
|
-
}, z.ZodTypeAny, "passthrough">>;
|
|
85
|
+
}, z.core.$loose>;
|
|
94
86
|
export type Scope = z.infer<typeof Scope>;
|
|
95
87
|
/** One membership row: `principal` holds `role` inside `scopeId`. Per-scope — the same principal may
|
|
96
88
|
* hold different roles in different scopes. */
|
|
97
89
|
export declare const ScopeMember: z.ZodObject<{
|
|
98
90
|
scopeId: z.ZodString;
|
|
99
|
-
/** The user identity — same principal string the JWT `sub` / x-agent-principal pipeline uses. */
|
|
100
|
-
principal: z.ZodString;
|
|
101
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
102
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
103
|
-
scopeId: z.ZodString;
|
|
104
|
-
/** The user identity — same principal string the JWT `sub` / x-agent-principal pipeline uses. */
|
|
105
91
|
principal: z.ZodString;
|
|
106
|
-
role: z.ZodEnum<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}, z.
|
|
92
|
+
role: z.ZodEnum<{
|
|
93
|
+
viewer: "viewer";
|
|
94
|
+
editor: "editor";
|
|
95
|
+
publisher: "publisher";
|
|
96
|
+
admin: "admin";
|
|
97
|
+
}>;
|
|
98
|
+
}, z.core.$loose>;
|
|
113
99
|
export type ScopeMember = z.infer<typeof ScopeMember>;
|
|
114
100
|
/** One list entry: a scope the caller can act in, with the caller's EFFECTIVE role there
|
|
115
101
|
* (effectiveScopeRole — instance admins list every scope with role "admin"). */
|
|
116
102
|
export declare const ScopeListEntry: z.ZodObject<{
|
|
117
103
|
id: z.ZodString;
|
|
118
104
|
name: z.ZodString;
|
|
119
|
-
role: z.ZodEnum<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
name: z.ZodString;
|
|
127
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
128
|
-
}, z.ZodTypeAny, "passthrough">>;
|
|
105
|
+
role: z.ZodEnum<{
|
|
106
|
+
viewer: "viewer";
|
|
107
|
+
editor: "editor";
|
|
108
|
+
publisher: "publisher";
|
|
109
|
+
admin: "admin";
|
|
110
|
+
}>;
|
|
111
|
+
}, z.core.$loose>;
|
|
129
112
|
export type ScopeListEntry = z.infer<typeof ScopeListEntry>;
|
|
130
113
|
export declare const ScopesListResponse: z.ZodObject<{
|
|
131
114
|
scopes: z.ZodArray<z.ZodObject<{
|
|
132
115
|
id: z.ZodString;
|
|
133
116
|
name: z.ZodString;
|
|
134
|
-
role: z.ZodEnum<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
143
|
-
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
144
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
145
|
-
scopes: z.ZodArray<z.ZodObject<{
|
|
146
|
-
id: z.ZodString;
|
|
147
|
-
name: z.ZodString;
|
|
148
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
149
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
150
|
-
id: z.ZodString;
|
|
151
|
-
name: z.ZodString;
|
|
152
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
153
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
154
|
-
id: z.ZodString;
|
|
155
|
-
name: z.ZodString;
|
|
156
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
157
|
-
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
158
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
159
|
-
scopes: z.ZodArray<z.ZodObject<{
|
|
160
|
-
id: z.ZodString;
|
|
161
|
-
name: z.ZodString;
|
|
162
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
163
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
164
|
-
id: z.ZodString;
|
|
165
|
-
name: z.ZodString;
|
|
166
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
167
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
168
|
-
id: z.ZodString;
|
|
169
|
-
name: z.ZodString;
|
|
170
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
171
|
-
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
172
|
-
}, z.ZodTypeAny, "passthrough">>;
|
|
117
|
+
role: z.ZodEnum<{
|
|
118
|
+
viewer: "viewer";
|
|
119
|
+
editor: "editor";
|
|
120
|
+
publisher: "publisher";
|
|
121
|
+
admin: "admin";
|
|
122
|
+
}>;
|
|
123
|
+
}, z.core.$loose>>;
|
|
124
|
+
}, z.core.$loose>;
|
|
173
125
|
export type ScopesListResponse = z.infer<typeof ScopesListResponse>;
|
|
174
126
|
/** Request: the target scope. Errors: `invalid_request` (malformed), `access_denied` (not a member and
|
|
175
127
|
* not an instance admin — canSwitchScope false). */
|
|
176
128
|
export declare const ScopeSwitchRequest: z.ZodObject<{
|
|
177
129
|
scope: z.ZodString;
|
|
178
|
-
},
|
|
179
|
-
scope: z.ZodString;
|
|
180
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
181
|
-
scope: z.ZodString;
|
|
182
|
-
}, z.ZodTypeAny, "passthrough">>;
|
|
130
|
+
}, z.core.$loose>;
|
|
183
131
|
export type ScopeSwitchRequest = z.infer<typeof ScopeSwitchRequest>;
|
|
184
132
|
/** A fresh access token bound to the requested scope (`scope` claim = `scope` field below). The refresh
|
|
185
133
|
* grant is untouched — this endpoint never returns a refresh_token. `role` = the caller's effective role
|
|
186
134
|
* inside the new scope (effectiveScopeRole). */
|
|
187
135
|
export declare const ScopeSwitchResponse: z.ZodObject<{
|
|
188
|
-
/** RS256 JWT with the `scope` claim set — verify per api/auth-bridge; VerifiedIdentity.scope carries it. */
|
|
189
|
-
access_token: z.ZodString;
|
|
190
|
-
token_type: z.ZodLiteral<"Bearer">;
|
|
191
|
-
/** Access-token lifetime in seconds (ACCESS_TOKEN_TTL_SECONDS — same mint as api/auth). */
|
|
192
|
-
expires_in: z.ZodNumber;
|
|
193
|
-
scope: z.ZodString;
|
|
194
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
195
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
196
|
-
/** RS256 JWT with the `scope` claim set — verify per api/auth-bridge; VerifiedIdentity.scope carries it. */
|
|
197
|
-
access_token: z.ZodString;
|
|
198
|
-
token_type: z.ZodLiteral<"Bearer">;
|
|
199
|
-
/** Access-token lifetime in seconds (ACCESS_TOKEN_TTL_SECONDS — same mint as api/auth). */
|
|
200
|
-
expires_in: z.ZodNumber;
|
|
201
|
-
scope: z.ZodString;
|
|
202
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
203
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
204
|
-
/** RS256 JWT with the `scope` claim set — verify per api/auth-bridge; VerifiedIdentity.scope carries it. */
|
|
205
136
|
access_token: z.ZodString;
|
|
206
137
|
token_type: z.ZodLiteral<"Bearer">;
|
|
207
|
-
/** Access-token lifetime in seconds (ACCESS_TOKEN_TTL_SECONDS — same mint as api/auth). */
|
|
208
138
|
expires_in: z.ZodNumber;
|
|
209
139
|
scope: z.ZodString;
|
|
210
|
-
role: z.ZodEnum<
|
|
211
|
-
|
|
140
|
+
role: z.ZodEnum<{
|
|
141
|
+
viewer: "viewer";
|
|
142
|
+
editor: "editor";
|
|
143
|
+
publisher: "publisher";
|
|
144
|
+
admin: "admin";
|
|
145
|
+
}>;
|
|
146
|
+
}, z.core.$loose>;
|
|
212
147
|
export type ScopeSwitchResponse = z.infer<typeof ScopeSwitchResponse>;
|
|
213
148
|
/** Re-exported convenience: the switch endpoint mints through the SAME exit as api/auth (1h JWT). */
|
|
214
149
|
export declare const SCOPE_SWITCH_TOKEN_TTL_SECONDS: number;
|
|
215
150
|
/** Reserved ids ("global") are rejected AT THE SCHEMA — the default scope pre-exists and is not creatable. */
|
|
216
151
|
export declare const ScopeCreateRequest: z.ZodObject<{
|
|
217
|
-
id: z.
|
|
218
|
-
name: z.ZodString;
|
|
219
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
220
|
-
id: z.ZodEffects<z.ZodString, string, string>;
|
|
221
|
-
name: z.ZodString;
|
|
222
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
223
|
-
id: z.ZodEffects<z.ZodString, string, string>;
|
|
152
|
+
id: z.ZodString;
|
|
224
153
|
name: z.ZodString;
|
|
225
|
-
}, z.
|
|
154
|
+
}, z.core.$loose>;
|
|
226
155
|
export type ScopeCreateRequest = z.infer<typeof ScopeCreateRequest>;
|
|
227
156
|
/** The created scope, as stored. */
|
|
228
157
|
export declare const ScopeCreateResponse: z.ZodObject<{
|
|
229
158
|
id: z.ZodString;
|
|
230
|
-
/** Human display name (non-unique, non-secret). */
|
|
231
|
-
name: z.ZodString;
|
|
232
|
-
/** Creation time, epoch MILLISECONDS. The reserved "global" scope reports its provisioning time. */
|
|
233
|
-
createdAt: z.ZodNumber;
|
|
234
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
235
|
-
id: z.ZodString;
|
|
236
|
-
/** Human display name (non-unique, non-secret). */
|
|
237
159
|
name: z.ZodString;
|
|
238
|
-
/** Creation time, epoch MILLISECONDS. The reserved "global" scope reports its provisioning time. */
|
|
239
160
|
createdAt: z.ZodNumber;
|
|
240
|
-
}, z.
|
|
241
|
-
id: z.ZodString;
|
|
242
|
-
/** Human display name (non-unique, non-secret). */
|
|
243
|
-
name: z.ZodString;
|
|
244
|
-
/** Creation time, epoch MILLISECONDS. The reserved "global" scope reports its provisioning time. */
|
|
245
|
-
createdAt: z.ZodNumber;
|
|
246
|
-
}, z.ZodTypeAny, "passthrough">>;
|
|
161
|
+
}, z.core.$loose>;
|
|
247
162
|
export type ScopeCreateResponse = Scope;
|
|
248
163
|
/** PUT body — upsert: sets `principal`'s role in the path scope, creating or replacing the row. */
|
|
249
164
|
export declare const ScopeMemberUpsertRequest: z.ZodObject<{
|
|
250
165
|
principal: z.ZodString;
|
|
251
|
-
role: z.ZodEnum<
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}, z.ZodTypeAny, "passthrough">>;
|
|
166
|
+
role: z.ZodEnum<{
|
|
167
|
+
viewer: "viewer";
|
|
168
|
+
editor: "editor";
|
|
169
|
+
publisher: "publisher";
|
|
170
|
+
admin: "admin";
|
|
171
|
+
}>;
|
|
172
|
+
}, z.core.$loose>;
|
|
259
173
|
export type ScopeMemberUpsertRequest = z.infer<typeof ScopeMemberUpsertRequest>;
|
|
260
174
|
/** The stored membership row (scopeId echoes the path). */
|
|
261
175
|
export declare const ScopeMemberUpsertResponse: z.ZodObject<{
|
|
262
176
|
scopeId: z.ZodString;
|
|
263
|
-
/** The user identity — same principal string the JWT `sub` / x-agent-principal pipeline uses. */
|
|
264
|
-
principal: z.ZodString;
|
|
265
|
-
role: z.ZodEnum<[UserRole, ...UserRole[]]>;
|
|
266
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
267
|
-
scopeId: z.ZodString;
|
|
268
|
-
/** The user identity — same principal string the JWT `sub` / x-agent-principal pipeline uses. */
|
|
269
177
|
principal: z.ZodString;
|
|
270
|
-
role: z.ZodEnum<
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}, z.
|
|
178
|
+
role: z.ZodEnum<{
|
|
179
|
+
viewer: "viewer";
|
|
180
|
+
editor: "editor";
|
|
181
|
+
publisher: "publisher";
|
|
182
|
+
admin: "admin";
|
|
183
|
+
}>;
|
|
184
|
+
}, z.core.$loose>;
|
|
277
185
|
export type ScopeMemberUpsertResponse = ScopeMember;
|
|
278
186
|
/** DELETE body — remove `principal` from the path scope. */
|
|
279
187
|
export declare const ScopeMemberDeleteRequest: z.ZodObject<{
|
|
280
188
|
principal: z.ZodString;
|
|
281
|
-
},
|
|
282
|
-
principal: z.ZodString;
|
|
283
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
284
|
-
principal: z.ZodString;
|
|
285
|
-
}, z.ZodTypeAny, "passthrough">>;
|
|
189
|
+
}, z.core.$loose>;
|
|
286
190
|
export type ScopeMemberDeleteRequest = z.infer<typeof ScopeMemberDeleteRequest>;
|
|
287
191
|
/** Idempotent: `{ok:true}` whether or not the principal was a member (no membership disclosure beyond
|
|
288
192
|
* what the admin caller already sees). */
|
|
289
193
|
export declare const ScopeMemberDeleteResponse: z.ZodObject<{
|
|
290
194
|
ok: z.ZodLiteral<true>;
|
|
291
|
-
},
|
|
292
|
-
ok: z.ZodLiteral<true>;
|
|
293
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
294
|
-
ok: z.ZodLiteral<true>;
|
|
295
|
-
}, z.ZodTypeAny, "passthrough">>;
|
|
195
|
+
}, z.core.$loose>;
|
|
296
196
|
export type ScopeMemberDeleteResponse = z.infer<typeof ScopeMemberDeleteResponse>;
|
|
297
197
|
/** A membership as the decision functions see it (a projection of ScopeMember). */
|
|
298
198
|
export interface ScopeMembershipInput {
|
|
@@ -317,7 +217,7 @@ export interface ResolvedTokenScope {
|
|
|
317
217
|
export declare function effectiveScopeRole(membershipRole: UserRole | undefined, instanceRole: string): UserRole | undefined;
|
|
318
218
|
/**
|
|
319
219
|
* THE token-minting scope decision — which scope (and role claim) a fresh access token binds to, given
|
|
320
|
-
* the principal's scope memberships and instance role. Deterministic;
|
|
220
|
+
* the principal's scope memberships and instance role. Deterministic; web-admin (Node/Next) imports
|
|
321
221
|
* and calls this at every mint (device-flow grant AND refresh). NORMATIVE RULES (verbatim, 任务书拍定):
|
|
322
222
|
*
|
|
323
223
|
* 1. **唯一成员 scope** — exactly one membership → bind that scope, role = that membership's effective
|
|
@@ -342,11 +242,19 @@ export declare function resolveTokenScope(memberships: readonly ScopeMembershipI
|
|
|
342
242
|
* membership cannot switch to global (their default mint never lands there either, rule 3 above).
|
|
343
243
|
*/
|
|
344
244
|
export declare function canSwitchScope(memberships: readonly ScopeMembershipInput[], instanceRole: string, targetScope: string): boolean;
|
|
245
|
+
/** Why {@link readTokenScope} refuses — a CLOSED set (one word today). */
|
|
246
|
+
export declare const TOKEN_SCOPE_REFUSALS: readonly ["scope_claim_missing"];
|
|
247
|
+
export type TokenScopeRefusal = (typeof TOKEN_SCOPE_REFUSALS)[number];
|
|
345
248
|
/**
|
|
346
|
-
* Read the active scope off verified JWT claims
|
|
347
|
-
*
|
|
348
|
-
*
|
|
249
|
+
* Read the active scope off verified JWT claims — the ONE reader. A non-empty string `scope` claim ⇒ `{ scope }`; a claim that is
|
|
250
|
+
* missing, empty or not a string ⇒ `{ refused: "scope_claim_missing" }` (the reader answers 401). There is no default scope here:
|
|
251
|
+
* every access token web-admin mints carries the claim, an access token lives at most {@link ACCESS_TOKEN_TTL_SECONDS}, and a refresh
|
|
252
|
+
* re-mints one with the claim — a token without it is not a token this contract reads (it used to be read as `"global"`).
|
|
349
253
|
*/
|
|
350
|
-
export declare function
|
|
254
|
+
export declare function readTokenScope(claims: {
|
|
351
255
|
scope?: unknown;
|
|
352
|
-
}):
|
|
256
|
+
}): {
|
|
257
|
+
scope: string;
|
|
258
|
+
} | {
|
|
259
|
+
refused: TokenScopeRefusal;
|
|
260
|
+
};
|