@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
package/dist/secret-scan.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 秘密扫描读的**归一化**,外加「URL 的 userinfo 就是凭据」那一只判定({@link carriesUserinfo})—— 包内私有模块(6.3.0):不进主 barrel、
|
|
3
3
|
* 不进 `package.json` 的 `exports`,消费方拿不到。
|
|
4
|
-
* 校验侧探测器 `containsSecretToken`(`./types`)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* 校验侧探测器 `containsSecretToken`(`./types`)与展示侧脱敏器 `redactSecrets`(`./config-fns`)读**同一组**形 {@link secretScanForms}
|
|
5
|
+
* (11.0.0 起一只扫描器,S-1274;改前校验侧只读百分号层)。这组读法(边界读法、`+` 读法、「没走完」那一位)刻意不做公开契约:
|
|
6
|
+
* 它们以后要随新发现的编码形调整,调整不该是契约变更(opus 独立腿 L7)—— 但写与显永远同时调整。
|
|
7
7
|
*
|
|
8
8
|
* **单遍、零异常**(opus 独立腿 M1,对 6.2.0 的读路径 CPU 回归):6.2.0 起的宽容解码对每个 `%HH` 各 `try decodeURIComponent`,
|
|
9
9
|
* 非 ASCII 的转义每个都抛一次异常(≈ 20–30 µs / 字节,同步阻塞);6.3.0 首稿又让探测器顺带算它不用的展示形。这里两处解码都
|
|
10
10
|
* 不靠异常:单个转义的判据写成字符集(只有 ASCII 的 `%00`–`%7F` 单独解得开),转义串的判据写成 UTF-8 良构判定 ——
|
|
11
11
|
* 结果与原来的 try/catch 逐字相同(`test/disclosable-identity-6.3.0.test.ts` 的两只冻结 oracle 钉着)。
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* 本模块只引 `./url-shape`(零 import 的叶子;`./types` 引本模块,本模块不回引,不成环)。
|
|
14
14
|
*/
|
|
15
|
+
import { httpUrlFlaws } from "./url-shape.js"; // the URL shape judge — whether a parsed URL carries userinfo is its call
|
|
15
16
|
/** How many percent-decoded layers a secret scan reads beyond the raw string (a multi-encoded `%2573%256B…` →
|
|
16
17
|
* `%73%6B…` → `sk-…`). Bounded → deterministic. */
|
|
17
18
|
export const SECRET_SCAN_DECODE_LAYERS = 3;
|
|
@@ -23,8 +24,8 @@ function percentDecodeLayer(s) {
|
|
|
23
24
|
return s.indexOf("%") === -1 ? s : s.replace(/%([0-7][0-9A-Fa-f])/g, (_m, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
24
25
|
}
|
|
25
26
|
/** The raw string first, then up to {@link SECRET_SCAN_DECODE_LAYERS} tolerant percent-decoded layers, stopping at the first
|
|
26
|
-
* layer that decodes nothing — the
|
|
27
|
-
|
|
27
|
+
* layer that decodes nothing — the first part of {@link secretScanForms}. */
|
|
28
|
+
function percentLayers(text) {
|
|
28
29
|
const layers = [text];
|
|
29
30
|
let cur = text;
|
|
30
31
|
for (let i = 0; i < SECRET_SCAN_DECODE_LAYERS; i++) {
|
|
@@ -70,7 +71,7 @@ function isWellFormedUtf8(bytes) {
|
|
|
70
71
|
* is well-formed (an encoded NBSP / zero-width space becomes the delimiter it really is); otherwise its ASCII escapes decode
|
|
71
72
|
* on their own and every other escape reads as a space — an escape never glues its hex digits onto the token after it
|
|
72
73
|
* (`%C2%A0sk-live-…` read escape by escape is `%C2%A0sk-…`, whose tokens are `C2` / `A0sk-…`: the `sk-` boundary is gone,
|
|
73
|
-
* though any standard decoder restores it).
|
|
74
|
+
* though any standard decoder restores it). Read by both scanners since 11.0.0. */
|
|
74
75
|
function boundaryReading(s) {
|
|
75
76
|
if (s.indexOf("%") === -1)
|
|
76
77
|
return s;
|
|
@@ -81,7 +82,8 @@ function boundaryReading(s) {
|
|
|
81
82
|
return isWellFormedUtf8(bytes) ? decodeURIComponent(run) : bytes.map((b) => (b < 0x80 ? String.fromCharCode(b) : " ")).join("");
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
|
-
/** The
|
|
85
|
+
/** The normalization BOTH scanners read (11.0.0): the validation detector `containsSecretToken` (types) and the display redactor
|
|
86
|
+
* `redactSecrets` (config-fns). */
|
|
85
87
|
export function secretScanForms(text) {
|
|
86
88
|
const layers = percentLayers(text);
|
|
87
89
|
const last = layers[layers.length - 1];
|
|
@@ -90,21 +92,41 @@ export function secretScanForms(text) {
|
|
|
90
92
|
const forms = [...new Set([...readings, ...readings.map((reading) => (reading.indexOf("+") === -1 ? reading : reading.replace(/\+/g, " ")))])];
|
|
91
93
|
return { percentLayers: layers, forms, unresolved };
|
|
92
94
|
}
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
*
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
/** 空白(一段文本里候选段的唯一分隔:引号、尖括号、反引号都可能在口令里 —— WHATWG 收 `https://u:p'w@h`)。 */
|
|
96
|
+
const isSpace = (c) => c === " " || c === "\t" || c === "\n" || c === "\r" || c === "\f" || c === "\v" || /\s/.test(c);
|
|
97
|
+
/** 「URL 的 userinfo 就是凭据」的**唯一**判定(6.3.0 起;11.0.0 S-811 扩到自由文本):文本是带 userinfo 的 URL(URL 形判官
|
|
98
|
+
* {@link httpUrlFlaws} 判出 `userinfo` 这一件 —— 层级式任意 scheme,`ssh://git@host` 只带用户名也算),或文本里某个 `@` 收尾的那一段
|
|
99
|
+
* (从 `@` 向前到空白或上一个 `@`)是凭据对:
|
|
100
|
+
* · authority 形 —— 段里最后一个 `//` 到 `@` 之间非空且不含 `/ ? #`(`jdbc:postgresql://svc:pw@…`、`//admin:pw@…`、`ssh://git@h`);
|
|
101
|
+
* · 冒号形 —— 段里最后一个 `/ ? #` 之后的那一截是 `user:pw`(两侧非空),且 `@` 后紧跟主机字符(`admin:pw@db:5432/app`、`postgres:/svc:pw@h`)。
|
|
102
|
+
* 引号 / 尖括号 / 反引号**不**断段(它们可以在口令里)。单遍:每个字符至多被一次向前扫描读到(段以上一个 `@` 为界、互不重叠)⇒ 线性,
|
|
103
|
+
* 不用回溯正则(codex r1:扫描正则在 `;` × 4 万 + `@` 上要 10 s)。
|
|
104
|
+
* 读者:展示侧 `redactSecrets`(`./config-fns`)对空白分隔的候选段与归一化形各问一次 —— 自由文本出门面(坐标 / 错误句 / 主机名标签)与身份面
|
|
105
|
+
* (`isDisclosableIdentity` 问的就是 `redactSecrets` 原样放不放行)同一条律。校验侧不读它:URL 座位的 userinfo 由 URL 形判官拒,自由文本写口
|
|
106
|
+
* 不因 userinfo 拒(契约未变)。负控:路径里的 `@`(`https://h/@scope/pkg`)、邮箱、`name@版本`、空 userinfo(`https://@h`)都不是。 */
|
|
107
|
+
export function carriesUserinfo(text) {
|
|
108
|
+
if (!text.includes("@"))
|
|
109
|
+
return false; // userinfo ends at an `@` in every form above — no `@`, no credential pair (and no parse)
|
|
110
|
+
if (httpUrlFlaws(text, ["userinfo"]).includes("userinfo"))
|
|
111
|
+
return true;
|
|
112
|
+
let floor = 0; // 上一个 `@` 之后:向前扫描不越过它 ⇒ 各段不重叠
|
|
113
|
+
for (let at = text.indexOf("@"); at !== -1; at = text.indexOf("@", at + 1)) {
|
|
114
|
+
let start = at;
|
|
115
|
+
while (start > floor && !isSpace(text[start - 1]))
|
|
116
|
+
start--;
|
|
117
|
+
floor = at + 1;
|
|
118
|
+
const run = text.slice(start, at);
|
|
119
|
+
const slashes = run.lastIndexOf("//");
|
|
120
|
+
if (slashes !== -1) {
|
|
121
|
+
const userinfo = run.slice(slashes + 2);
|
|
122
|
+
if (userinfo !== "" && !/[/?#]/.test(userinfo))
|
|
123
|
+
return true;
|
|
105
124
|
}
|
|
125
|
+
const tail = run.slice(Math.max(run.lastIndexOf("/"), run.lastIndexOf("?"), run.lastIndexOf("#")) + 1);
|
|
126
|
+
const colon = tail.indexOf(":");
|
|
127
|
+
const next = text[at + 1];
|
|
128
|
+
if (colon > 0 && colon < tail.length - 1 && next !== undefined && next !== "@" && !isSpace(next))
|
|
129
|
+
return true;
|
|
106
130
|
}
|
|
107
|
-
|
|
108
|
-
url = u;
|
|
109
|
-
return url.username !== "" || url.password !== "";
|
|
131
|
+
return false;
|
|
110
132
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHA-256 / HMAC-SHA256 的纯 JS 核(内部模块,不在 exports 表里)—— 同步、零依赖、browser-safe。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不是 `node:crypto`:读它的模块在纯 barrel 里(内容寻址 `sha256Hex`、按 scope 绑定的令牌 `./api/pull-token`),
|
|
5
|
+
* 浏览器侧的消费方 import 那只 barrel 拿 zod 型,`node:crypto` 会打断(或用 polyfill 撑胖)它的构建;Web Crypto 只有异步。
|
|
6
|
+
* 与 `node:crypto` 逐字节同 —— 测试里对拍。
|
|
7
|
+
*/
|
|
8
|
+
/** Encode a JS string to UTF-8 bytes (no Node Buffer / TextEncoder dependency required — but use
|
|
9
|
+
* TextEncoder when present for speed; it exists in both modern Node and browsers). */
|
|
10
|
+
export declare function utf8Bytes(s: string): Uint8Array;
|
|
11
|
+
/** SHA-256 of raw bytes → the 32-byte digest. */
|
|
12
|
+
export declare function sha256Digest(msg: Uint8Array): Uint8Array;
|
|
13
|
+
/** Bytes → lowercase hex. */
|
|
14
|
+
export declare function toHex(bytes: Uint8Array): string;
|
|
15
|
+
/** HMAC-SHA256(key, message) over UTF-8 strings → lowercase hex (RFC 2104;块长 64 字节,长于块长的密钥先哈希)。 */
|
|
16
|
+
export declare function hmacSha256Hex(key: string, message: string): string;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHA-256 / HMAC-SHA256 的纯 JS 核(内部模块,不在 exports 表里)—— 同步、零依赖、browser-safe。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不是 `node:crypto`:读它的模块在纯 barrel 里(内容寻址 `sha256Hex`、按 scope 绑定的令牌 `./api/pull-token`),
|
|
5
|
+
* 浏览器侧的消费方 import 那只 barrel 拿 zod 型,`node:crypto` 会打断(或用 polyfill 撑胖)它的构建;Web Crypto 只有异步。
|
|
6
|
+
* 与 `node:crypto` 逐字节同 —— 测试里对拍。
|
|
7
|
+
*/
|
|
8
|
+
const K = new Uint32Array([
|
|
9
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
10
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
11
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
12
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
13
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
14
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
15
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
16
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
17
|
+
]);
|
|
18
|
+
/** Encode a JS string to UTF-8 bytes (no Node Buffer / TextEncoder dependency required — but use
|
|
19
|
+
* TextEncoder when present for speed; it exists in both modern Node and browsers). */
|
|
20
|
+
export function utf8Bytes(s) {
|
|
21
|
+
if (typeof TextEncoder !== "undefined")
|
|
22
|
+
return new TextEncoder().encode(s);
|
|
23
|
+
// Minimal fallback encoder (very old runtimes only).
|
|
24
|
+
const out = [];
|
|
25
|
+
for (let i = 0; i < s.length; i++) {
|
|
26
|
+
let c = s.charCodeAt(i);
|
|
27
|
+
if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) {
|
|
28
|
+
const c2 = s.charCodeAt(i + 1);
|
|
29
|
+
if (c2 >= 0xdc00 && c2 <= 0xdfff) {
|
|
30
|
+
c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
|
|
31
|
+
i++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (c < 0x80)
|
|
35
|
+
out.push(c);
|
|
36
|
+
else if (c < 0x800)
|
|
37
|
+
out.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
|
|
38
|
+
else if (c < 0x10000)
|
|
39
|
+
out.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
|
|
40
|
+
else
|
|
41
|
+
out.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 0x3f), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
|
|
42
|
+
}
|
|
43
|
+
return Uint8Array.from(out);
|
|
44
|
+
}
|
|
45
|
+
const rotr = (x, n) => (x >>> n) | (x << (32 - n));
|
|
46
|
+
/** SHA-256 of raw bytes → the 32-byte digest. */
|
|
47
|
+
export function sha256Digest(msg) {
|
|
48
|
+
const bitLen = msg.length * 8;
|
|
49
|
+
// Pad: 0x80, then zeros, then 64-bit big-endian length, to a multiple of 64 bytes.
|
|
50
|
+
const withOne = msg.length + 1;
|
|
51
|
+
const total = withOne + ((56 - (withOne % 64) + 64) % 64) + 8;
|
|
52
|
+
const buf = new Uint8Array(total);
|
|
53
|
+
buf.set(msg);
|
|
54
|
+
buf[msg.length] = 0x80;
|
|
55
|
+
// 64-bit length, big-endian. bitLen fits in 53 bits safely for any realistic skill body.
|
|
56
|
+
const hi = Math.floor(bitLen / 0x100000000);
|
|
57
|
+
const lo = bitLen >>> 0;
|
|
58
|
+
buf[total - 8] = (hi >>> 24) & 0xff;
|
|
59
|
+
buf[total - 7] = (hi >>> 16) & 0xff;
|
|
60
|
+
buf[total - 6] = (hi >>> 8) & 0xff;
|
|
61
|
+
buf[total - 5] = hi & 0xff;
|
|
62
|
+
buf[total - 4] = (lo >>> 24) & 0xff;
|
|
63
|
+
buf[total - 3] = (lo >>> 16) & 0xff;
|
|
64
|
+
buf[total - 2] = (lo >>> 8) & 0xff;
|
|
65
|
+
buf[total - 1] = lo & 0xff;
|
|
66
|
+
let h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a;
|
|
67
|
+
let h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
|
|
68
|
+
const w = new Uint32Array(64);
|
|
69
|
+
for (let off = 0; off < total; off += 64) {
|
|
70
|
+
for (let i = 0; i < 16; i++) {
|
|
71
|
+
w[i] = (buf[off + i * 4] << 24) | (buf[off + i * 4 + 1] << 16) | (buf[off + i * 4 + 2] << 8) | buf[off + i * 4 + 3];
|
|
72
|
+
}
|
|
73
|
+
for (let i = 16; i < 64; i++) {
|
|
74
|
+
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);
|
|
75
|
+
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
|
|
76
|
+
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0;
|
|
77
|
+
}
|
|
78
|
+
let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
|
|
79
|
+
for (let i = 0; i < 64; i++) {
|
|
80
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
81
|
+
const ch = (e & f) ^ (~e & g);
|
|
82
|
+
const t1 = (h + S1 + ch + K[i] + w[i]) | 0;
|
|
83
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
84
|
+
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
85
|
+
const t2 = (S0 + maj) | 0;
|
|
86
|
+
h = g;
|
|
87
|
+
g = f;
|
|
88
|
+
f = e;
|
|
89
|
+
e = (d + t1) | 0;
|
|
90
|
+
d = c;
|
|
91
|
+
c = b;
|
|
92
|
+
b = a;
|
|
93
|
+
a = (t1 + t2) | 0;
|
|
94
|
+
}
|
|
95
|
+
h0 = (h0 + a) | 0;
|
|
96
|
+
h1 = (h1 + b) | 0;
|
|
97
|
+
h2 = (h2 + c) | 0;
|
|
98
|
+
h3 = (h3 + d) | 0;
|
|
99
|
+
h4 = (h4 + e) | 0;
|
|
100
|
+
h5 = (h5 + f) | 0;
|
|
101
|
+
h6 = (h6 + g) | 0;
|
|
102
|
+
h7 = (h7 + h) | 0;
|
|
103
|
+
}
|
|
104
|
+
const out = new Uint8Array(32);
|
|
105
|
+
[h0, h1, h2, h3, h4, h5, h6, h7].forEach((h, i) => {
|
|
106
|
+
out[i * 4] = (h >>> 24) & 0xff;
|
|
107
|
+
out[i * 4 + 1] = (h >>> 16) & 0xff;
|
|
108
|
+
out[i * 4 + 2] = (h >>> 8) & 0xff;
|
|
109
|
+
out[i * 4 + 3] = h & 0xff;
|
|
110
|
+
});
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/** Bytes → lowercase hex. */
|
|
114
|
+
export function toHex(bytes) {
|
|
115
|
+
let s = "";
|
|
116
|
+
for (const byte of bytes)
|
|
117
|
+
s += byte.toString(16).padStart(2, "0");
|
|
118
|
+
return s;
|
|
119
|
+
}
|
|
120
|
+
/** HMAC-SHA256(key, message) over UTF-8 strings → lowercase hex (RFC 2104;块长 64 字节,长于块长的密钥先哈希)。 */
|
|
121
|
+
export function hmacSha256Hex(key, message) {
|
|
122
|
+
let k = utf8Bytes(key);
|
|
123
|
+
if (k.length > 64)
|
|
124
|
+
k = sha256Digest(k);
|
|
125
|
+
const block = new Uint8Array(64);
|
|
126
|
+
block.set(k);
|
|
127
|
+
const ipad = block.map((x) => x ^ 0x36);
|
|
128
|
+
const opad = block.map((x) => x ^ 0x5c);
|
|
129
|
+
const msg = utf8Bytes(message);
|
|
130
|
+
const innerInput = new Uint8Array(64 + msg.length);
|
|
131
|
+
innerInput.set(ipad);
|
|
132
|
+
innerInput.set(msg, 64);
|
|
133
|
+
const innerHash = sha256Digest(innerInput);
|
|
134
|
+
const outerInput = new Uint8Array(64 + 32);
|
|
135
|
+
outerInput.set(opad);
|
|
136
|
+
outerInput.set(innerHash, 64);
|
|
137
|
+
return toHex(sha256Digest(outerInput));
|
|
138
|
+
}
|
package/dist/sha256.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Pure, dependency-free, **synchronous** SHA-256 over a UTF-8 string → lowercase hex.
|
|
3
3
|
*
|
|
4
4
|
* WHY a hand-rolled impl instead of `node:crypto`: this module sits in the PURE barrel
|
|
5
|
-
* (re-exported via skills-manifest.ts → resolve-roster.ts → index.ts).
|
|
5
|
+
* (re-exported via skills-manifest.ts → resolve-roster.ts → index.ts). web-admin's CLIENT components
|
|
6
6
|
* import that barrel for the zod types, so anything in it ships into the BROWSER bundle. A `node:crypto`
|
|
7
7
|
* import there breaks (or bloats with a polyfill) the client build — the exact blocker flagged in review.
|
|
8
8
|
* Web Crypto (`crypto.subtle.digest`) is async-only, but `skillContentHash` is a sync contract used in
|
|
@@ -12,5 +12,10 @@
|
|
|
12
12
|
* node:crypto in the tests — so the `sha256:<hex>` content-address the service re-hashes after pulling
|
|
13
13
|
* (tamper detection) still matches. This is content-addressing/integrity, NOT a secret/MAC.
|
|
14
14
|
*/
|
|
15
|
+
import { z } from "zod";
|
|
15
16
|
/** SHA-256 of a UTF-8 string → 64-char lowercase hex. */
|
|
16
17
|
export declare function sha256Hex(input: string): string;
|
|
18
|
+
/** The one spelling of a content digest on the wire: `sha256:` + 64 lowercase hex characters. */
|
|
19
|
+
export declare const SHA256_DIGEST_RE: RegExp;
|
|
20
|
+
/** {@link SHA256_DIGEST_RE} as a schema —— 本包每个装 `sha256:<hex>` 摘要的座位都引它(此前五处各写一遍同一条正则)。 */
|
|
21
|
+
export declare const Sha256Digest: z.ZodString;
|
package/dist/sha256.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Pure, dependency-free, **synchronous** SHA-256 over a UTF-8 string → lowercase hex.
|
|
3
3
|
*
|
|
4
4
|
* WHY a hand-rolled impl instead of `node:crypto`: this module sits in the PURE barrel
|
|
5
|
-
* (re-exported via skills-manifest.ts → resolve-roster.ts → index.ts).
|
|
5
|
+
* (re-exported via skills-manifest.ts → resolve-roster.ts → index.ts). web-admin's CLIENT components
|
|
6
6
|
* import that barrel for the zod types, so anything in it ships into the BROWSER bundle. A `node:crypto`
|
|
7
7
|
* import there breaks (or bloats with a polyfill) the client build — the exact blocker flagged in review.
|
|
8
8
|
* Web Crypto (`crypto.subtle.digest`) is async-only, but `skillContentHash` is a sync contract used in
|
|
@@ -12,103 +12,13 @@
|
|
|
12
12
|
* node:crypto in the tests — so the `sha256:<hex>` content-address the service re-hashes after pulling
|
|
13
13
|
* (tamper detection) still matches. This is content-addressing/integrity, NOT a secret/MAC.
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
18
|
-
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
19
|
-
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
20
|
-
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
21
|
-
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
22
|
-
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
23
|
-
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
24
|
-
]);
|
|
25
|
-
/** Encode a JS string to UTF-8 bytes (no Node Buffer / TextEncoder dependency required — but use
|
|
26
|
-
* TextEncoder when present for speed; it exists in both modern Node and browsers). */
|
|
27
|
-
function utf8Bytes(s) {
|
|
28
|
-
if (typeof TextEncoder !== "undefined")
|
|
29
|
-
return new TextEncoder().encode(s);
|
|
30
|
-
// Minimal fallback encoder (very old runtimes only).
|
|
31
|
-
const out = [];
|
|
32
|
-
for (let i = 0; i < s.length; i++) {
|
|
33
|
-
let c = s.charCodeAt(i);
|
|
34
|
-
if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) {
|
|
35
|
-
const c2 = s.charCodeAt(i + 1);
|
|
36
|
-
if (c2 >= 0xdc00 && c2 <= 0xdfff) {
|
|
37
|
-
c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
|
|
38
|
-
i++;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (c < 0x80)
|
|
42
|
-
out.push(c);
|
|
43
|
-
else if (c < 0x800)
|
|
44
|
-
out.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
|
|
45
|
-
else if (c < 0x10000)
|
|
46
|
-
out.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
|
|
47
|
-
else
|
|
48
|
-
out.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 0x3f), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
|
|
49
|
-
}
|
|
50
|
-
return Uint8Array.from(out);
|
|
51
|
-
}
|
|
52
|
-
const rotr = (x, n) => (x >>> n) | (x << (32 - n));
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import { sha256Digest, toHex, utf8Bytes } from "./sha256-core.js";
|
|
53
17
|
/** SHA-256 of a UTF-8 string → 64-char lowercase hex. */
|
|
54
18
|
export function sha256Hex(input) {
|
|
55
|
-
|
|
56
|
-
const bitLen = msg.length * 8;
|
|
57
|
-
// Pad: 0x80, then zeros, then 64-bit big-endian length, to a multiple of 64 bytes.
|
|
58
|
-
const withOne = msg.length + 1;
|
|
59
|
-
const total = withOne + ((56 - (withOne % 64) + 64) % 64) + 8;
|
|
60
|
-
const buf = new Uint8Array(total);
|
|
61
|
-
buf.set(msg);
|
|
62
|
-
buf[msg.length] = 0x80;
|
|
63
|
-
// 64-bit length, big-endian. bitLen fits in 53 bits safely for any realistic skill body.
|
|
64
|
-
const hi = Math.floor(bitLen / 0x100000000);
|
|
65
|
-
const lo = bitLen >>> 0;
|
|
66
|
-
buf[total - 8] = (hi >>> 24) & 0xff;
|
|
67
|
-
buf[total - 7] = (hi >>> 16) & 0xff;
|
|
68
|
-
buf[total - 6] = (hi >>> 8) & 0xff;
|
|
69
|
-
buf[total - 5] = hi & 0xff;
|
|
70
|
-
buf[total - 4] = (lo >>> 24) & 0xff;
|
|
71
|
-
buf[total - 3] = (lo >>> 16) & 0xff;
|
|
72
|
-
buf[total - 2] = (lo >>> 8) & 0xff;
|
|
73
|
-
buf[total - 1] = lo & 0xff;
|
|
74
|
-
let h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a;
|
|
75
|
-
let h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
|
|
76
|
-
const w = new Uint32Array(64);
|
|
77
|
-
for (let off = 0; off < total; off += 64) {
|
|
78
|
-
for (let i = 0; i < 16; i++) {
|
|
79
|
-
w[i] = (buf[off + i * 4] << 24) | (buf[off + i * 4 + 1] << 16) | (buf[off + i * 4 + 2] << 8) | buf[off + i * 4 + 3];
|
|
80
|
-
}
|
|
81
|
-
for (let i = 16; i < 64; i++) {
|
|
82
|
-
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);
|
|
83
|
-
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
|
|
84
|
-
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0;
|
|
85
|
-
}
|
|
86
|
-
let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
|
|
87
|
-
for (let i = 0; i < 64; i++) {
|
|
88
|
-
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
89
|
-
const ch = (e & f) ^ (~e & g);
|
|
90
|
-
const t1 = (h + S1 + ch + K[i] + w[i]) | 0;
|
|
91
|
-
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
92
|
-
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
93
|
-
const t2 = (S0 + maj) | 0;
|
|
94
|
-
h = g;
|
|
95
|
-
g = f;
|
|
96
|
-
f = e;
|
|
97
|
-
e = (d + t1) | 0;
|
|
98
|
-
d = c;
|
|
99
|
-
c = b;
|
|
100
|
-
b = a;
|
|
101
|
-
a = (t1 + t2) | 0;
|
|
102
|
-
}
|
|
103
|
-
h0 = (h0 + a) | 0;
|
|
104
|
-
h1 = (h1 + b) | 0;
|
|
105
|
-
h2 = (h2 + c) | 0;
|
|
106
|
-
h3 = (h3 + d) | 0;
|
|
107
|
-
h4 = (h4 + e) | 0;
|
|
108
|
-
h5 = (h5 + f) | 0;
|
|
109
|
-
h6 = (h6 + g) | 0;
|
|
110
|
-
h7 = (h7 + h) | 0;
|
|
111
|
-
}
|
|
112
|
-
const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
|
|
113
|
-
return toHex(h0) + toHex(h1) + toHex(h2) + toHex(h3) + toHex(h4) + toHex(h5) + toHex(h6) + toHex(h7);
|
|
19
|
+
return toHex(sha256Digest(utf8Bytes(input)));
|
|
114
20
|
}
|
|
21
|
+
/** The one spelling of a content digest on the wire: `sha256:` + 64 lowercase hex characters. */
|
|
22
|
+
export const SHA256_DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
|
|
23
|
+
/** {@link SHA256_DIGEST_RE} as a schema —— 本包每个装 `sha256:<hex>` 摘要的座位都引它(此前五处各写一遍同一条正则)。 */
|
|
24
|
+
export const Sha256Digest = z.string().regex(SHA256_DIGEST_RE);
|
package/dist/skills-manifest.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* — distinct from the FNV `stableHash` in hash.ts, which is a fast non-crypto change-detector for ETags.
|
|
10
10
|
*
|
|
11
11
|
* The digest is a PURE-JS sha256 (./sha256.ts), NOT `node:crypto`: this module is re-exported through the
|
|
12
|
-
* package barrel, which
|
|
12
|
+
* package barrel, which web-admin's CLIENT components import for the zod types — so a `node:crypto`
|
|
13
13
|
* import here would leak `node:*` into the browser bundle (the review-flagged blocker). Output is byte-identical to
|
|
14
14
|
* `createHash("sha256")`, so the `sha256:<hex>` content-address the service verifies still matches.
|
|
15
15
|
*/
|