@hasna/skills 0.10.39 → 0.10.42
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/README.md +41 -0
- package/bin/index.js +426 -115
- package/bin/mcp.js +138 -19
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/reviewed-runtime-lock.d.ts +17 -0
- package/dist/cli/commands/runtime-copyfile.d.ts +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +163 -31
- package/dist/lib/agent-discovery.d.ts +1 -1
- package/dist/lib/agent-integration.d.ts +1 -1
- package/dist/lib/codex-settings-witness.d.ts +13 -0
- package/dist/sdk/index.js +161 -31
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.10.
|
|
36871
|
+
version: "0.10.42",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -40019,8 +40019,8 @@ function assertAgentPolicyCollections(policy) {
|
|
|
40019
40019
|
text(source.path);
|
|
40020
40020
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
40021
40021
|
if (source.hashMode !== undefined)
|
|
40022
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
40023
|
-
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3")
|
|
40022
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
40023
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
40024
40024
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve3(source.path) === source.path && basename2(source.path) === "config.toml");
|
|
40025
40025
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
40026
40026
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve3(source.path) === source.path && basename2(source.path) === "settings.json");
|
|
@@ -52223,6 +52223,7 @@ var init_codex_mcp_discovery_projection = () => {};
|
|
|
52223
52223
|
|
|
52224
52224
|
// src/lib/codex-settings-witness.ts
|
|
52225
52225
|
import { createHash as createHash12 } from "crypto";
|
|
52226
|
+
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
52226
52227
|
function need4(value) {
|
|
52227
52228
|
if (!value)
|
|
52228
52229
|
throw new Error("Invalid Codex settings witness");
|
|
@@ -52328,7 +52329,7 @@ function disablesOneSkill(entry) {
|
|
|
52328
52329
|
return selector.length === 1 && typeof entry[selector[0]] === "string" && entry[selector[0]].length > 0;
|
|
52329
52330
|
}
|
|
52330
52331
|
function inference(target, version) {
|
|
52331
|
-
const keys2 = version
|
|
52332
|
+
const keys2 = version >= 3 ? [...INFERENCE, "service_tier", "plan_mode_reasoning_effort", "personality"] : INFERENCE;
|
|
52332
52333
|
for (const key of keys2)
|
|
52333
52334
|
if (Object.hasOwn(target, key)) {
|
|
52334
52335
|
const value = target[key];
|
|
@@ -52336,7 +52337,7 @@ function inference(target, version) {
|
|
|
52336
52337
|
need4(typeof value === "string" && MODEL.test(value));
|
|
52337
52338
|
else if (key === "personality")
|
|
52338
52339
|
need4(typeof value === "string" && ["none", "friendly", "pragmatic"].includes(value));
|
|
52339
|
-
else if (version
|
|
52340
|
+
else if (version >= 3 && key !== "model_verbosity") {
|
|
52340
52341
|
need4(typeof value === "string" && value.length <= 128 && !/[\x00-\x1f\x7f]/.test(value));
|
|
52341
52342
|
if (key !== "service_tier")
|
|
52342
52343
|
need4(value.length > 0);
|
|
@@ -52345,6 +52346,115 @@ function inference(target, version) {
|
|
|
52345
52346
|
delete target[key];
|
|
52346
52347
|
}
|
|
52347
52348
|
}
|
|
52349
|
+
function projectModelAvailabilityNux(config, text2) {
|
|
52350
|
+
const tui = config.tui, counts = isRecord3(tui) ? tui.model_availability_nux : undefined;
|
|
52351
|
+
if (isRecord3(tui) && !Object.keys(tui).length) {
|
|
52352
|
+
delete config.tui;
|
|
52353
|
+
return { config, numericSource: text2 };
|
|
52354
|
+
}
|
|
52355
|
+
if (!isRecord3(counts) || Object.keys(counts).length > 256 || !Object.values(counts).every((value) => typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 4294967295))
|
|
52356
|
+
return { config, numericSource: text2 };
|
|
52357
|
+
const expected = structuredClone(config);
|
|
52358
|
+
delete expected.tui.model_availability_nux;
|
|
52359
|
+
const stripEmptyTui = (value) => {
|
|
52360
|
+
if (isRecord3(value.tui) && !Object.keys(value.tui).length)
|
|
52361
|
+
delete value.tui;
|
|
52362
|
+
};
|
|
52363
|
+
stripEmptyTui(expected);
|
|
52364
|
+
const headers = [];
|
|
52365
|
+
const spans = [];
|
|
52366
|
+
const integer = /^(?:\+?(?:0|[1-9](?:_?\d)*)|0x[0-9a-fA-F](?:_?[0-9a-fA-F])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*)$/;
|
|
52367
|
+
const counterIntegers = (span) => {
|
|
52368
|
+
let values2 = 0;
|
|
52369
|
+
for (let at2 = 0;at2 < span.length; ) {
|
|
52370
|
+
const char = span[at2];
|
|
52371
|
+
if (char === "#") {
|
|
52372
|
+
while (at2 < span.length && span[at2] !== `
|
|
52373
|
+
`)
|
|
52374
|
+
at2++;
|
|
52375
|
+
continue;
|
|
52376
|
+
}
|
|
52377
|
+
if (char === '"' || char === "'") {
|
|
52378
|
+
const quote = char;
|
|
52379
|
+
at2++;
|
|
52380
|
+
while (at2 < span.length) {
|
|
52381
|
+
if (quote === '"' && span[at2] === "\\") {
|
|
52382
|
+
at2 += 2;
|
|
52383
|
+
continue;
|
|
52384
|
+
}
|
|
52385
|
+
if (span[at2++] === quote)
|
|
52386
|
+
break;
|
|
52387
|
+
}
|
|
52388
|
+
continue;
|
|
52389
|
+
}
|
|
52390
|
+
if (char !== "=") {
|
|
52391
|
+
at2++;
|
|
52392
|
+
continue;
|
|
52393
|
+
}
|
|
52394
|
+
at2++;
|
|
52395
|
+
while (at2 < span.length && /\s/.test(span[at2]))
|
|
52396
|
+
at2++;
|
|
52397
|
+
if (span[at2] === "{") {
|
|
52398
|
+
at2++;
|
|
52399
|
+
continue;
|
|
52400
|
+
}
|
|
52401
|
+
const start = at2;
|
|
52402
|
+
while (at2 < span.length && !/[\s,\]{}#]/.test(span[at2]))
|
|
52403
|
+
at2++;
|
|
52404
|
+
if (!integer.test(span.slice(start, at2)))
|
|
52405
|
+
return false;
|
|
52406
|
+
values2++;
|
|
52407
|
+
}
|
|
52408
|
+
return values2 === Object.keys(counts).length;
|
|
52409
|
+
};
|
|
52410
|
+
let attempts = 0;
|
|
52411
|
+
const realPrefix = (end) => {
|
|
52412
|
+
if (++attempts > 256)
|
|
52413
|
+
return false;
|
|
52414
|
+
try {
|
|
52415
|
+
Bun.TOML.parse(text2.slice(0, end));
|
|
52416
|
+
return true;
|
|
52417
|
+
} catch {
|
|
52418
|
+
return false;
|
|
52419
|
+
}
|
|
52420
|
+
};
|
|
52421
|
+
for (const match of text2.matchAll(/^[ \t]*\[[^\r\n]+\][ \t]*(?:#[^\r\n]*)?(?:\r?\n|$)/gm)) {
|
|
52422
|
+
const start = match.index, end = start + match[0].length;
|
|
52423
|
+
if (realPrefix(end))
|
|
52424
|
+
headers.push({ start, end });
|
|
52425
|
+
}
|
|
52426
|
+
for (let at2 = 0;at2 < headers.length; at2++) {
|
|
52427
|
+
const header = headers[at2];
|
|
52428
|
+
let parsed;
|
|
52429
|
+
try {
|
|
52430
|
+
parsed = Bun.TOML.parse(text2.slice(header.start, header.end));
|
|
52431
|
+
} catch {
|
|
52432
|
+
continue;
|
|
52433
|
+
}
|
|
52434
|
+
if (isRecord3(parsed.tui) && isRecord3(parsed.tui.model_availability_nux) && !Object.keys(parsed.tui.model_availability_nux).length && Object.keys(parsed).length === 1 && Object.keys(parsed.tui).length === 1)
|
|
52435
|
+
spans.push({ start: header.start, end: headers[at2 + 1]?.start ?? text2.length });
|
|
52436
|
+
}
|
|
52437
|
+
for (const match of text2.matchAll(/^[ \t]*(?:(?:tui|"tui"|'tui')[ \t]*\.[ \t]*)?(?:model_availability_nux|"model_availability_nux"|'model_availability_nux')[ \t]*=[ \t]*\{[^\r\n]*(?:\r?\n|$)/gm)) {
|
|
52438
|
+
const start = match.index, end = start + match[0].length;
|
|
52439
|
+
if (realPrefix(end))
|
|
52440
|
+
spans.push({ start, end });
|
|
52441
|
+
}
|
|
52442
|
+
for (const { start, end } of spans) {
|
|
52443
|
+
if (!counterIntegers(text2.slice(start, end)))
|
|
52444
|
+
continue;
|
|
52445
|
+
const remaining = text2.slice(0, start) + text2.slice(end);
|
|
52446
|
+
let parsed;
|
|
52447
|
+
try {
|
|
52448
|
+
parsed = Bun.TOML.parse(remaining);
|
|
52449
|
+
} catch {
|
|
52450
|
+
continue;
|
|
52451
|
+
}
|
|
52452
|
+
stripEmptyTui(parsed);
|
|
52453
|
+
if (isDeepStrictEqual4(parsed, expected))
|
|
52454
|
+
return { config: expected, numericSource: remaining };
|
|
52455
|
+
}
|
|
52456
|
+
return { config, numericSource: text2 };
|
|
52457
|
+
}
|
|
52348
52458
|
function settingsDigest2(text2, version = 2) {
|
|
52349
52459
|
let config;
|
|
52350
52460
|
try {
|
|
@@ -52353,7 +52463,8 @@ function settingsDigest2(text2, version = 2) {
|
|
|
52353
52463
|
throw new Error("Invalid Codex settings witness");
|
|
52354
52464
|
}
|
|
52355
52465
|
need4(isRecord3(config));
|
|
52356
|
-
const
|
|
52466
|
+
const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
|
|
52467
|
+
const projected = version === 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
|
|
52357
52468
|
normalize3(projected.config, version);
|
|
52358
52469
|
return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical4(projected.config), numericTokens2(projected.numericSource)])}`);
|
|
52359
52470
|
}
|
|
@@ -52398,6 +52509,25 @@ function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
|
|
|
52398
52509
|
throw new Error("Codex settings outside the reviewed V3 contract changed; explicit discovery review required");
|
|
52399
52510
|
return { path: previous.path, hashMode: "codex-settings-v3", sha256: sha2562 };
|
|
52400
52511
|
}
|
|
52512
|
+
function hashCodexSettingsReplacementV4(text2, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
52513
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
|
|
52514
|
+
budget.remaining -= Buffer.byteLength(text2);
|
|
52515
|
+
return settingsDigest2(text2, 4);
|
|
52516
|
+
}
|
|
52517
|
+
function captureCodexSettingsV4(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
52518
|
+
return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget, "config.toml"), 4) };
|
|
52519
|
+
}
|
|
52520
|
+
function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
52521
|
+
need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
|
|
52522
|
+
const budget = { remaining: 2 * 1024 * 1024 };
|
|
52523
|
+
const before2 = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
|
|
52524
|
+
const original = previous.hashMode === "codex-settings-v3" ? settingsDigest2(before2, 3) : previous.hashMode === "codex-settings-v2" ? settingsDigest2(before2, 2) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before2) : sha2(before2);
|
|
52525
|
+
need4(original === previous.sha256);
|
|
52526
|
+
const sha2562 = settingsDigest2(current, 4);
|
|
52527
|
+
if (settingsDigest2(before2, 4) !== sha2562)
|
|
52528
|
+
throw new Error("Codex settings outside the reviewed V4 contract changed; explicit discovery review required");
|
|
52529
|
+
return { path: previous.path, hashMode: "codex-settings-v4", sha256: sha2562 };
|
|
52530
|
+
}
|
|
52401
52531
|
var sha2 = (text2) => createHash12("sha256").update(text2).digest("hex"), EFFORT2, VERBOSITY2, MODEL, CODEX_DISCOVERY_PROJECTION_FIELDS, INFERENCE;
|
|
52402
52532
|
var init_codex_settings_witness = __esm(() => {
|
|
52403
52533
|
init_codex_settings_witness_v1();
|
|
@@ -52540,7 +52670,7 @@ var init_agent_bridge = __esm(() => {
|
|
|
52540
52670
|
});
|
|
52541
52671
|
|
|
52542
52672
|
// src/lib/agent-codex.ts
|
|
52543
|
-
import { isDeepStrictEqual as
|
|
52673
|
+
import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
|
|
52544
52674
|
function splitTopLevel(value, delimiter2) {
|
|
52545
52675
|
const result2 = [];
|
|
52546
52676
|
let start = 0, square = 0, curly = 0, quote = null, escaped = false;
|
|
@@ -52669,7 +52799,7 @@ function normalizeCodexInlinePathConfig(text2) {
|
|
|
52669
52799
|
const expectedWithoutConfig = structuredClone(before2);
|
|
52670
52800
|
delete expectedWithoutConfig.skills.config;
|
|
52671
52801
|
try {
|
|
52672
|
-
if (
|
|
52802
|
+
if (isDeepStrictEqual5(Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, "")), expectedWithoutConfig))
|
|
52673
52803
|
return text2;
|
|
52674
52804
|
} catch {}
|
|
52675
52805
|
const matches2 = [];
|
|
@@ -52722,7 +52852,7 @@ function normalizeCodexInlinePathConfig(text2) {
|
|
|
52722
52852
|
try {
|
|
52723
52853
|
const after2 = Bun.TOML.parse(candidate);
|
|
52724
52854
|
const expected = structuredClone(before2);
|
|
52725
|
-
if (
|
|
52855
|
+
if (isDeepStrictEqual5(after2, expected))
|
|
52726
52856
|
matches2.push(candidate);
|
|
52727
52857
|
} catch {}
|
|
52728
52858
|
}
|
|
@@ -52738,11 +52868,11 @@ function assertCodexPathConfigEditable(text2) {
|
|
|
52738
52868
|
delete expected.skills.config;
|
|
52739
52869
|
try {
|
|
52740
52870
|
const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
52741
|
-
if (
|
|
52871
|
+
if (isDeepStrictEqual5(withoutSections, expected))
|
|
52742
52872
|
return;
|
|
52743
52873
|
const normalized = normalizeCodexInlinePathConfig(text2);
|
|
52744
52874
|
const withoutNormalizedSections = Bun.TOML.parse(normalized.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
52745
|
-
if (
|
|
52875
|
+
if (isDeepStrictEqual5(withoutNormalizedSections, expected))
|
|
52746
52876
|
return;
|
|
52747
52877
|
} catch {}
|
|
52748
52878
|
throw codexPathConfigRefusal();
|
|
@@ -52780,7 +52910,7 @@ function disableCodexBundledSkills(text2) {
|
|
|
52780
52910
|
const candidates = new Set;
|
|
52781
52911
|
const consider = (candidate) => {
|
|
52782
52912
|
try {
|
|
52783
|
-
if (
|
|
52913
|
+
if (isDeepStrictEqual5(Bun.TOML.parse(candidate), expected))
|
|
52784
52914
|
candidates.add(candidate);
|
|
52785
52915
|
} catch {}
|
|
52786
52916
|
};
|
|
@@ -52981,7 +53111,7 @@ var init_codex_hook_trust_files = () => {};
|
|
|
52981
53111
|
|
|
52982
53112
|
// src/lib/agent-integration.ts
|
|
52983
53113
|
import { createHash as createHash15, randomUUID as randomUUID5 } from "crypto";
|
|
52984
|
-
import { isDeepStrictEqual as
|
|
53114
|
+
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
52985
53115
|
import { existsSync as existsSync13, lstatSync as lstatSync18, statSync as statSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync14, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync8, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, chmodSync, openSync as openSync9, closeSync as closeSync9, fsyncSync as fsyncSync3, fstatSync as fstatSync9, readSync as readSync7, constants as constants10, linkSync as linkSync2 } from "fs";
|
|
52986
53116
|
import { dirname as dirname13, join as join26, resolve as resolve13, relative as relative6, isAbsolute as isAbsolute12, sep as sep6 } from "path";
|
|
52987
53117
|
import { homedir as homedir3 } from "os";
|
|
@@ -53837,7 +53967,7 @@ function projectedHookExecutable(command, expectedResolved) {
|
|
|
53837
53967
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53838
53968
|
const recheck = () => {
|
|
53839
53969
|
try {
|
|
53840
|
-
if (realpathSync8(command) !== resolved || linkIdentity(lstatSync18(command, { bigint: true })) !== linkIdentity(link2) || !
|
|
53970
|
+
if (realpathSync8(command) !== resolved || linkIdentity(lstatSync18(command, { bigint: true })) !== linkIdentity(link2) || !isDeepStrictEqual6(parents2(), parentIdentity) || !isDeepStrictEqual6(parents2(resolved), resolvedParentIdentity))
|
|
53841
53971
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53842
53972
|
projectedHookAcl(cli.file, cli.stat);
|
|
53843
53973
|
projectedHookAcl(manifest.file, manifest.stat);
|
|
@@ -53977,7 +54107,7 @@ function atomicWrite2(path, content) {
|
|
|
53977
54107
|
}
|
|
53978
54108
|
}
|
|
53979
54109
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
53980
|
-
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3].includes(options.targetCodexVersion)))
|
|
54110
|
+
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3, 4].includes(options.targetCodexVersion)))
|
|
53981
54111
|
throw new Error("Invalid Codex witness target version");
|
|
53982
54112
|
const home = resolve13(options.home ?? homedir3()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
|
|
53983
54113
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir3);
|
|
@@ -54000,21 +54130,21 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
54000
54130
|
throw new Error("Settings witness upgrade requires a preserved settings preimage");
|
|
54001
54131
|
const replaced = [previous];
|
|
54002
54132
|
if (options.agent === "codex") {
|
|
54003
|
-
if (previous.hashMode !== undefined && previous.hashMode !== "bytes" && previous.hashMode !== "codex-settings-v1" && !(options.targetCodexVersion === 3 && previous.hashMode === "codex-settings-v2"))
|
|
54133
|
+
if (previous.hashMode !== undefined && previous.hashMode !== "bytes" && previous.hashMode !== "codex-settings-v1" && !((options.targetCodexVersion === 3 || options.targetCodexVersion === 4) && previous.hashMode === "codex-settings-v2") && !(options.targetCodexVersion === 4 && previous.hashMode === "codex-settings-v3"))
|
|
54004
54134
|
throw new Error("Codex settings witness upgrade requires an eligible preserved configuration witness");
|
|
54005
54135
|
const preimage = readCodexSettingsPreimage(options.reviewedPreimage);
|
|
54006
54136
|
for (const source of binding.sources) {
|
|
54007
54137
|
if (source === previous || source.path !== configPath)
|
|
54008
54138
|
continue;
|
|
54009
54139
|
const fields = source.fields;
|
|
54010
|
-
if (source.hashMode !== undefined || source.format !== "toml" || !Array.isArray(fields) || !
|
|
54140
|
+
if (source.hashMode !== undefined || source.format !== "toml" || !Array.isArray(fields) || !isDeepStrictEqual6(fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]) || typeof source.sha256 !== "string")
|
|
54011
54141
|
throw new Error("Codex settings witness upgrade found an unrecognized configuration witness");
|
|
54012
54142
|
if (projectNativeDiscoveryFields(preimage, "toml", fields, configPath) !== source.sha256)
|
|
54013
54143
|
throw new Error("Reviewed preimage does not explain the Codex discovery projection witness");
|
|
54014
54144
|
replaced.push(source);
|
|
54015
54145
|
}
|
|
54016
54146
|
}
|
|
54017
|
-
const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
54147
|
+
const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
54018
54148
|
const rebound = [];
|
|
54019
54149
|
for (const source of binding.sources) {
|
|
54020
54150
|
if (!replaced.includes(source)) {
|
|
@@ -54060,7 +54190,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
54060
54190
|
const upgrade = plan.settingsWitnessUpgrade;
|
|
54061
54191
|
if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
|
|
54062
54192
|
throw new Error("Invalid settings witness upgrade plan");
|
|
54063
|
-
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
54193
|
+
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, ...upgrade.agent === "codex" ? { targetCodexVersion: upgrade.toHashMode === "codex-settings-v4" ? 4 : upgrade.toHashMode === "codex-settings-v3" ? 3 : 2 } : {}, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
54064
54194
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
|
|
54065
54195
|
throw new Error("Settings witness upgrade plan changed");
|
|
54066
54196
|
provenDiscovery = verified.discoveryBefore[0];
|
|
@@ -57400,7 +57530,7 @@ var init_claude_marketplace_registry = __esm(() => {
|
|
|
57400
57530
|
});
|
|
57401
57531
|
|
|
57402
57532
|
// src/lib/codex-hook-trust-layout.ts
|
|
57403
|
-
import { isDeepStrictEqual as
|
|
57533
|
+
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
57404
57534
|
function codexTrustTextWitness(text2, keys2, canonicalize = true) {
|
|
57405
57535
|
const parsed = Bun.TOML.parse(text2), wanted = new Set(keys2);
|
|
57406
57536
|
const headers = [...text2.matchAll(/^[ \t]*(\[[^\r\n]+\])[ \t]*(#[^\r\n]*)?(?:\r?\n|$)/gm)];
|
|
@@ -57465,7 +57595,7 @@ function codexTrustTextWitness(text2, keys2, canonicalize = true) {
|
|
|
57465
57595
|
if (expected.hooks)
|
|
57466
57596
|
delete expected.hooks.state;
|
|
57467
57597
|
try {
|
|
57468
|
-
if (!
|
|
57598
|
+
if (!isDeepStrictEqual7(prune(Bun.TOML.parse(noState)), prune(expected)))
|
|
57469
57599
|
throw fail3();
|
|
57470
57600
|
} catch {
|
|
57471
57601
|
throw fail3();
|
|
@@ -57535,13 +57665,13 @@ var init_codex_hook_trust_layout = () => {};
|
|
|
57535
57665
|
|
|
57536
57666
|
// src/lib/codex-hook-additions-review.ts
|
|
57537
57667
|
import { isAbsolute as isAbsolute17 } from "path";
|
|
57538
|
-
import { isDeepStrictEqual as
|
|
57668
|
+
import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
|
|
57539
57669
|
function exactKeys2(value, keys2) {
|
|
57540
|
-
need5(value && typeof value === "object" && !Array.isArray(value) &&
|
|
57670
|
+
need5(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual8(Object.keys(value).sort(), [...keys2].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
57541
57671
|
}
|
|
57542
57672
|
function readReviewedAdditionTrustStates(proof, before2, current, journalHooks) {
|
|
57543
57673
|
const captured = proofs.get(proof);
|
|
57544
|
-
need5(!!captured && captured.before.file === before2.file && captured.before.sha256 === before2.sha256 && captured.current.file === current.file && captured.current.sha256 === current.sha256 &&
|
|
57674
|
+
need5(!!captured && captured.before.file === before2.file && captured.before.sha256 === before2.sha256 && captured.current.file === current.file && captured.current.sha256 === current.sha256 && isDeepStrictEqual8(captured.journalHooks, journalHooks.map(({ key, currentHash }) => ({ key, currentHash }))), "RECONCILE_ADDITIONS_PROOF_CHANGED");
|
|
57545
57675
|
captured.recheck();
|
|
57546
57676
|
unchanged2(before2);
|
|
57547
57677
|
unchanged2(current);
|
|
@@ -57564,7 +57694,7 @@ function reviewNativeHookAdditions(options) {
|
|
|
57564
57694
|
expectedProducer.hooks.state ??= {};
|
|
57565
57695
|
for (const hook of options.journalHooks)
|
|
57566
57696
|
expectedProducer.hooks.state[hook.key] = { ...expectedProducer.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
57567
|
-
need5(
|
|
57697
|
+
need5(isDeepStrictEqual8(expectedProducer, producer) && codexTrustReconcileWitness(options.configBefore.text, originalKeys) === codexTrustReconcileWitness(producerConfig.text, originalKeys) && codexTrustUnmanagedSemantic(options.configBefore.text, originalKeys) === codexTrustUnmanagedSemantic(producerConfig.text, originalKeys), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
57568
57698
|
need5(Array.isArray(review.additions) && review.additions.length > 0 && review.additions.length <= 20, "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
57569
57699
|
const expectedHooks = JSON.parse(options.hooksBefore.text), currentHooks = JSON.parse(options.hooksCurrent.text), expectedConfig = structuredClone(producer);
|
|
57570
57700
|
need5(expectedHooks.hooks && typeof expectedHooks.hooks === "object" && !Array.isArray(expectedHooks.hooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
@@ -57593,9 +57723,9 @@ function reviewNativeHookAdditions(options) {
|
|
|
57593
57723
|
expectedHooks.hooks[event] = [...groups, { ...event === "PreToolUse" ? { matcher: hook.matcher } : {}, hooks: [{ type: "command", command: hook.command, timeout: hook.timeoutSec }] }];
|
|
57594
57724
|
expectedConfig.hooks.state[key] = trustState;
|
|
57595
57725
|
}
|
|
57596
|
-
need5(
|
|
57726
|
+
need5(isDeepStrictEqual8(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
57597
57727
|
const keys2 = added.map((hook) => hook.key);
|
|
57598
|
-
need5(
|
|
57728
|
+
need5(isDeepStrictEqual8(expectedConfig, structure(options.configCurrent.text)) && codexTrustReconcileWitness(producerConfig.text, keys2) === codexTrustReconcileWitness(options.configCurrent.text, keys2) && codexTrustUnmanagedSemantic(producerConfig.text, keys2) === codexTrustUnmanagedSemantic(options.configCurrent.text, keys2), "RECONCILE_ADDITIONS_CONFIG");
|
|
57599
57729
|
const inputs = [reviewFile, producerConfig, producerHooks, options.intent, options.policy, options.configBefore, options.configCurrent, options.hooksBefore, options.hooksCurrent];
|
|
57600
57730
|
const recheck = () => {
|
|
57601
57731
|
for (const input of inputs)
|
|
@@ -57618,7 +57748,7 @@ var init_codex_hook_additions_review = __esm(() => {
|
|
|
57618
57748
|
|
|
57619
57749
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
57620
57750
|
import { createHash as createHash19 } from "crypto";
|
|
57621
|
-
import { isDeepStrictEqual as
|
|
57751
|
+
import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
|
|
57622
57752
|
function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, additions) {
|
|
57623
57753
|
const journalHooks = hooks.map(({ key, currentHash }) => ({ key, currentHash }));
|
|
57624
57754
|
const additionStates = additions ? readReviewedAdditionTrustStates(additions, before2, current, journalHooks) : [];
|
|
@@ -57632,11 +57762,11 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
|
|
|
57632
57762
|
need5(binding.agent === "codex" && binding.method === "reviewed" && raw.length === 1 && raw[0].sha256 === before2.sha256 && before2.sha256 !== current.sha256, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
|
|
57633
57763
|
need5(raw[0].format === undefined && raw[0].fields === undefined && raw[0].managedPlugins === undefined, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
|
|
57634
57764
|
const typed = binding.sources.filter((source) => source.path === current.file && source.format === "toml");
|
|
57635
|
-
need5(typed.length === 1 && typed[0].hashMode === undefined &&
|
|
57765
|
+
need5(typed.length === 1 && typed[0].hashMode === undefined && isDeepStrictEqual9(typed[0].fields, fields), "RECONCILE_DISCOVERY_TYPED_WITNESS");
|
|
57636
57766
|
const original = structure2(before2.text), actual = structure2(current.text);
|
|
57637
57767
|
const project = (value) => Object.fromEntries(fields.map((field2) => [field2, value[field2] ?? null]));
|
|
57638
57768
|
const projection = sha4(JSON.stringify(project(original)));
|
|
57639
|
-
need5(typed[0].sha256 === projection && sha4(JSON.stringify(project(actual))) === projection &&
|
|
57769
|
+
need5(typed[0].sha256 === projection && sha4(JSON.stringify(project(actual))) === projection && isDeepStrictEqual9(project(original), project(actual)), "RECONCILE_DISCOVERY_TYPED_WITNESS");
|
|
57640
57770
|
const keys2 = [...journalHooks.map((hook) => hook.key), ...additionStates.map((hook) => hook.key)];
|
|
57641
57771
|
need5(journalHooks.length > 0 && keys2.length <= 100 && new Set(keys2).size === keys2.length && journalHooks.every((hook) => typeof hook.key === "string" && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash)), "RECONCILE_DISCOVERY_TRUST_WITNESS");
|
|
57642
57772
|
const expected = structuredClone(original);
|
|
@@ -57646,7 +57776,7 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
|
|
|
57646
57776
|
expected.hooks.state[hook.key] = { ...expected.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
57647
57777
|
for (const { key, state } of additionStates)
|
|
57648
57778
|
expected.hooks.state[key] = { ...state };
|
|
57649
|
-
need5(
|
|
57779
|
+
need5(isDeepStrictEqual9(expected, actual) && codexTrustReconcileWitness(before2.text, keys2) === codexTrustReconcileWitness(current.text, keys2) && codexTrustUnmanagedSemantic(before2.text, keys2) === codexTrustUnmanagedSemantic(current.text, keys2), "RECONCILE_DISCOVERY_TRUST_WITNESS");
|
|
57650
57780
|
unchanged2(before2);
|
|
57651
57781
|
unchanged2(current);
|
|
57652
57782
|
const witness = Object.freeze({ configPath: current.file, beforeSha256: before2.sha256, currentSha256: current.sha256, discoverySha256: sha4(JSON.stringify(binding)), typedProjectionSha256: projection });
|
|
@@ -57681,7 +57811,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
|
|
|
57681
57811
|
import { createHash as createHash20 } from "crypto";
|
|
57682
57812
|
import { lstatSync as lstatSync23, readFileSync as readFileSync19, readdirSync as readdirSync11, realpathSync as realpathSync9 } from "fs";
|
|
57683
57813
|
import { basename as basename10, dirname as dirname18, isAbsolute as isAbsolute18, join as join35, resolve as resolve18, sep as sep7 } from "path";
|
|
57684
|
-
import { isDeepStrictEqual as
|
|
57814
|
+
import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
|
|
57685
57815
|
function parseConfig(text3, path, toml = false) {
|
|
57686
57816
|
try {
|
|
57687
57817
|
const value = toml ? Bun.TOML.parse(text3) : JSON.parse(text3);
|
|
@@ -57720,19 +57850,19 @@ function projectNativeDiscoveryFields(text3, format, fields2, path) {
|
|
|
57720
57850
|
return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object5[field2] ?? null]))));
|
|
57721
57851
|
}
|
|
57722
57852
|
function isSupersededCodexProjection(agent, configPath, source) {
|
|
57723
|
-
return agent === "codex" && source.path === configPath && source.format === "toml" &&
|
|
57853
|
+
return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual10(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
|
|
57724
57854
|
}
|
|
57725
57855
|
function supersedesCodexProjection(sources, configPath) {
|
|
57726
|
-
return sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3"));
|
|
57856
|
+
return sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")));
|
|
57727
57857
|
}
|
|
57728
57858
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
57729
|
-
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3") {
|
|
57859
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) {
|
|
57730
57860
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
57731
57861
|
throw new Error("Codex settings witnesses require exact typed metadata");
|
|
57732
|
-
const current = (source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
|
|
57862
|
+
const current = (source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
|
|
57733
57863
|
if (current !== source.sha256)
|
|
57734
57864
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
57735
|
-
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
|
|
57865
|
+
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
|
|
57736
57866
|
}
|
|
57737
57867
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
57738
57868
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
@@ -57828,7 +57958,7 @@ function captureRetiredCodexDiscovery(binding, controls, rules) {
|
|
|
57828
57958
|
return current;
|
|
57829
57959
|
const installation = codexInstallationRoots(binding);
|
|
57830
57960
|
const directories = (binding.directories ?? []).filter((directory) => !installation.some((root2) => directory.path === root2 || directory.path.startsWith(root2 + sep7) || root2.startsWith(directory.path + sep7)));
|
|
57831
|
-
if (previous &&
|
|
57961
|
+
if (previous && isDeepStrictEqual10(previous.roots, roots) && isDeepStrictEqual10(previous.parents, parents3) && isDeepStrictEqual10(previous.directories.map((item) => item.path), directories.map((item) => item.path)))
|
|
57832
57962
|
return { ...current, codexRetiredMaterializations: previous };
|
|
57833
57963
|
return { ...current, codexRetiredMaterializations: { roots, parents: parents3, directories: captureDiscoveryDirectoryProjection(directories.map((item) => item.path), roots) } };
|
|
57834
57964
|
}
|
|
@@ -57904,7 +58034,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
57904
58034
|
if (omitted.some((root2) => !retiredRoots.includes(root2)))
|
|
57905
58035
|
throw new Error("Missing reviewed directory membership inventory; provide a fresh discovery review");
|
|
57906
58036
|
const current = captureDiscoveryDirectoryProjection(ordinary.map((item) => item.path), retiredRoots).map(({ entries, ...item }) => item);
|
|
57907
|
-
if (!
|
|
58037
|
+
if (!isDeepStrictEqual10(current, projection))
|
|
57908
58038
|
throw new Error("Native discovery directory membership changed outside retired Codex roots");
|
|
57909
58039
|
}
|
|
57910
58040
|
} else
|
|
@@ -57963,7 +58093,7 @@ function codexInstallationRoots(binding) {
|
|
|
57963
58093
|
});
|
|
57964
58094
|
}
|
|
57965
58095
|
function assertMarketplaceBinding(binding, source) {
|
|
57966
|
-
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
|
|
58096
|
+
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
|
|
57967
58097
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
57968
58098
|
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
|
|
57969
58099
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
@@ -58152,10 +58282,10 @@ function resolveAgentDiscovery(options) {
|
|
|
58152
58282
|
verifyAgentDiscovery(retained);
|
|
58153
58283
|
if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
|
|
58154
58284
|
unresolved("retained review is missing the full agent configuration source");
|
|
58155
|
-
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3"))) && source.path !== canonical6(configPath)))
|
|
58285
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
|
|
58156
58286
|
unresolved("retained settings witness names another configuration source");
|
|
58157
58287
|
const covered = supersedesCodexProjection(retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
|
|
58158
|
-
if (covered.some((source) => !retained.sources.some((saved) =>
|
|
58288
|
+
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual10(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
58159
58289
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
58160
58290
|
return retained;
|
|
58161
58291
|
}
|
|
@@ -58169,7 +58299,7 @@ function resolveAgentDiscovery(options) {
|
|
|
58169
58299
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
58170
58300
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
58171
58301
|
verifyAgentDiscovery(supplied);
|
|
58172
|
-
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3"))) && source.path !== canonical6(configPath)))
|
|
58302
|
+
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
|
|
58173
58303
|
throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
|
|
58174
58304
|
if (!review.sources.some((source) => source.path === canonical6(configPath)))
|
|
58175
58305
|
throw new Error("Discovery review must include the agent configuration source");
|
|
@@ -84267,7 +84397,7 @@ var init_skill_context = __esm(() => {
|
|
|
84267
84397
|
|
|
84268
84398
|
// src/lib/session-reconciliation.ts
|
|
84269
84399
|
import { createHash as createHash26 } from "crypto";
|
|
84270
|
-
import { isDeepStrictEqual as
|
|
84400
|
+
import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
|
|
84271
84401
|
function exactTimestamp(value) {
|
|
84272
84402
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
84273
84403
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -84324,7 +84454,7 @@ async function reconcileSkillSessionIfSafe(sessionId, profileId, options = {}) {
|
|
|
84324
84454
|
for (const key of old.loaded) {
|
|
84325
84455
|
const previous = oldByKey.get(key);
|
|
84326
84456
|
const current = targetBySlug.get(previous.slug);
|
|
84327
|
-
if (!current || !
|
|
84457
|
+
if (!current || !isDeepStrictEqual11({ ...previous, profileRevision: target.profileRevision }, current)) {
|
|
84328
84458
|
throw new SkillSelectionError("SESSION_RECONCILIATION_REQUIRED", "A loaded Skills selection was removed or changed; review skills sessions reconcile before changing this session's pin.");
|
|
84329
84459
|
}
|
|
84330
84460
|
}
|
|
@@ -88966,7 +89096,7 @@ var init_execution_grants = __esm(() => {
|
|
|
88966
89096
|
});
|
|
88967
89097
|
|
|
88968
89098
|
// src/lib/execution-grant-client.ts
|
|
88969
|
-
import { isDeepStrictEqual as
|
|
89099
|
+
import { isDeepStrictEqual as isDeepStrictEqual12 } from "util";
|
|
88970
89100
|
function id(value) {
|
|
88971
89101
|
if (!grantIdentifier(value))
|
|
88972
89102
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -89018,7 +89148,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
|
|
|
89018
89148
|
headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
|
|
89019
89149
|
body: JSON.stringify({ grants: snapshot2 })
|
|
89020
89150
|
}));
|
|
89021
|
-
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !
|
|
89151
|
+
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual12(policy.grants, snapshot2)) {
|
|
89022
89152
|
throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
|
|
89023
89153
|
}
|
|
89024
89154
|
return policy;
|
|
@@ -89029,7 +89159,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
89029
89159
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
89030
89160
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
89031
89161
|
}
|
|
89032
|
-
if (!
|
|
89162
|
+
if (!isDeepStrictEqual12(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual12(value.bindings.consumer, snapshot2.consumer)) {
|
|
89033
89163
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
89034
89164
|
}
|
|
89035
89165
|
return value;
|
|
@@ -110034,8 +110164,139 @@ var init_remote_account2 = __esm(() => {
|
|
|
110034
110164
|
init_remote_client();
|
|
110035
110165
|
});
|
|
110036
110166
|
|
|
110167
|
+
// src/cli/commands/reviewed-runtime-lock.ts
|
|
110168
|
+
import { createHash as createHash30 } from "crypto";
|
|
110169
|
+
function row(value) {
|
|
110170
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
110171
|
+
throw new Error("REVIEWED_LOCK_SHAPE_INVALID");
|
|
110172
|
+
return value;
|
|
110173
|
+
}
|
|
110174
|
+
function same4(a, b) {
|
|
110175
|
+
const entries = (v) => Object.entries(row(v ?? {})).sort(([a2], [b2]) => a2.localeCompare(b2));
|
|
110176
|
+
return JSON.stringify(entries(a)) === JSON.stringify(entries(b));
|
|
110177
|
+
}
|
|
110178
|
+
function packageName(path) {
|
|
110179
|
+
const parts = path.split("/node_modules/");
|
|
110180
|
+
if (!parts[0].startsWith("node_modules/"))
|
|
110181
|
+
throw new Error("REVIEWED_LOCK_LOCATION_INVALID");
|
|
110182
|
+
parts[0] = parts[0].slice("node_modules/".length);
|
|
110183
|
+
if (parts.some((name) => !NAME.test(name) || name.split("/").some((part) => part === "." || part === "..")))
|
|
110184
|
+
throw new Error("REVIEWED_LOCK_LOCATION_INVALID");
|
|
110185
|
+
return parts.at(-1);
|
|
110186
|
+
}
|
|
110187
|
+
function excluded(name, patterns) {
|
|
110188
|
+
return patterns.some((pattern) => new RegExp(`^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`).test(name));
|
|
110189
|
+
}
|
|
110190
|
+
async function metadata(name, context2) {
|
|
110191
|
+
const url2 = new URL(encodeURIComponent(name), `${context2.registryOrigin}/`);
|
|
110192
|
+
const response = await (context2.fetcher ?? fetch)(url2, { redirect: "error", signal: AbortSignal.timeout(30000) });
|
|
110193
|
+
if (!response.ok || !response.body)
|
|
110194
|
+
throw new Error("REVIEWED_LOCK_METADATA_UNAVAILABLE");
|
|
110195
|
+
const reader = response.body.getReader();
|
|
110196
|
+
const chunks = [];
|
|
110197
|
+
let size2 = 0;
|
|
110198
|
+
try {
|
|
110199
|
+
for (;; ) {
|
|
110200
|
+
const { done, value } = await reader.read();
|
|
110201
|
+
if (done)
|
|
110202
|
+
break;
|
|
110203
|
+
size2 += value.byteLength;
|
|
110204
|
+
if (size2 > MAX_METADATA_BYTES) {
|
|
110205
|
+
await reader.cancel();
|
|
110206
|
+
throw new Error("REVIEWED_LOCK_METADATA_TOO_LARGE");
|
|
110207
|
+
}
|
|
110208
|
+
chunks.push(value);
|
|
110209
|
+
}
|
|
110210
|
+
} finally {
|
|
110211
|
+
reader.releaseLock();
|
|
110212
|
+
}
|
|
110213
|
+
try {
|
|
110214
|
+
return row(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
110215
|
+
} catch {
|
|
110216
|
+
throw new Error("REVIEWED_LOCK_METADATA_INVALID");
|
|
110217
|
+
}
|
|
110218
|
+
}
|
|
110219
|
+
async function validateReviewedRuntimeLock(bytes, sha2562, context2) {
|
|
110220
|
+
if (!/^[a-f0-9]{64}$/.test(sha2562))
|
|
110221
|
+
throw new Error("REVIEWED_LOCK_SHA256_INVALID");
|
|
110222
|
+
if (bytes.byteLength > MAX_LOCK_BYTES || createHash30("sha256").update(bytes).digest("hex") !== sha2562)
|
|
110223
|
+
throw new Error("REVIEWED_LOCK_HASH_MISMATCH");
|
|
110224
|
+
if (!Number.isSafeInteger(context2.minReleaseAge) || context2.minReleaseAge < 1)
|
|
110225
|
+
throw new Error("REVIEWED_LOCK_RELEASE_AGE_REQUIRED");
|
|
110226
|
+
let lock;
|
|
110227
|
+
try {
|
|
110228
|
+
lock = row(JSON.parse(Buffer.from(bytes).toString("utf8")));
|
|
110229
|
+
} catch {
|
|
110230
|
+
throw new Error("REVIEWED_LOCK_SHAPE_INVALID");
|
|
110231
|
+
}
|
|
110232
|
+
if (lock.lockfileVersion !== 3 || lock.requires !== true)
|
|
110233
|
+
throw new Error("REVIEWED_LOCK_FORMAT_UNSUPPORTED");
|
|
110234
|
+
const packages = row(lock.packages);
|
|
110235
|
+
if (Object.keys(packages).length > 5000)
|
|
110236
|
+
throw new Error("REVIEWED_LOCK_POPULATION_TOO_LARGE");
|
|
110237
|
+
const root2 = row(packages[""]);
|
|
110238
|
+
if (!same4(root2.dependencies, { [PACKAGE2]: "file:./verified.tgz" }) || Object.keys(row(root2.devDependencies ?? {})).length || Object.keys(row(root2.optionalDependencies ?? {})).length)
|
|
110239
|
+
throw new Error("REVIEWED_LOCK_ROOT_MISMATCH");
|
|
110240
|
+
const target = row(packages[`node_modules/${PACKAGE2}`]);
|
|
110241
|
+
if (target.version !== context2.version || target.resolved !== "file:verified.tgz" || target.integrity !== context2.archiveIntegrity || target.name !== undefined && target.name !== PACKAGE2)
|
|
110242
|
+
throw new Error("REVIEWED_LOCK_ARCHIVE_MISMATCH");
|
|
110243
|
+
for (const field2 of ["dependencies", "optionalDependencies", "peerDependencies"]) {
|
|
110244
|
+
if (!same4(target[field2], context2.packageManifest[field2]))
|
|
110245
|
+
throw new Error("REVIEWED_LOCK_MANIFEST_MISMATCH");
|
|
110246
|
+
}
|
|
110247
|
+
const registry2 = new URL(context2.registryOrigin);
|
|
110248
|
+
const identities = new Map;
|
|
110249
|
+
for (const [location, value] of Object.entries(packages)) {
|
|
110250
|
+
if (!location)
|
|
110251
|
+
continue;
|
|
110252
|
+
const name = packageName(location), entry = row(value);
|
|
110253
|
+
if (entry.link || entry.dev || entry.name !== undefined && entry.name !== name)
|
|
110254
|
+
throw new Error("REVIEWED_LOCK_LOCATION_INVALID");
|
|
110255
|
+
if (location === `node_modules/${PACKAGE2}`)
|
|
110256
|
+
continue;
|
|
110257
|
+
if (typeof entry.version !== "string" || !VERSION.test(entry.version) || typeof entry.resolved !== "string" || typeof entry.integrity !== "string" || !INTEGRITY.test(entry.integrity))
|
|
110258
|
+
throw new Error("REVIEWED_LOCK_PACKAGE_INVALID");
|
|
110259
|
+
let url2;
|
|
110260
|
+
try {
|
|
110261
|
+
url2 = new URL(entry.resolved);
|
|
110262
|
+
} catch {
|
|
110263
|
+
throw new Error("REVIEWED_LOCK_REGISTRY_INVALID");
|
|
110264
|
+
}
|
|
110265
|
+
if (url2.origin !== registry2.origin || url2.username || url2.password || url2.search || url2.hash || !url2.pathname.endsWith(".tgz"))
|
|
110266
|
+
throw new Error("REVIEWED_LOCK_REGISTRY_INVALID");
|
|
110267
|
+
const entries = identities.get(name) ?? [];
|
|
110268
|
+
entries.push({ name, version: entry.version, resolved: entry.resolved, integrity: entry.integrity });
|
|
110269
|
+
identities.set(name, entries);
|
|
110270
|
+
}
|
|
110271
|
+
const names = [...identities.keys()];
|
|
110272
|
+
const now3 = context2.now ?? Date.now();
|
|
110273
|
+
for (let offset = 0;offset < names.length; offset += 6) {
|
|
110274
|
+
await Promise.all(names.slice(offset, offset + 6).map(async (name) => {
|
|
110275
|
+
const data = await metadata(name, context2), versions2 = row(data.versions), times2 = row(data.time);
|
|
110276
|
+
if (data.name !== name)
|
|
110277
|
+
throw new Error("REVIEWED_LOCK_METADATA_IDENTITY_MISMATCH");
|
|
110278
|
+
for (const entry of identities.get(name)) {
|
|
110279
|
+
const version2 = row(versions2[entry.version]), dist = row(version2.dist);
|
|
110280
|
+
if (version2.name !== name || version2.version !== entry.version || dist.integrity !== entry.integrity || dist.tarball !== entry.resolved)
|
|
110281
|
+
throw new Error("REVIEWED_LOCK_METADATA_IDENTITY_MISMATCH");
|
|
110282
|
+
const published = typeof times2[entry.version] === "string" ? Date.parse(times2[entry.version]) : NaN;
|
|
110283
|
+
if (!Number.isFinite(published) || published > now3 || !excluded(name, context2.minReleaseAgeExclude) && now3 - published < context2.minReleaseAge * 86400000)
|
|
110284
|
+
throw new Error("REVIEWED_LOCK_RELEASE_AGE_REFUSED");
|
|
110285
|
+
}
|
|
110286
|
+
}));
|
|
110287
|
+
}
|
|
110288
|
+
}
|
|
110289
|
+
var PACKAGE2 = "@hasna/skills", NAME, VERSION, INTEGRITY, MAX_LOCK_BYTES, MAX_METADATA_BYTES;
|
|
110290
|
+
var init_reviewed_runtime_lock = __esm(() => {
|
|
110291
|
+
NAME = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/;
|
|
110292
|
+
VERSION = /^\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?(?:\+[a-zA-Z0-9.-]+)?$/;
|
|
110293
|
+
INTEGRITY = /^sha512-[A-Za-z0-9+/]{86}==$/;
|
|
110294
|
+
MAX_LOCK_BYTES = 8 * 1024 * 1024;
|
|
110295
|
+
MAX_METADATA_BYTES = 32 * 1024 * 1024;
|
|
110296
|
+
});
|
|
110297
|
+
|
|
110037
110298
|
// src/cli/commands/runtime-copyfile.ts
|
|
110038
|
-
import { createHash as
|
|
110299
|
+
import { createHash as createHash31, randomUUID as randomUUID9 } from "crypto";
|
|
110039
110300
|
import { Readable } from "stream";
|
|
110040
110301
|
import { createGunzip as createGunzip2 } from "zlib";
|
|
110041
110302
|
import {
|
|
@@ -110059,7 +110320,7 @@ function object9(value, code2) {
|
|
|
110059
110320
|
return value;
|
|
110060
110321
|
}
|
|
110061
110322
|
function hash5(bytes) {
|
|
110062
|
-
return
|
|
110323
|
+
return createHash31("sha256").update(bytes).digest("hex");
|
|
110063
110324
|
}
|
|
110064
110325
|
function isWithin(parent, child) {
|
|
110065
110326
|
const rel = relative10(parent, child);
|
|
@@ -110210,7 +110471,7 @@ function verifyIntegrity(bytes, integrity) {
|
|
|
110210
110471
|
const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(integrity);
|
|
110211
110472
|
if (!match)
|
|
110212
110473
|
throw new Error("REGISTRY_INTEGRITY_UNSUPPORTED");
|
|
110213
|
-
const actual =
|
|
110474
|
+
const actual = createHash31("sha512").update(bytes).digest("base64");
|
|
110214
110475
|
if (actual !== match[1])
|
|
110215
110476
|
throw new Error("TARBALL_INTEGRITY_MISMATCH");
|
|
110216
110477
|
}
|
|
@@ -110322,10 +110583,10 @@ async function downloadPackage(version2, stagingRoot, registryOrigin, fetcher) {
|
|
|
110322
110583
|
const metadataResponse = await fetcher(metadataUrl, { redirect: "error", signal: timeout });
|
|
110323
110584
|
if (!metadataResponse.ok || new URL(metadataResponse.url || metadataUrl).origin !== new URL(registryOrigin).origin)
|
|
110324
110585
|
throw new Error("REGISTRY_METADATA_FETCH_FAILED");
|
|
110325
|
-
const
|
|
110326
|
-
if (
|
|
110586
|
+
const metadata2 = object9(JSON.parse(new TextDecoder().decode(await readBodyCapped(metadataResponse, 5 * 1024 * 1024, "REGISTRY_METADATA_SIZE_INVALID"))), "REGISTRY_METADATA_INVALID");
|
|
110587
|
+
if (metadata2.name !== PACKAGE_NAME || metadata2.version !== version2 || metadata2._id !== `${PACKAGE_NAME}@${version2}`)
|
|
110327
110588
|
throw new Error("REGISTRY_PACKAGE_IDENTITY_MISMATCH");
|
|
110328
|
-
const dist = object9(
|
|
110589
|
+
const dist = object9(metadata2.dist, "REGISTRY_DIST_INVALID");
|
|
110329
110590
|
const integrity = typeof dist.integrity === "string" ? dist.integrity : "";
|
|
110330
110591
|
const tarballUrl = expectedTarballUrl(dist.tarball, registryOrigin, version2);
|
|
110331
110592
|
const response = await fetcher(tarballUrl, { redirect: "error", signal: AbortSignal.timeout(30000) });
|
|
@@ -110996,6 +111257,24 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
110996
111257
|
if (!STABLE_SEMVER_PATTERN.test(version2))
|
|
110997
111258
|
throw new Error("EXACT_STABLE_VERSION_REQUIRED");
|
|
110998
111259
|
const policy = releaseAgePolicy(options);
|
|
111260
|
+
let reviewedLockBytes;
|
|
111261
|
+
if (options.reviewedLock !== undefined || options.reviewedLockSha256 !== undefined) {
|
|
111262
|
+
if (!options.reviewedLock || !options.reviewedLockSha256)
|
|
111263
|
+
throw new Error("REVIEWED_LOCK_INPUT_PAIR_REQUIRED");
|
|
111264
|
+
if (!policy)
|
|
111265
|
+
throw new Error("REVIEWED_LOCK_RELEASE_AGE_REQUIRED");
|
|
111266
|
+
if (!isAbsolute25(options.reviewedLock))
|
|
111267
|
+
throw new Error("REVIEWED_LOCK_ABSOLUTE_PATH_REQUIRED");
|
|
111268
|
+
assertNoSymlinkAncestors(dirname31(options.reviewedLock));
|
|
111269
|
+
const stat3 = lstatSync30(options.reviewedLock);
|
|
111270
|
+
if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.nlink !== 1 || stat3.uid !== process.getuid?.() || stat3.mode & 63 || stat3.size > 8 * 1024 * 1024)
|
|
111271
|
+
throw new Error("REVIEWED_LOCK_FILE_UNSAFE");
|
|
111272
|
+
reviewedLockBytes = readFileSync38(options.reviewedLock);
|
|
111273
|
+
if (!/^[a-f0-9]{64}$/.test(options.reviewedLockSha256))
|
|
111274
|
+
throw new Error("REVIEWED_LOCK_SHA256_INVALID");
|
|
111275
|
+
if (hash5(reviewedLockBytes) !== options.reviewedLockSha256)
|
|
111276
|
+
throw new Error("REVIEWED_LOCK_HASH_MISMATCH");
|
|
111277
|
+
}
|
|
110999
111278
|
const home = resolve25(options.homeDir ?? process.env.HOME ?? "");
|
|
111000
111279
|
if (!home || !existsSync34(home))
|
|
111001
111280
|
throw new Error("HOME_NOT_FOUND");
|
|
@@ -111032,12 +111311,37 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
111032
111311
|
const stagePackage = join58(stagePath, "install");
|
|
111033
111312
|
mkdirSync24(stagePackage, { mode: 448 });
|
|
111034
111313
|
copyFileSync(artifact.tarballPath, join58(stagePackage, "verified.tgz"));
|
|
111035
|
-
writeFileSync23(join58(stagePackage, "package.json"), JSON.stringify({ private: true, dependencies: { [PACKAGE_NAME]: "file:./verified.tgz" } }, null, 2), { mode: 384, flag: "wx" });
|
|
111036
|
-
|
|
111314
|
+
writeFileSync23(join58(stagePackage, "package.json"), JSON.stringify({ name: "skills-runtime-install", version: "0.0.0", private: true, dependencies: { [PACKAGE_NAME]: "file:./verified.tgz" } }, null, 2), { mode: 384, flag: "wx" });
|
|
111315
|
+
let npmVersion;
|
|
111316
|
+
if (reviewedLockBytes) {
|
|
111317
|
+
await validateReviewedRuntimeLock(reviewedLockBytes, options.reviewedLockSha256, {
|
|
111318
|
+
version: version2,
|
|
111319
|
+
archiveIntegrity: artifact.integrity,
|
|
111320
|
+
packageManifest: object9(JSON.parse(readFileSync38(join58(stagePath, "verified-archive", "package", "package.json"), "utf8")), "PACKAGE_MANIFEST_INVALID"),
|
|
111321
|
+
registryOrigin: REGISTRY_ORIGIN,
|
|
111322
|
+
...policy,
|
|
111323
|
+
fetcher
|
|
111324
|
+
});
|
|
111325
|
+
writeFileSync23(join58(stagePackage, "package-lock.json"), reviewedLockBytes, { mode: 384, flag: "wx" });
|
|
111326
|
+
if (!readFileSync38(join58(stagePackage, "package-lock.json")).equals(reviewedLockBytes))
|
|
111327
|
+
throw new Error("REVIEWED_LOCK_COPY_MISMATCH");
|
|
111328
|
+
} else {
|
|
111329
|
+
npmVersion = await runNpm(["install", "--package-lock-only", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
111330
|
+
}
|
|
111037
111331
|
const ciNpmVersion = await runNpm(["ci", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
111038
|
-
if (npmVersion !== ciNpmVersion)
|
|
111332
|
+
if (!reviewedLockBytes && npmVersion !== ciNpmVersion)
|
|
111039
111333
|
throw new Error("NPM_VERSION_DRIFT_DURING_UPDATE");
|
|
111334
|
+
npmVersion = ciNpmVersion;
|
|
111040
111335
|
const lockBytes = readFileSync38(join58(stagePackage, "package-lock.json"));
|
|
111336
|
+
if (reviewedLockBytes) {
|
|
111337
|
+
if (!lockBytes.equals(reviewedLockBytes))
|
|
111338
|
+
throw new Error("REVIEWED_LOCK_DRIFT_DURING_INSTALL");
|
|
111339
|
+
const treeNpmVersion = await runNpm(["sbom", "--sbom-format=cyclonedx", "--package-lock-only=false", "--omit=dev", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
111340
|
+
if (treeNpmVersion !== npmVersion)
|
|
111341
|
+
throw new Error("NPM_VERSION_DRIFT_DURING_UPDATE");
|
|
111342
|
+
if (!readFileSync38(join58(stagePackage, "package-lock.json")).equals(reviewedLockBytes))
|
|
111343
|
+
throw new Error("REVIEWED_LOCK_DRIFT_DURING_INSTALL");
|
|
111344
|
+
}
|
|
111041
111345
|
const installedPackage = join58(stagePackage, "node_modules", "@hasna", "skills");
|
|
111042
111346
|
const installed = readPackage(join58(installedPackage, "package.json"));
|
|
111043
111347
|
if (installed.version !== version2 || JSON.stringify(Object.entries(installed.bins).sort()) !== JSON.stringify(Object.entries(layout.bin).sort())) {
|
|
@@ -111078,6 +111382,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
111078
111382
|
configs: configPreimages,
|
|
111079
111383
|
preimageSha256,
|
|
111080
111384
|
...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {},
|
|
111385
|
+
...reviewedLockBytes ? { reviewedLockSha256: options.reviewedLockSha256 } : {},
|
|
111081
111386
|
launchers,
|
|
111082
111387
|
switchedLaunchers: [],
|
|
111083
111388
|
rollbackCompletedLaunchers: []
|
|
@@ -111116,7 +111421,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
111116
111421
|
}
|
|
111117
111422
|
receipt.state = "switched";
|
|
111118
111423
|
persistReceipt(join58(finalPath, "rollout-receipt.json"), receipt);
|
|
111119
|
-
return { updated: true, version: version2, currentVersion: layout.currentVersion, receiptId: id2, runtimeRoot: finalPath, tarballSha256: artifact.sha256, tarballIntegrity: artifact.integrity, runtimeTreeSha256: stagedTree.digest, launcherCount: launchers.length, configCount: configPreimages.length, ...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {} };
|
|
111424
|
+
return { updated: true, version: version2, currentVersion: layout.currentVersion, receiptId: id2, runtimeRoot: finalPath, tarballSha256: artifact.sha256, tarballIntegrity: artifact.integrity, runtimeTreeSha256: stagedTree.digest, launcherCount: launchers.length, configCount: configPreimages.length, ...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {}, ...reviewedLockBytes ? { reviewedLockSha256: options.reviewedLockSha256 } : {} };
|
|
111120
111425
|
} catch (error61) {
|
|
111121
111426
|
if (switched.length > 0 || moved) {
|
|
111122
111427
|
let rollbackFailed = false;
|
|
@@ -111253,6 +111558,7 @@ function rollbackCopyfileRuntime(receiptId, options = {}) {
|
|
|
111253
111558
|
var PACKAGE_NAME = "@hasna/skills", REGISTRY_ORIGIN = "https://registry.npmjs.org", STABLE_SEMVER_PATTERN, MAX_TARBALL_BYTES, MAX_TARBALL_EXPANDED_BYTES, MAX_TARBALL_ENTRIES = 20000, CONFIG_PREIMAGE_PATHS;
|
|
111254
111559
|
var init_runtime_copyfile = __esm(() => {
|
|
111255
111560
|
init_skill_contract();
|
|
111561
|
+
init_reviewed_runtime_lock();
|
|
111256
111562
|
STABLE_SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
111257
111563
|
MAX_TARBALL_BYTES = 100 * 1024 * 1024;
|
|
111258
111564
|
MAX_TARBALL_EXPANDED_BYTES = 256 * 1024 * 1024;
|
|
@@ -111269,7 +111575,7 @@ var exports_runtime = {};
|
|
|
111269
111575
|
__export(exports_runtime, {
|
|
111270
111576
|
registerRuntime: () => registerRuntime
|
|
111271
111577
|
});
|
|
111272
|
-
import { createHash as
|
|
111578
|
+
import { createHash as createHash32, randomUUID as randomUUID10 } from "crypto";
|
|
111273
111579
|
import { lstatSync as lstatSync31, mkdirSync as mkdirSync25, readFileSync as readFileSync39, realpathSync as realpathSync17, writeFileSync as writeFileSync24 } from "fs";
|
|
111274
111580
|
import { basename as basename16, isAbsolute as isAbsolute26, join as join59 } from "path";
|
|
111275
111581
|
import { createInterface } from "readline";
|
|
@@ -111310,7 +111616,7 @@ function registerRuntime(parent) {
|
|
|
111310
111616
|
if (!expected)
|
|
111311
111617
|
throw new Error("Cloud artifact not found");
|
|
111312
111618
|
const bytes = await client2.download(id2, name);
|
|
111313
|
-
if (bytes.length !== expected.byteSize ||
|
|
111619
|
+
if (bytes.length !== expected.byteSize || createHash32("sha256").update(bytes).digest("hex") !== expected.sha256)
|
|
111314
111620
|
throw new Error("Cloud artifact integrity verification failed");
|
|
111315
111621
|
writeFileSync24(options.output, bytes, { flag: "wx", mode: 384 });
|
|
111316
111622
|
console.log(JSON.stringify({ path: options.output, sha256: expected.sha256, byteSize: bytes.length }));
|
|
@@ -111340,7 +111646,12 @@ function registerRuntime(parent) {
|
|
|
111340
111646
|
});
|
|
111341
111647
|
const setup = parent.command("setup").description("Point this CLI at a Skills API server, or register agent integrations").option("--api-url <url>", "Skills API origin to send remote work to").option("--global", "Accepted for compatibility; the API origin is always per-user", false).option("--json", "Output setup result as JSON", false).action(async (options) => handleSetup(options));
|
|
111342
111648
|
setup.command("agents").option("--json", "Output registration result as JSON", false).description("Register the Skills MCP server with all supported agents").action(async (options) => handleMcp({ register: "all", json: options.json }));
|
|
111343
|
-
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).option("--version <version>", "Install this exact @hasna/skills version as a staged copyfile runtime").option("--min-release-age <days>", "Require a positive integer dependency release age in days for an exact-version update").option("--min-release-age-exclude <pattern>", "Exclude matching package identities from the release-age policy (repeatable)", (pattern, prior) => [...prior, pattern], []).option("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").option("--adopt-aliases", "Switch verified legacy @hasna/skills aliases to the active copyfile runtime").option("--rollback-aliases <receipt-id>", "Restore aliases from an exact adoption receipt").action(async (options) => {
|
|
111649
|
+
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).option("--version <version>", "Install this exact @hasna/skills version as a staged copyfile runtime").option("--min-release-age <days>", "Require a positive integer dependency release age in days for an exact-version update").option("--min-release-age-exclude <pattern>", "Exclude matching package identities from the release-age policy (repeatable)", (pattern, prior) => [...prior, pattern], []).option("--reviewed-lock <path>", "Install from this private absolute package-lock.json path without resolving dependency ranges").option("--reviewed-lock-sha256 <sha256>", "Exact reviewed SHA-256 of the supplied package-lock.json").option("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").option("--adopt-aliases", "Switch verified legacy @hasna/skills aliases to the active copyfile runtime").option("--rollback-aliases <receipt-id>", "Restore aliases from an exact adoption receipt").action(async (options) => {
|
|
111650
|
+
if ((options.reviewedLock !== undefined || options.reviewedLockSha256 !== undefined) && (!options.version || options.rollback || options.adoptAliases || options.rollbackAliases)) {
|
|
111651
|
+
console.error(JSON.stringify({ error: "REVIEWED_LOCK_REQUIRES_EXACT_VERSION" }));
|
|
111652
|
+
process.exitCode = 1;
|
|
111653
|
+
return;
|
|
111654
|
+
}
|
|
111344
111655
|
if ((options.minReleaseAge !== undefined || options.minReleaseAgeExclude.length > 0) && (!options.version || options.rollback || options.adoptAliases || options.rollbackAliases)) {
|
|
111345
111656
|
console.error(JSON.stringify({ error: "RELEASE_AGE_POLICY_REQUIRES_EXACT_VERSION" }));
|
|
111346
111657
|
process.exitCode = 1;
|
|
@@ -111382,7 +111693,7 @@ function registerRuntime(parent) {
|
|
|
111382
111693
|
if (options.version) {
|
|
111383
111694
|
try {
|
|
111384
111695
|
const minReleaseAge = options.minReleaseAge === undefined ? undefined : /^\d+$/.test(options.minReleaseAge) ? Number(options.minReleaseAge) : NaN;
|
|
111385
|
-
const result2 = await updateCopyfileRuntime(options.version, { minReleaseAge, minReleaseAgeExclude: options.minReleaseAgeExclude });
|
|
111696
|
+
const result2 = await updateCopyfileRuntime(options.version, { minReleaseAge, minReleaseAgeExclude: options.minReleaseAgeExclude, reviewedLock: options.reviewedLock, reviewedLockSha256: options.reviewedLockSha256 });
|
|
111386
111697
|
if (options.json)
|
|
111387
111698
|
console.log(JSON.stringify(result2));
|
|
111388
111699
|
else
|
|
@@ -112959,7 +113270,7 @@ var init_portable_snapshot_filter = __esm(() => {
|
|
|
112959
113270
|
});
|
|
112960
113271
|
|
|
112961
113272
|
// src/lib/station-snapshot.ts
|
|
112962
|
-
import { createHash as
|
|
113273
|
+
import { createHash as createHash33 } from "crypto";
|
|
112963
113274
|
import {
|
|
112964
113275
|
copyFileSync as copyFileSync2,
|
|
112965
113276
|
mkdirSync as mkdirSync26,
|
|
@@ -112974,7 +113285,7 @@ function validateStationId(stationId) {
|
|
|
112974
113285
|
}
|
|
112975
113286
|
}
|
|
112976
113287
|
function sha256File(filePath) {
|
|
112977
|
-
return
|
|
113288
|
+
return createHash33("sha256").update(readFileSync40(filePath)).digest("hex");
|
|
112978
113289
|
}
|
|
112979
113290
|
function scanHome(definition, homesRoot) {
|
|
112980
113291
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -113546,7 +113857,7 @@ var init_create_sync_config = __esm(() => {
|
|
|
113546
113857
|
});
|
|
113547
113858
|
|
|
113548
113859
|
// src/lib/codex-native-config.ts
|
|
113549
|
-
import { isDeepStrictEqual as
|
|
113860
|
+
import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
|
|
113550
113861
|
function isRecord8(value) {
|
|
113551
113862
|
if (!value || typeof value !== "object")
|
|
113552
113863
|
return false;
|
|
@@ -113568,7 +113879,7 @@ function decimalIdentity(text4) {
|
|
|
113568
113879
|
return `${match[1]}${coefficient}e${exponent}`;
|
|
113569
113880
|
}
|
|
113570
113881
|
function codexNativeConfigEqual(native, expected) {
|
|
113571
|
-
if (
|
|
113882
|
+
if (isDeepStrictEqual13(native, expected))
|
|
113572
113883
|
return true;
|
|
113573
113884
|
if (typeof expected === "number") {
|
|
113574
113885
|
if (!Number.isFinite(expected) || !isRecord8(native))
|
|
@@ -113594,7 +113905,7 @@ var init_codex_native_config = () => {};
|
|
|
113594
113905
|
// src/lib/codex-hook-trust-identity.ts
|
|
113595
113906
|
import { realpathSync as realpathSync18 } from "fs";
|
|
113596
113907
|
import { dirname as dirname34, isAbsolute as isAbsolute28, join as join62 } from "path";
|
|
113597
|
-
import { isDeepStrictEqual as
|
|
113908
|
+
import { isDeepStrictEqual as isDeepStrictEqual14 } from "util";
|
|
113598
113909
|
function inspectRecordedSkillsCli(command, expected) {
|
|
113599
113910
|
need5(isAbsolute28(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
|
|
113600
113911
|
const resolved = realpathSync18(command);
|
|
@@ -113604,7 +113915,7 @@ function inspectRecordedSkillsCli(command, expected) {
|
|
|
113604
113915
|
need5(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join62(dirname34(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
113605
113916
|
need5((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
113606
113917
|
const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
|
|
113607
|
-
need5(
|
|
113918
|
+
need5(isDeepStrictEqual14(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
113608
113919
|
return { receipt, recheck() {
|
|
113609
113920
|
need5(realpathSync18(command) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
113610
113921
|
unchanged2(cli);
|
|
@@ -113640,19 +113951,19 @@ var init_codex_hook_trust_identity = __esm(() => {
|
|
|
113640
113951
|
});
|
|
113641
113952
|
|
|
113642
113953
|
// src/lib/codex-hook-policy-recovery.ts
|
|
113643
|
-
import { isDeepStrictEqual as
|
|
113954
|
+
import { isDeepStrictEqual as isDeepStrictEqual15 } from "util";
|
|
113644
113955
|
function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
|
|
113645
113956
|
need5(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
|
|
113646
113957
|
need5(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
113647
113958
|
const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
|
|
113648
113959
|
const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
|
|
113649
113960
|
need5(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
|
|
113650
|
-
const changes = ["roots", "sources"].filter((key) => !
|
|
113961
|
+
const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual15(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
|
|
113651
113962
|
need5(changes.length > 0, "RECONCILE_POLICY_CHANGED");
|
|
113652
113963
|
const compared = structuredClone(current);
|
|
113653
113964
|
compared.bridge.discovery.claude.roots = oldDiscovery.roots;
|
|
113654
113965
|
compared.bridge.discovery.claude.sources = oldDiscovery.sources;
|
|
113655
|
-
need5(
|
|
113966
|
+
need5(isDeepStrictEqual15(before2, compared), "RECONCILE_POLICY_CHANGED");
|
|
113656
113967
|
verifyAgentDiscovery(newDiscovery);
|
|
113657
113968
|
return { reason: options.reason, changes };
|
|
113658
113969
|
}
|
|
@@ -113664,21 +113975,21 @@ var init_codex_hook_policy_recovery = __esm(() => {
|
|
|
113664
113975
|
|
|
113665
113976
|
// src/lib/agent-codex-trust.ts
|
|
113666
113977
|
import { lstatSync as lstatSync33, mkdirSync as mkdirSync27, readdirSync as readdirSync23, readFileSync as readFileSync41, realpathSync as realpathSync19, statSync as statSync22 } from "fs";
|
|
113667
|
-
import { createHash as
|
|
113978
|
+
import { createHash as createHash34, randomUUID as randomUUID11 } from "crypto";
|
|
113668
113979
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
113669
113980
|
import { dirname as dirname35, isAbsolute as isAbsolute29, join as join63, resolve as resolve27 } from "path";
|
|
113670
113981
|
import { homedir as homedir12 } from "os";
|
|
113671
|
-
import { isDeepStrictEqual as
|
|
113982
|
+
import { isDeepStrictEqual as isDeepStrictEqual16 } from "util";
|
|
113672
113983
|
function verifyReleaseArtifact(proof, skillsCli) {
|
|
113673
113984
|
need5(isAbsolute29(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
113674
113985
|
const tarPath = realpathSync19(proof.packageTarPath);
|
|
113675
113986
|
need5(statSync22(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
113676
113987
|
const tarBytes = readFileSync41(tarPath);
|
|
113677
|
-
need5(tarBytes.byteLength === proof.packageTarBytes &&
|
|
113988
|
+
need5(tarBytes.byteLength === proof.packageTarBytes && createHash34("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
|
|
113678
113989
|
const packageRoot = dirname35(dirname35(realpathSync19(skillsCli.receipt.path)));
|
|
113679
113990
|
const manifest = readFileSync41(join63(packageRoot, "package.json"));
|
|
113680
|
-
need5(
|
|
113681
|
-
need5(
|
|
113991
|
+
need5(createHash34("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
|
|
113992
|
+
need5(createHash34("sha256").update(readFileSync41(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
|
|
113682
113993
|
const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync41(skillsCli.receipt.path)]];
|
|
113683
113994
|
for (const [member, expected] of members2) {
|
|
113684
113995
|
let actual;
|
|
@@ -113710,7 +114021,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
113710
114021
|
const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
|
|
113711
114022
|
const digestInput = { version: 1, home: intent.home ?? home, configPath: intent.configPath, ...intent.nativeConfigPath !== undefined ? { nativeConfigPath: intent.nativeConfigPath } : {}, configSha256, configVersion, policySha256: intent.policySha256, hooksSha256: intent.hooksSha256, skillsCli: intent.skillsCli, nativeVersion: intent.nativeVersion, declarations, admitted };
|
|
113712
114023
|
need5(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
|
|
113713
|
-
need5(
|
|
114024
|
+
need5(isDeepStrictEqual16(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
|
|
113714
114025
|
need5(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
|
|
113715
114026
|
const policyCurrent = snapshot(join63(dataDir3, "agent-policy.json"), true);
|
|
113716
114027
|
const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
|
|
@@ -113723,7 +114034,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
113723
114034
|
if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
|
|
113724
114035
|
need5(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
|
|
113725
114036
|
const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
|
|
113726
|
-
need5(
|
|
114037
|
+
need5(isDeepStrictEqual16(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
|
|
113727
114038
|
supersededBinding = true;
|
|
113728
114039
|
}
|
|
113729
114040
|
if (supersededBinding || recovery) {
|
|
@@ -113759,7 +114070,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
113759
114070
|
need5(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
|
|
113760
114071
|
}
|
|
113761
114072
|
} else
|
|
113762
|
-
need5(
|
|
114073
|
+
need5(isDeepStrictEqual16(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
|
|
113763
114074
|
const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
|
|
113764
114075
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
|
|
113765
114076
|
const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
|
|
@@ -113768,14 +114079,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
113768
114079
|
const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
113769
114080
|
if (supersededBinding)
|
|
113770
114081
|
verifyReleaseArtifact(options.releaseProof, skillsCli);
|
|
113771
|
-
const skillsBindingChanged = !
|
|
114082
|
+
const skillsBindingChanged = !isDeepStrictEqual16(skillsCli.receipt, intent.skillsCli);
|
|
113772
114083
|
need5(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
113773
114084
|
if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
|
|
113774
114085
|
need5(supersededBinding, "RECONCILE_HOOKS_CHANGED");
|
|
113775
114086
|
const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
|
|
113776
114087
|
for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
|
|
113777
114088
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
113778
|
-
need5(
|
|
114089
|
+
need5(isDeepStrictEqual16(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
113779
114090
|
}
|
|
113780
114091
|
}
|
|
113781
114092
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
@@ -113798,9 +114109,9 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
113798
114109
|
delete identity6.command;
|
|
113799
114110
|
return identity6;
|
|
113800
114111
|
};
|
|
113801
|
-
need5(
|
|
114112
|
+
need5(isDeepStrictEqual16(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
113802
114113
|
if (!plannedKeys.has(expected.key))
|
|
113803
|
-
need5(
|
|
114114
|
+
need5(isDeepStrictEqual16(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
|
|
113804
114115
|
}
|
|
113805
114116
|
for (const wanted of intent.hooks) {
|
|
113806
114117
|
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join63(home, ".codex/hooks.json"), hooksPath));
|
|
@@ -113819,7 +114130,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
113819
114130
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
113820
114131
|
need5(layers?.length === 1 && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
|
|
113821
114132
|
const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
|
|
113822
|
-
need5(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks ||
|
|
114133
|
+
need5(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual16(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
|
|
113823
114134
|
const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
113824
114135
|
const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
113825
114136
|
need5(finalLayers?.length === 1 && !finalLayers[0].disabledReason && codexNativeConfigEqual(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
|
|
@@ -113884,7 +114195,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect) {
|
|
|
113884
114195
|
const groups = declarations[event];
|
|
113885
114196
|
need5(Array.isArray(groups), "DECLARATION_CHANGED");
|
|
113886
114197
|
const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
|
|
113887
|
-
need5(matches2.length === 1 &&
|
|
114198
|
+
need5(matches2.length === 1 && isDeepStrictEqual16(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
|
|
113888
114199
|
expected.push({ event: nativeEvent, key: `${join63(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
|
|
113889
114200
|
}
|
|
113890
114201
|
codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
|
|
@@ -113950,7 +114261,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect) {
|
|
|
113950
114261
|
save(join63(journal, "hooks.before.json"), hooksFile.bytes);
|
|
113951
114262
|
save(join63(journal, "policy.before.json"), policyFile.bytes);
|
|
113952
114263
|
save(join63(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
|
|
113953
|
-
need5(
|
|
114264
|
+
need5(isDeepStrictEqual16(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
|
|
113954
114265
|
checkInputs();
|
|
113955
114266
|
let result2;
|
|
113956
114267
|
try {
|
|
@@ -113964,7 +114275,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect) {
|
|
|
113964
114275
|
expectedConfig.hooks.state ??= {};
|
|
113965
114276
|
for (const h of planned)
|
|
113966
114277
|
expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
|
|
113967
|
-
need5(
|
|
114278
|
+
need5(isDeepStrictEqual16(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
|
|
113968
114279
|
const savedConfig = snapshot(join63(journal, "config.before.toml"), true);
|
|
113969
114280
|
need5(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
|
|
113970
114281
|
const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
|
|
@@ -113972,8 +114283,8 @@ async function enrollCodexNativeHooksUnlocked(options, connect) {
|
|
|
113972
114283
|
unchanged2(hooksFile);
|
|
113973
114284
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
|
|
113974
114285
|
const current = await list2(), confirmed = admit(current);
|
|
113975
|
-
need5(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") &&
|
|
113976
|
-
need5(
|
|
114286
|
+
need5(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual16(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
|
|
114287
|
+
need5(isDeepStrictEqual16(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
|
|
113977
114288
|
unchanged2(after2);
|
|
113978
114289
|
unchanged2(savedConfig);
|
|
113979
114290
|
unchanged2(hooksFile);
|
|
@@ -113996,7 +114307,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect) {
|
|
|
113996
114307
|
}
|
|
113997
114308
|
}
|
|
113998
114309
|
var json2 = (value) => JSON.stringify(value, null, 2) + `
|
|
113999
|
-
`, structure3 = (text4) => JSON.parse(JSON.stringify(Bun.TOML.parse(text4))), eventNames, makePlanDigest = (value) =>
|
|
114310
|
+
`, structure3 = (text4) => JSON.parse(JSON.stringify(Bun.TOML.parse(text4))), eventNames, makePlanDigest = (value) => createHash34("sha256").update(json2(value)).digest("hex"), safeNativeHook = (hook) => ({ key: hook.key, eventName: hook.eventName, handlerType: hook.handlerType, command: hook.command, matcher: hook.matcher, timeoutSec: hook.timeoutSec, async: hook.async, statusMessage: hook.statusMessage, additionalContextLimit: hook.additionalContextLimit, sourcePath: hook.sourcePath, source: hook.source, pluginId: hook.pluginId, isManaged: hook.isManaged, currentHash: hook.currentHash, enabled: hook.enabled, trustStatus: hook.trustStatus }), withoutCodexCommand = (value) => {
|
|
114000
114311
|
const copy = structuredClone(value);
|
|
114001
114312
|
if (copy?.bridge?.commands && Object.hasOwn(copy.bridge.commands, "codex"))
|
|
114002
114313
|
copy.bridge.commands.codex = "<reviewed-codex-binding>";
|
|
@@ -114138,7 +114449,7 @@ __export(exports_agent_integration, {
|
|
|
114138
114449
|
registerAgentIntegration: () => registerAgentIntegration
|
|
114139
114450
|
});
|
|
114140
114451
|
import { existsSync as existsSync36, lstatSync as lstatSync34, readFileSync as readFileSync42, readSync as readSync17, writeFileSync as writeFileSync26 } from "fs";
|
|
114141
|
-
import { createHash as
|
|
114452
|
+
import { createHash as createHash35 } from "crypto";
|
|
114142
114453
|
import { homedir as homedir13 } from "os";
|
|
114143
114454
|
import { isAbsolute as isAbsolute30 } from "path";
|
|
114144
114455
|
function pinnedHookProfile(input3, configuredProfile) {
|
|
@@ -114263,7 +114574,7 @@ function registerAgentIntegration(parent) {
|
|
|
114263
114574
|
skillCount: catalog.skills.length,
|
|
114264
114575
|
output: options.output,
|
|
114265
114576
|
bytes: Buffer.byteLength(bytes),
|
|
114266
|
-
sha256:
|
|
114577
|
+
sha256: createHash35("sha256").update(bytes).digest("hex")
|
|
114267
114578
|
};
|
|
114268
114579
|
await writeCliOutput(options.json ? JSON.stringify(receipt) : `Captured ${receipt.skillCount} native Codex skill(s) in ${receipt.output}.`);
|
|
114269
114580
|
} catch (error61) {
|
|
@@ -114272,18 +114583,18 @@ function registerAgentIntegration(parent) {
|
|
|
114272
114583
|
process.exitCode = 1;
|
|
114273
114584
|
}
|
|
114274
114585
|
});
|
|
114275
|
-
hook.command("witness").requiredOption("--kind <kind>", "claude-settings-v3, claude-settings-v2, codex-settings-v3, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2").requiredOption("--path <path>", "Canonical absolute path to the reviewed settings or registry file").option("--json", "Output the discovery witness as JSON", false).description("Capture an explicit versioned review witness without installing it").action(async (options) => {
|
|
114276
|
-
const capture2 = options.kind === "codex-settings-v3" ? captureCodexSettingsV3 : options.kind === "codex-settings-v2" ? captureCodexSettingsV2 : options.kind === "codex-settings-v1" ? captureCodexSettings : options.kind === "claude-settings-v3" ? captureClaudeSettingsV3 : options.kind === "claude-settings-v2" ? captureClaudeSettingsV2 : options.kind === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : null;
|
|
114586
|
+
hook.command("witness").requiredOption("--kind <kind>", "claude-settings-v3, claude-settings-v2, codex-settings-v4, codex-settings-v3, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2").requiredOption("--path <path>", "Canonical absolute path to the reviewed settings or registry file").option("--json", "Output the discovery witness as JSON", false).description("Capture an explicit versioned review witness without installing it").action(async (options) => {
|
|
114587
|
+
const capture2 = options.kind === "codex-settings-v4" ? captureCodexSettingsV4 : options.kind === "codex-settings-v3" ? captureCodexSettingsV3 : options.kind === "codex-settings-v2" ? captureCodexSettingsV2 : options.kind === "codex-settings-v1" ? captureCodexSettings : options.kind === "claude-settings-v3" ? captureClaudeSettingsV3 : options.kind === "claude-settings-v2" ? captureClaudeSettingsV2 : options.kind === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : null;
|
|
114277
114588
|
if (!capture2)
|
|
114278
|
-
throw new Error("Unsupported witness kind; select claude-settings-v3, claude-settings-v2, codex-settings-v3, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2");
|
|
114589
|
+
throw new Error("Unsupported witness kind; select claude-settings-v3, claude-settings-v2, codex-settings-v4, codex-settings-v3, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2");
|
|
114279
114590
|
await writeCliOutput(JSON.stringify(capture2(options.path), null, 2));
|
|
114280
114591
|
});
|
|
114281
|
-
hook.command("rebind-settings").requiredOption("--agent <agent>", "claude or codex").requiredOption("--reviewed-preimage <path>", "Exact preserved legacy settings.json or config.toml").requiredOption("--expected-policy-sha256 <sha256>", "Exact current managed policy bytes").requiredOption("--expected-settings-sha256 <sha256>", "Exact current native settings bytes").option("--codex-witness-version <version>", "Explicit target: 2 (legacy default)
|
|
114592
|
+
hook.command("rebind-settings").requiredOption("--agent <agent>", "claude or codex").requiredOption("--reviewed-preimage <path>", "Exact preserved legacy settings.json or config.toml").requiredOption("--expected-policy-sha256 <sha256>", "Exact current managed policy bytes").requiredOption("--expected-settings-sha256 <sha256>", "Exact current native settings bytes").option("--codex-witness-version <version>", "Explicit target: 2 (legacy default) 3 (service tier and model-advertised effort), or 4 (native availability UI counts)").option("--apply", "Apply the explicit semantic witness upgrade with preservation and readback", false).option("--json", "Return the metadata-only plan or receipt", false).description("Explicitly migrate a legacy native settings witness after proving only known preferences changed").action(async (options) => {
|
|
114282
114593
|
try {
|
|
114283
114594
|
if (!["claude", "codex"].includes(options.agent))
|
|
114284
114595
|
throw new Error("Settings witness rebind accepts claude or codex");
|
|
114285
|
-
if (options.codexWitnessVersion !== undefined && !["2", "3"].includes(options.codexWitnessVersion))
|
|
114286
|
-
throw new Error("Codex witness version accepts 2 or
|
|
114596
|
+
if (options.codexWitnessVersion !== undefined && !["2", "3", "4"].includes(options.codexWitnessVersion))
|
|
114597
|
+
throw new Error("Codex witness version accepts 2, 3 or 4");
|
|
114287
114598
|
const plan = planAgentSettingsWitnessUpgrade({ ...options.codexWitnessVersion !== undefined ? { targetCodexVersion: Number(options.codexWitnessVersion) } : {}, agent: options.agent, reviewedPreimage: options.reviewedPreimage, expectedPolicySha256: options.expectedPolicySha256, expectedSettingsSha256: options.expectedSettingsSha256 });
|
|
114288
114599
|
const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
|
|
114289
114600
|
const receipt = { applied: options.apply, settingsWitnessUpgrade: plan.settingsWitnessUpgrade, ...result2 };
|
|
@@ -114779,7 +115090,7 @@ var init_grants = __esm(() => {
|
|
|
114779
115090
|
});
|
|
114780
115091
|
|
|
114781
115092
|
// src/lib/station-hydrate.ts
|
|
114782
|
-
import { createHash as
|
|
115093
|
+
import { createHash as createHash36 } from "crypto";
|
|
114783
115094
|
import {
|
|
114784
115095
|
copyFileSync as copyFileSync3,
|
|
114785
115096
|
mkdirSync as mkdirSync28,
|
|
@@ -114982,7 +115293,7 @@ function skillSha256(skill) {
|
|
|
114982
115293
|
return sha256File(skill.files[0].winner.fullPath);
|
|
114983
115294
|
}
|
|
114984
115295
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
114985
|
-
return
|
|
115296
|
+
return createHash36("sha256").update(joined.sort().join(`
|
|
114986
115297
|
`)).digest("hex");
|
|
114987
115298
|
}
|
|
114988
115299
|
function writeStationHydration(options) {
|
|
@@ -115139,7 +115450,7 @@ var init_hydrate = __esm(() => {
|
|
|
115139
115450
|
});
|
|
115140
115451
|
|
|
115141
115452
|
// src/lib/prepare-skill.ts
|
|
115142
|
-
import { createHash as
|
|
115453
|
+
import { createHash as createHash37, randomUUID as randomUUID12 } from "crypto";
|
|
115143
115454
|
import { chmodSync as chmodSync5, existsSync as existsSync37, lstatSync as lstatSync35, mkdirSync as mkdirSync29, mkdtempSync as mkdtempSync6, readFileSync as readFileSync44, readdirSync as readdirSync25, renameSync as renameSync14, rmSync as rmSync9, writeFileSync as writeFileSync30 } from "fs";
|
|
115144
115455
|
import { tmpdir as tmpdir2 } from "os";
|
|
115145
115456
|
import { dirname as dirname37, isAbsolute as isAbsolute31, join as join65, relative as relative12, resolve as resolve29, sep as sep15 } from "path";
|
|
@@ -115338,7 +115649,7 @@ function snapshot2(root2) {
|
|
|
115338
115649
|
if (rootStat.isSymbolicLink() || !rootStat.isDirectory())
|
|
115339
115650
|
throw new Error("Skill root must be a real directory, not a symlink.");
|
|
115340
115651
|
const files = [], directories = [];
|
|
115341
|
-
const hash6 =
|
|
115652
|
+
const hash6 = createHash37("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
|
|
115342
115653
|
let count = 0, bytes = 0;
|
|
115343
115654
|
const walk2 = (relative13) => {
|
|
115344
115655
|
for (const name of readdirSync25(join65(root2, relative13)).sort()) {
|
|
@@ -115769,7 +116080,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
115769
116080
|
const includeDocs = options.includeDocs ?? true;
|
|
115770
116081
|
const includeRequirements = options.includeRequirements ?? true;
|
|
115771
116082
|
const includeValidation = options.includeValidation ?? true;
|
|
115772
|
-
const
|
|
116083
|
+
const packageName2 = options.packageName ?? "@hasna/skills";
|
|
115773
116084
|
const packageVersion = options.packageVersion ?? package_default.version;
|
|
115774
116085
|
const sourceRepository = options.sourceRepository ?? "hasna/skills";
|
|
115775
116086
|
const registry2 = [...loadRegistryProfile(profile)].sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -115787,7 +116098,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
115787
116098
|
tags: [...skill.tags].sort(),
|
|
115788
116099
|
dependencies: [...skill.dependencies ?? []].sort(),
|
|
115789
116100
|
source: {
|
|
115790
|
-
packageName,
|
|
116101
|
+
packageName: packageName2,
|
|
115791
116102
|
packageVersion,
|
|
115792
116103
|
repository: sourceRepository,
|
|
115793
116104
|
directory: directory2
|
|
@@ -115805,7 +116116,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
115805
116116
|
return {
|
|
115806
116117
|
schemaVersion: 1,
|
|
115807
116118
|
source: {
|
|
115808
|
-
packageName,
|
|
116119
|
+
packageName: packageName2,
|
|
115809
116120
|
packageVersion,
|
|
115810
116121
|
repository: sourceRepository,
|
|
115811
116122
|
profile
|
|
@@ -115881,10 +116192,10 @@ async function exportSkillVersionForReview(spec, output2, client2) {
|
|
|
115881
116192
|
const target = assertPrivateDestination(output2);
|
|
115882
116193
|
if (existsSync38(target))
|
|
115883
116194
|
throw new Error("Review output already exists; choose a new filename.");
|
|
115884
|
-
const
|
|
115885
|
-
if (!
|
|
116195
|
+
const metadata2 = await client2.getSkillVersion(slug2, version2);
|
|
116196
|
+
if (!metadata2)
|
|
115886
116197
|
throw new Error(`Published skill version ${slug2}@${version2} was not found.`);
|
|
115887
|
-
if (!/^[a-f0-9]{64}$/i.test(
|
|
116198
|
+
if (!/^[a-f0-9]{64}$/i.test(metadata2.bundleSha256) || !Number.isSafeInteger(metadata2.bundleByteSize) || metadata2.bundleByteSize < 1 || metadata2.bundleByteSize > MAX_REVIEW_BUNDLE_BYTES) {
|
|
115888
116199
|
throw new Error("Published bundle metadata is invalid or exceeds the review size limit.");
|
|
115889
116200
|
}
|
|
115890
116201
|
const response = await client2.getBundle(slug2, version2);
|
|
@@ -115896,12 +116207,12 @@ async function exportSkillVersionForReview(spec, output2, client2) {
|
|
|
115896
116207
|
}
|
|
115897
116208
|
const headerSha = response.headers.get("X-Skill-Bundle-Sha256");
|
|
115898
116209
|
const headerVersion = response.headers.get("X-Skill-Version");
|
|
115899
|
-
if (!headerSha || headerSha.toLowerCase() !==
|
|
116210
|
+
if (!headerSha || headerSha.toLowerCase() !== metadata2.bundleSha256.toLowerCase() || headerVersion !== version2) {
|
|
115900
116211
|
response.body?.cancel().catch(() => {});
|
|
115901
116212
|
throw new Error("Published bundle headers do not match the exact version metadata.");
|
|
115902
116213
|
}
|
|
115903
|
-
const bytes = await readBoundedResponse(response, Math.min(
|
|
115904
|
-
if (bytes.byteLength !==
|
|
116214
|
+
const bytes = await readBoundedResponse(response, Math.min(metadata2.bundleByteSize, MAX_REVIEW_BUNDLE_BYTES));
|
|
116215
|
+
if (bytes.byteLength !== metadata2.bundleByteSize || sha256Hex(bytes) !== metadata2.bundleSha256.toLowerCase()) {
|
|
115905
116216
|
throw new Error("Published bundle bytes do not match the exact version metadata.");
|
|
115906
116217
|
}
|
|
115907
116218
|
const fd = openSync25(target, constants23.O_WRONLY | constants23.O_CREAT | constants23.O_EXCL | constants23.O_NOFOLLOW, 384);
|
|
@@ -115915,7 +116226,7 @@ async function exportSkillVersionForReview(spec, output2, client2) {
|
|
|
115915
116226
|
} finally {
|
|
115916
116227
|
closeSync25(fd);
|
|
115917
116228
|
}
|
|
115918
|
-
return { slug: slug2, version: version2, bundleSha256:
|
|
116229
|
+
return { slug: slug2, version: version2, bundleSha256: metadata2.bundleSha256.toLowerCase(), bundleByteSize: bytes.byteLength, output: target };
|
|
115919
116230
|
}
|
|
115920
116231
|
var MAX_REVIEW_BUNDLE_BYTES, SLUG2, NATIVE_CONTAINERS;
|
|
115921
116232
|
var init_review_export = __esm(() => {
|
|
@@ -118099,10 +118410,10 @@ async function reconcileRegistry(options = {}) {
|
|
|
118099
118410
|
throw new ReconcileRegistryError(`Registry listing failed: expected an array of skills, got ${shape}.`, ["Check HASNA_SKILLS_API_URL and the resolved credential; a failed listing must not be read as an empty registry."]);
|
|
118100
118411
|
}
|
|
118101
118412
|
const remotes = new Map;
|
|
118102
|
-
for (const
|
|
118103
|
-
if (!
|
|
118413
|
+
for (const row2 of remoteRowsPayload) {
|
|
118414
|
+
if (!row2 || typeof row2 !== "object")
|
|
118104
118415
|
continue;
|
|
118105
|
-
const skill = remoteRowToSkill(
|
|
118416
|
+
const skill = remoteRowToSkill(row2);
|
|
118106
118417
|
if (skill)
|
|
118107
118418
|
remotes.set(skill.slug, skill);
|
|
118108
118419
|
}
|