@hasna/skills 0.10.34 → 0.10.36
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 +6 -0
- package/bin/index.js +558 -338
- package/bin/mcp.js +124 -123
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +413 -219
- package/dist/lib/agent-integration.d.ts +27 -0
- package/dist/lib/codex-plugin-skill-controls.d.ts +1 -1
- package/dist/sdk/index.js +204 -11
- 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.36",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -51577,7 +51577,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
|
|
|
51577
51577
|
return false;
|
|
51578
51578
|
}
|
|
51579
51579
|
}
|
|
51580
|
-
function reviewCodexPluginControls(catalog, documents, cache3, cwd2, read2, rules, pluginSettings) {
|
|
51580
|
+
function reviewCodexPluginControls(catalog, documents, cache3, cwd2, read2, rules, pluginSettings, plannedRules) {
|
|
51581
51581
|
if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd2)
|
|
51582
51582
|
refuse2();
|
|
51583
51583
|
const skills = projectCodexNativeSkillCatalog({ data: [{ cwd: cwd2, errors: [], skills: catalog.skills }] }, cwd2);
|
|
@@ -51648,7 +51648,9 @@ function reviewCodexPluginControls(catalog, documents, cache3, cwd2, read2, rule
|
|
|
51648
51648
|
remotePluginId = plugin.remotePluginId;
|
|
51649
51649
|
}
|
|
51650
51650
|
const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document3 && rule?.enabled === false);
|
|
51651
|
-
|
|
51651
|
+
const nativeSkill = { name: parsed.name, path: document3, pluginId: expectedPluginId, enabled: true };
|
|
51652
|
+
const plannedPathDeny = catalog.version === "codex-cli 0.160.0" && remotePluginId !== undefined && Array.isArray(rules) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === parsed.name && rule.enabled === false) && !rules.some((rule) => (rule?.path === document3 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document3 && rule.enabled === false) && isCodexNativeSkillDisabled(nativeSkill, plannedRules);
|
|
51653
|
+
if (!exactPathDeny && !plannedPathDeny || !isCodexNativeSkillDisabled(nativeSkill, rules))
|
|
51652
51654
|
refuse2();
|
|
51653
51655
|
}
|
|
51654
51656
|
result2.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
|
|
@@ -52593,18 +52595,80 @@ var init_agent_sumi = __esm(() => {
|
|
|
52593
52595
|
];
|
|
52594
52596
|
});
|
|
52595
52597
|
|
|
52598
|
+
// src/lib/codex-hook-trust-files.ts
|
|
52599
|
+
import { closeSync as closeSync8, constants as constants8, fstatSync as fstatSync8, fsyncSync as fsyncSync2, lstatSync as lstatSync16, openSync as openSync8, readSync as readSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
52600
|
+
import { createHash as createHash12 } from "crypto";
|
|
52601
|
+
import { dirname as dirname11 } from "path";
|
|
52602
|
+
function need5(value, code) {
|
|
52603
|
+
if (!value)
|
|
52604
|
+
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
52605
|
+
}
|
|
52606
|
+
function safeParents(file) {
|
|
52607
|
+
for (let p = dirname11(file);; p = dirname11(p)) {
|
|
52608
|
+
const s = lstatSync16(p);
|
|
52609
|
+
need5(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
|
|
52610
|
+
need5((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
52611
|
+
if (dirname11(p) === p)
|
|
52612
|
+
break;
|
|
52613
|
+
}
|
|
52614
|
+
}
|
|
52615
|
+
function snapshot(file, privateFile = false, options = {}) {
|
|
52616
|
+
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
52617
|
+
safeParents(file);
|
|
52618
|
+
const initial2 = lstatSync16(file, { bigint: true });
|
|
52619
|
+
need5(initial2.isFile() && initial2.uid === BigInt(process.getuid()) && (initial2.nlink === 1n || readOnlyPackage && initial2.nlink > 1n) && (initial2.mode & 0o022n) === 0n && (!privateFile || (initial2.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
|
|
52620
|
+
need5(initial2.size <= 8n * 1024n * 1024n, "FILE_BOUND");
|
|
52621
|
+
const fd = openSync8(file, constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK);
|
|
52622
|
+
try {
|
|
52623
|
+
const before2 = fstatSync8(fd, { bigint: true });
|
|
52624
|
+
need5(sameStat(initial2, before2), "INPUT_CHANGED");
|
|
52625
|
+
const bytes = Buffer.alloc(Number(before2.size) + 1);
|
|
52626
|
+
let length = 0;
|
|
52627
|
+
while (length < bytes.length) {
|
|
52628
|
+
const n2 = readSync6(fd, bytes, length, bytes.length - length, null);
|
|
52629
|
+
if (!n2)
|
|
52630
|
+
break;
|
|
52631
|
+
length += n2;
|
|
52632
|
+
}
|
|
52633
|
+
need5(length === Number(before2.size) && sameStat(before2, fstatSync8(fd, { bigint: true })) && sameStat(before2, lstatSync16(file, { bigint: true })), "INPUT_CHANGED");
|
|
52634
|
+
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
52635
|
+
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before2, sha256: createHash12("sha256").update(content).digest("hex") };
|
|
52636
|
+
} finally {
|
|
52637
|
+
closeSync8(fd);
|
|
52638
|
+
}
|
|
52639
|
+
}
|
|
52640
|
+
function unchanged2(before2) {
|
|
52641
|
+
const after2 = snapshot(before2.file, before2.privateFile, before2.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
52642
|
+
need5(sameStat(before2.stat, after2.stat) && after2.sha256 === before2.sha256, "INPUT_CHANGED");
|
|
52643
|
+
}
|
|
52644
|
+
function save(file, content) {
|
|
52645
|
+
safeParents(file);
|
|
52646
|
+
const fd = openSync8(file, constants8.O_WRONLY | constants8.O_CREAT | constants8.O_EXCL | constants8.O_NOFOLLOW, 384);
|
|
52647
|
+
try {
|
|
52648
|
+
writeFileSync4(fd, content);
|
|
52649
|
+
fsyncSync2(fd);
|
|
52650
|
+
} finally {
|
|
52651
|
+
closeSync8(fd);
|
|
52652
|
+
}
|
|
52653
|
+
}
|
|
52654
|
+
var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
|
|
52655
|
+
var init_codex_hook_trust_files = () => {};
|
|
52656
|
+
|
|
52596
52657
|
// src/lib/agent-integration.ts
|
|
52597
|
-
import { createHash as
|
|
52658
|
+
import { createHash as createHash13, randomUUID as randomUUID5 } from "crypto";
|
|
52598
52659
|
import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
|
|
52599
|
-
import { existsSync as existsSync13, lstatSync as
|
|
52600
|
-
import { dirname as
|
|
52660
|
+
import { existsSync as existsSync13, lstatSync as lstatSync17, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync13, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync7, 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 constants9, linkSync as linkSync2 } from "fs";
|
|
52661
|
+
import { dirname as dirname12, join as join23, resolve as resolve12, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
|
|
52601
52662
|
import { homedir as homedir3 } from "os";
|
|
52602
|
-
function
|
|
52663
|
+
function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
|
|
52603
52664
|
try {
|
|
52604
|
-
const { dlopen, read: read2 } = __require("bun:ffi");
|
|
52665
|
+
const { dlopen, read: read2, ptr } = __require("bun:ffi");
|
|
52605
52666
|
const library = dlopen("/usr/lib/libSystem.B.dylib", {
|
|
52606
52667
|
acl_get_fd: { args: ["i32"], returns: "ptr" },
|
|
52607
52668
|
acl_free: { args: ["ptr"], returns: "i32" },
|
|
52669
|
+
acl_valid: { args: ["ptr"], returns: "i32" },
|
|
52670
|
+
acl_get_entry: { args: ["ptr", "i32", "ptr"], returns: "i32" },
|
|
52671
|
+
acl_get_tag_type: { args: ["ptr", "ptr"], returns: "i32" },
|
|
52608
52672
|
__error: { args: [], returns: "ptr" }
|
|
52609
52673
|
});
|
|
52610
52674
|
try {
|
|
@@ -52613,8 +52677,23 @@ function hasNoDarwinAcl(fd) {
|
|
|
52613
52677
|
return false;
|
|
52614
52678
|
const acl = library.symbols.acl_get_fd(fd);
|
|
52615
52679
|
if (acl) {
|
|
52616
|
-
|
|
52617
|
-
|
|
52680
|
+
try {
|
|
52681
|
+
if (!allowDenyOnly || library.symbols.acl_valid(acl) !== 0)
|
|
52682
|
+
return false;
|
|
52683
|
+
const entry = new BigUint64Array(1), tag = new Int32Array(1);
|
|
52684
|
+
for (let count = 0;count <= 128; count++) {
|
|
52685
|
+
const status = library.symbols.acl_get_entry(acl, count === 0 ? 0 : -1, ptr(entry));
|
|
52686
|
+
if (status !== 0) {
|
|
52687
|
+
return status === -1 && read2.i32(errno) === 22 && library.symbols.acl_valid(acl) === 0;
|
|
52688
|
+
}
|
|
52689
|
+
const value = read2.ptr(ptr(entry));
|
|
52690
|
+
if (count === 128 || !value || library.symbols.acl_get_tag_type(value, ptr(tag)) !== 0 || tag[0] !== 2)
|
|
52691
|
+
return false;
|
|
52692
|
+
}
|
|
52693
|
+
return false;
|
|
52694
|
+
} finally {
|
|
52695
|
+
library.symbols.acl_free(acl);
|
|
52696
|
+
}
|
|
52618
52697
|
}
|
|
52619
52698
|
return (acl === null || acl === 0) && read2.i32(errno) === 2;
|
|
52620
52699
|
} finally {
|
|
@@ -52630,11 +52709,11 @@ function isSystemRootAlias(path) {
|
|
|
52630
52709
|
const target = `/private${path}`, link2 = `private${path}`;
|
|
52631
52710
|
const identity5 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}`;
|
|
52632
52711
|
try {
|
|
52633
|
-
const before2 =
|
|
52712
|
+
const before2 = lstatSync17(path, { bigint: true });
|
|
52634
52713
|
if (!before2.isSymbolicLink() || before2.uid !== 0n || readlinkSync4(path) !== link2)
|
|
52635
52714
|
return false;
|
|
52636
52715
|
const directories = ["/", "/private", target].map((directory) => {
|
|
52637
|
-
const stat =
|
|
52716
|
+
const stat = lstatSync17(directory, { bigint: true });
|
|
52638
52717
|
if (!stat.isDirectory() || stat.uid !== 0n)
|
|
52639
52718
|
throw new Error("Untrusted system alias directory");
|
|
52640
52719
|
if (directory !== "/private/tmp") {
|
|
@@ -52642,23 +52721,23 @@ function isSystemRootAlias(path) {
|
|
|
52642
52721
|
throw new Error("Writable system alias directory");
|
|
52643
52722
|
} else if ((stat.mode & 0o1000n) === 0n)
|
|
52644
52723
|
throw new Error("Unprotected system temporary directory");
|
|
52645
|
-
const fd =
|
|
52724
|
+
const fd = openSync9(directory, constants9.O_RDONLY | constants9.O_DIRECTORY | constants9.O_NOFOLLOW);
|
|
52646
52725
|
try {
|
|
52647
|
-
if (identity5(
|
|
52726
|
+
if (identity5(fstatSync9(fd, { bigint: true })) !== identity5(stat) || !hasTrustedDarwinAcl(fd) || identity5(fstatSync9(fd, { bigint: true })) !== identity5(stat))
|
|
52648
52727
|
throw new Error("Unverified system alias directory ACL");
|
|
52649
52728
|
} finally {
|
|
52650
|
-
|
|
52729
|
+
closeSync9(fd);
|
|
52651
52730
|
}
|
|
52652
52731
|
return { directory, identity: identity5(stat) };
|
|
52653
52732
|
});
|
|
52654
|
-
return realpathSync7(path) === target && readlinkSync4(path) === link2 && identity5(
|
|
52733
|
+
return realpathSync7(path) === target && readlinkSync4(path) === link2 && identity5(lstatSync17(path, { bigint: true })) === identity5(before2) && directories.every((item) => identity5(lstatSync17(item.directory, { bigint: true })) === item.identity);
|
|
52655
52734
|
} catch {
|
|
52656
52735
|
return false;
|
|
52657
52736
|
}
|
|
52658
52737
|
}
|
|
52659
52738
|
function canonicalSystemPath(path) {
|
|
52660
52739
|
const absolute = resolve12(path), root = `/${absolute.split(sep5)[1]}`;
|
|
52661
|
-
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !
|
|
52740
|
+
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync17(root, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
52662
52741
|
return absolute;
|
|
52663
52742
|
if (!isSystemRootAlias(root))
|
|
52664
52743
|
throw new Error(`Refusing symlink path: ${root}`);
|
|
@@ -52667,9 +52746,9 @@ function canonicalSystemPath(path) {
|
|
|
52667
52746
|
function assertSafePath(path) {
|
|
52668
52747
|
let cursor = resolve12(path);
|
|
52669
52748
|
while (true) {
|
|
52670
|
-
if (
|
|
52749
|
+
if (lstatSync17(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
|
|
52671
52750
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
52672
|
-
const parent =
|
|
52751
|
+
const parent = dirname12(cursor);
|
|
52673
52752
|
if (parent === cursor)
|
|
52674
52753
|
return;
|
|
52675
52754
|
cursor = parent;
|
|
@@ -52678,12 +52757,12 @@ function assertSafePath(path) {
|
|
|
52678
52757
|
function readRootAlias(home, agent) {
|
|
52679
52758
|
assertSafePath(home);
|
|
52680
52759
|
const realHome = realpathSync7(home), alias = join23(realHome, `.${agent}`);
|
|
52681
|
-
const stat =
|
|
52760
|
+
const stat = lstatSync17(alias, { throwIfNoEntry: false, bigint: true });
|
|
52682
52761
|
if (!stat?.isSymbolicLink())
|
|
52683
52762
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
52684
|
-
const link2 = readlinkSync4(alias), targetPath = resolve12(
|
|
52763
|
+
const link2 = readlinkSync4(alias), targetPath = resolve12(dirname12(alias), link2);
|
|
52685
52764
|
assertSafePath(targetPath);
|
|
52686
|
-
const targetStat =
|
|
52765
|
+
const targetStat = lstatSync17(targetPath, { throwIfNoEntry: false });
|
|
52687
52766
|
if (!targetStat?.isDirectory())
|
|
52688
52767
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
52689
52768
|
const target = realpathSync7(targetPath), inside = relative5(realHome, target);
|
|
@@ -52696,7 +52775,7 @@ function rootAliases(home, allowed = false) {
|
|
|
52696
52775
|
return [];
|
|
52697
52776
|
const result2 = [];
|
|
52698
52777
|
for (const agent of ["claude", "codex"])
|
|
52699
|
-
if (
|
|
52778
|
+
if (lstatSync17(join23(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
|
|
52700
52779
|
result2.push(readRootAlias(home, agent));
|
|
52701
52780
|
if (result2.some((item, index) => result2.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep5) || other.target.startsWith(item.target + sep5))))
|
|
52702
52781
|
throw new Error("Agent root alias targets must not overlap");
|
|
@@ -52731,7 +52810,7 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
52731
52810
|
continue;
|
|
52732
52811
|
settings.set(document3, [...settings.get(document3) ?? [], entry.enabled === false]);
|
|
52733
52812
|
}
|
|
52734
|
-
return [...settings].filter(([, values2]) => values2.length === 1 && values2[0] === true).map(([document3]) =>
|
|
52813
|
+
return [...settings].filter(([, values2]) => values2.length === 1 && values2[0] === true).map(([document3]) => dirname12(document3));
|
|
52735
52814
|
}
|
|
52736
52815
|
function recheckRootAliases(aliases) {
|
|
52737
52816
|
for (const binding of aliases) {
|
|
@@ -52744,35 +52823,35 @@ function recheckRootAliases(aliases) {
|
|
|
52744
52823
|
}
|
|
52745
52824
|
function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
52746
52825
|
assertSafePath(path);
|
|
52747
|
-
const before2 =
|
|
52826
|
+
const before2 = lstatSync17(path);
|
|
52748
52827
|
if (!before2.isFile() || before2.size > maximum)
|
|
52749
52828
|
throw new Error("Unsupported or oversized native skill file");
|
|
52750
|
-
const descriptor =
|
|
52829
|
+
const descriptor = openSync9(path, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
|
|
52751
52830
|
try {
|
|
52752
|
-
const opened =
|
|
52831
|
+
const opened = fstatSync9(descriptor);
|
|
52753
52832
|
if (!opened.isFile() || opened.dev !== before2.dev || opened.ino !== before2.ino || opened.size !== before2.size)
|
|
52754
52833
|
throw new Error("Native skill file changed while reading");
|
|
52755
52834
|
const bytes = Buffer.allocUnsafe(before2.size + 1);
|
|
52756
52835
|
let length = 0;
|
|
52757
52836
|
while (length < bytes.length) {
|
|
52758
|
-
const count =
|
|
52837
|
+
const count = readSync7(descriptor, bytes, length, bytes.length - length, null);
|
|
52759
52838
|
if (!count)
|
|
52760
52839
|
break;
|
|
52761
52840
|
length += count;
|
|
52762
52841
|
}
|
|
52763
|
-
const after2 =
|
|
52842
|
+
const after2 = fstatSync9(descriptor), current = lstatSync17(path);
|
|
52764
52843
|
if (current.dev !== opened.dev || current.ino !== opened.ino || current.size !== after2.size || current.mtimeMs !== after2.mtimeMs || current.ctimeMs !== after2.ctimeMs || length !== before2.size || after2.size !== before2.size || after2.mtimeMs !== opened.mtimeMs || after2.ctimeMs !== opened.ctimeMs)
|
|
52765
52844
|
throw new Error("Native skill file changed while reading");
|
|
52766
52845
|
return bytes.subarray(0, length);
|
|
52767
52846
|
} finally {
|
|
52768
|
-
|
|
52847
|
+
closeSync9(descriptor);
|
|
52769
52848
|
}
|
|
52770
52849
|
}
|
|
52771
52850
|
function treeHash(root) {
|
|
52772
|
-
const hash2 =
|
|
52851
|
+
const hash2 = createHash13("sha256");
|
|
52773
52852
|
let bytes = 0, files = 0;
|
|
52774
52853
|
function visit2(path) {
|
|
52775
|
-
const stat =
|
|
52854
|
+
const stat = lstatSync17(path);
|
|
52776
52855
|
if (stat.isSymbolicLink())
|
|
52777
52856
|
throw new Error(`Refusing symlink in skill: ${path}`);
|
|
52778
52857
|
const rel = relative5(root, path);
|
|
@@ -52823,7 +52902,7 @@ function parseNativeMigrationTargetManifest(input) {
|
|
|
52823
52902
|
if (!isAbsolute11(item.projectRoot) || resolve12(item.projectRoot) !== item.projectRoot)
|
|
52824
52903
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${item.projectRoot}`);
|
|
52825
52904
|
const projectRoot = canonicalSystemPath(item.projectRoot);
|
|
52826
|
-
if (!existsSync13(projectRoot) || !
|
|
52905
|
+
if (!existsSync13(projectRoot) || !lstatSync17(projectRoot).isDirectory())
|
|
52827
52906
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${item.projectRoot}`);
|
|
52828
52907
|
assertSafePath(projectRoot);
|
|
52829
52908
|
if (isAbsolute11(item.path) || item.path.includes("\\") || item.path.length === 0 || item.path.includes("\x00"))
|
|
@@ -52853,7 +52932,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
52853
52932
|
if (!isAbsolute11(target.projectRoot) || resolve12(target.projectRoot) !== target.projectRoot)
|
|
52854
52933
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
52855
52934
|
const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative5(projectRoot, resolve12(projectRoot, target.path));
|
|
52856
|
-
if (!existsSync13(projectRoot) || !
|
|
52935
|
+
if (!existsSync13(projectRoot) || !lstatSync17(projectRoot).isDirectory())
|
|
52857
52936
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
52858
52937
|
assertSafePath(projectRoot);
|
|
52859
52938
|
if (isAbsolute11(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep5}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
|
|
@@ -52882,7 +52961,7 @@ function projectAncestorDirectories(projects) {
|
|
|
52882
52961
|
if (depth >= 100)
|
|
52883
52962
|
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
52884
52963
|
directories.add(path);
|
|
52885
|
-
const parent =
|
|
52964
|
+
const parent = dirname12(path);
|
|
52886
52965
|
if (parent === path)
|
|
52887
52966
|
break;
|
|
52888
52967
|
path = parent;
|
|
@@ -52928,12 +53007,12 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
52928
53007
|
let reviewedCacheAliasFound = false;
|
|
52929
53008
|
function verifiedSiblingCacheAlias(agent, path, parent) {
|
|
52930
53009
|
const link2 = readlinkSync4(path), lexicalTarget = resolve12(parent, link2);
|
|
52931
|
-
if (isAbsolute11(link2) ? link2 !== lexicalTarget :
|
|
53010
|
+
if (isAbsolute11(link2) ? link2 !== lexicalTarget : dirname12(link2) !== ".")
|
|
52932
53011
|
return false;
|
|
52933
53012
|
const target = canonicalSystemPath(lexicalTarget);
|
|
52934
|
-
if (
|
|
53013
|
+
if (dirname12(target) !== parent)
|
|
52935
53014
|
return false;
|
|
52936
|
-
if (!
|
|
53015
|
+
if (!lstatSync17(target, { throwIfNoEntry: false })?.isDirectory())
|
|
52937
53016
|
return false;
|
|
52938
53017
|
const scan = cacheScans.get(target);
|
|
52939
53018
|
if (!scan?.complete || scan.entries > maxAliasProofEntries)
|
|
@@ -52958,7 +53037,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
52958
53037
|
return scan;
|
|
52959
53038
|
if (!existsSync13(path))
|
|
52960
53039
|
return scan;
|
|
52961
|
-
const stat =
|
|
53040
|
+
const stat = lstatSync17(path);
|
|
52962
53041
|
if (stat.isFile()) {
|
|
52963
53042
|
if (agent === "sumi" && depth === 1 && path.endsWith(".md"))
|
|
52964
53043
|
throw new Error("NATIVE_SKILL_DRIFT: flat Sumi skill documents require a dedicated migration adapter");
|
|
@@ -53007,7 +53086,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
53007
53086
|
if (isVendor && !options.includeVendor)
|
|
53008
53087
|
continue;
|
|
53009
53088
|
const child = join23(path, name);
|
|
53010
|
-
if (isVendor &&
|
|
53089
|
+
if (isVendor && lstatSync17(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
53011
53090
|
const target = statSync8(child, { throwIfNoEntry: false });
|
|
53012
53091
|
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
|
|
53013
53092
|
scan.entries++;
|
|
@@ -53222,15 +53301,21 @@ function planAgentIntegration(options) {
|
|
|
53222
53301
|
}
|
|
53223
53302
|
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
53224
53303
|
});
|
|
53225
|
-
const
|
|
53304
|
+
const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases)) : null;
|
|
53305
|
+
const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
|
|
53226
53306
|
const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
|
|
53307
|
+
const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join23(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
|
|
53308
|
+
const plannedCodexRules = codexPathConfig === undefined ? undefined : Bun.TOML.parse(codexPathConfig).skills?.config ?? [];
|
|
53227
53309
|
const observedNativeSources = [];
|
|
53228
53310
|
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join23(entry.path, "SKILL.md")), canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), resolve12(options.projectDir ?? home), (path) => {
|
|
53229
53311
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
53230
53312
|
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
53231
53313
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
53232
|
-
}, codexConfig.skills?.config ?? [], codexConfig.plugins) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
|
|
53314
|
+
}, codexConfig.skills?.config ?? [], codexConfig.plugins, plannedCodexRules) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
|
|
53233
53315
|
const codexPluginSkills = codexPluginControls.skills;
|
|
53316
|
+
if (options.agents.includes("codex") && !options.codexNativeCatalog && codexPluginSkills.length && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), codexPluginSkills, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)))) {
|
|
53317
|
+
throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; provide a fresh native catalog and discovery review");
|
|
53318
|
+
}
|
|
53234
53319
|
const codexInactivePlugins = codexPluginControls.inactivePlugins;
|
|
53235
53320
|
const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
|
|
53236
53321
|
const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root + sep5)) && codexDiscovery.sources.some((source) => source.path === join23(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
|
|
@@ -53305,7 +53390,9 @@ function planAgentIntegration(options) {
|
|
|
53305
53390
|
changes.push({ path, before: before2, after: after2 });
|
|
53306
53391
|
if (agent === "codex") {
|
|
53307
53392
|
const configPath = canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
|
|
53308
|
-
|
|
53393
|
+
if (previous !== codexConfigBefore)
|
|
53394
|
+
throw new Error(`Configuration changed during planning: ${configPath}`);
|
|
53395
|
+
const next = disableReviewedCodexPluginNames(codexPathConfig, codexPluginSkills);
|
|
53309
53396
|
if (next !== (previous ?? ""))
|
|
53310
53397
|
changes.push({ path: configPath, before: previous, after: next });
|
|
53311
53398
|
}
|
|
@@ -53341,6 +53428,171 @@ function planAgentIntegration(options) {
|
|
|
53341
53428
|
recheckRootAliases(aliases);
|
|
53342
53429
|
return { dataDir: dataDir3, profileId, changes, nativeSkills, ...observedNativeSources.length ? { observedNativeSources } : {}, ...nextPolicy.bridge.codexPluginSkillReview ? { codexPluginSkillReview: nextPolicy.bridge.codexPluginSkillReview } : {}, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
|
|
53343
53430
|
}
|
|
53431
|
+
function hookProjectionRefusal(reason) {
|
|
53432
|
+
throw new Error(`CLAUDE_MANAGED_HOOK_${reason}`);
|
|
53433
|
+
}
|
|
53434
|
+
function projectedHookAcl(path, observed) {
|
|
53435
|
+
if (process.platform !== "darwin")
|
|
53436
|
+
return;
|
|
53437
|
+
const identity5 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}:${stat.nlink}:${stat.size}:${stat.mtimeNs}`;
|
|
53438
|
+
const fd = openSync9(path, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK | (observed.isDirectory() ? constants9.O_DIRECTORY : 0));
|
|
53439
|
+
try {
|
|
53440
|
+
if (identity5(fstatSync9(fd, { bigint: true })) !== identity5(observed) || !hasTrustedDarwinAcl(fd, true) || identity5(fstatSync9(fd, { bigint: true })) !== identity5(observed) || identity5(lstatSync17(path, { bigint: true })) !== identity5(observed))
|
|
53441
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53442
|
+
} finally {
|
|
53443
|
+
closeSync9(fd);
|
|
53444
|
+
}
|
|
53445
|
+
}
|
|
53446
|
+
function projectedHookExecutable(command, expectedResolved) {
|
|
53447
|
+
try {
|
|
53448
|
+
if (!isAbsolute11(command) || /[\0\r\n]/.test(command) || resolve12(command) !== command)
|
|
53449
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53450
|
+
command = canonicalSystemPath(command);
|
|
53451
|
+
const parents2 = (file = command) => {
|
|
53452
|
+
const result2 = [];
|
|
53453
|
+
for (let path = dirname12(file);; path = dirname12(path)) {
|
|
53454
|
+
const stat = lstatSync17(path, { bigint: true });
|
|
53455
|
+
if (!stat.isDirectory() || (stat.mode & 0o022n) !== 0n && !(stat.uid === 0n && (stat.mode & 0o1000n) !== 0n))
|
|
53456
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53457
|
+
projectedHookAcl(path, stat);
|
|
53458
|
+
result2.push(`${path}:${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}`);
|
|
53459
|
+
if (dirname12(path) === path)
|
|
53460
|
+
break;
|
|
53461
|
+
}
|
|
53462
|
+
return result2;
|
|
53463
|
+
};
|
|
53464
|
+
const parentIdentity = parents2(), link2 = lstatSync17(command, { bigint: true });
|
|
53465
|
+
if (link2.uid !== BigInt(process.getuid()) || !link2.isFile() && !link2.isSymbolicLink())
|
|
53466
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53467
|
+
const linkIdentity = (stat) => `${stat.dev}:${stat.ino}:${stat.mode}:${stat.uid}:${stat.ctimeNs}`;
|
|
53468
|
+
const resolved = realpathSync7(command);
|
|
53469
|
+
if (expectedResolved !== undefined && resolved !== expectedResolved)
|
|
53470
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53471
|
+
const resolvedParentIdentity = parents2(resolved);
|
|
53472
|
+
const manifest = snapshot(join23(dirname12(dirname12(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
53473
|
+
projectedHookAcl(manifest.file, manifest.stat);
|
|
53474
|
+
const pkg = JSON.parse(manifest.text);
|
|
53475
|
+
if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join23(dirname12(manifest.file), "bin/index.js"))
|
|
53476
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53477
|
+
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
53478
|
+
projectedHookAcl(cli.file, cli.stat);
|
|
53479
|
+
if ((cli.stat.mode & 0o100n) === 0n)
|
|
53480
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53481
|
+
const recheck = () => {
|
|
53482
|
+
try {
|
|
53483
|
+
if (realpathSync7(command) !== resolved || linkIdentity(lstatSync17(command, { bigint: true })) !== linkIdentity(link2) || !isDeepStrictEqual5(parents2(), parentIdentity) || !isDeepStrictEqual5(parents2(resolved), resolvedParentIdentity))
|
|
53484
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53485
|
+
projectedHookAcl(cli.file, cli.stat);
|
|
53486
|
+
projectedHookAcl(manifest.file, manifest.stat);
|
|
53487
|
+
unchanged2(cli);
|
|
53488
|
+
unchanged2(manifest);
|
|
53489
|
+
} catch {
|
|
53490
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53491
|
+
}
|
|
53492
|
+
};
|
|
53493
|
+
recheck();
|
|
53494
|
+
return { resolved, sha256: cli.sha256, manifestSha256: manifest.sha256, recheck };
|
|
53495
|
+
} catch {
|
|
53496
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53497
|
+
}
|
|
53498
|
+
}
|
|
53499
|
+
function planClaudeManagedHookProjection(options) {
|
|
53500
|
+
if (typeof options.targetSettings !== "string" || Buffer.byteLength(options.targetSettings) > 1024 * 1024)
|
|
53501
|
+
hookProjectionRefusal("TARGET_INVALID");
|
|
53502
|
+
const targetSha256 = sha3(options.targetSettings);
|
|
53503
|
+
if (!/^[a-f0-9]{64}$/.test(options.expectedTargetSha256) || options.expectedTargetSha256 !== targetSha256)
|
|
53504
|
+
hookProjectionRefusal("TARGET_CHANGED");
|
|
53505
|
+
let target;
|
|
53506
|
+
try {
|
|
53507
|
+
target = jsonObject(options.targetSettings, "target");
|
|
53508
|
+
} catch {
|
|
53509
|
+
hookProjectionRefusal("TARGET_INVALID");
|
|
53510
|
+
}
|
|
53511
|
+
const home = resolve12(options.home ?? homedir3()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
|
|
53512
|
+
const policyPath = join23(dataDir3, "agent-policy.json");
|
|
53513
|
+
let policyBytes, settingsBytes, settingsPath, command, profile;
|
|
53514
|
+
try {
|
|
53515
|
+
policyBytes = readNativeBytes(policyPath, 1024 * 1024);
|
|
53516
|
+
const snapshot2 = readManagedSkillPolicySnapshot(dataDir3);
|
|
53517
|
+
if (!snapshot2 || snapshot2.text !== policyBytes.toString("utf8"))
|
|
53518
|
+
hookProjectionRefusal("SOURCE_UNVERIFIED");
|
|
53519
|
+
const binding = snapshot2.value.bridge;
|
|
53520
|
+
settingsPath = canonicalAgentPath(join23(home, AGENT_ADAPTERS.claude.config), binding?.rootAliases ?? []);
|
|
53521
|
+
settingsBytes = readNativeBytes(settingsPath, 1024 * 1024);
|
|
53522
|
+
command = binding?.commands?.claude;
|
|
53523
|
+
profile = binding?.profiles?.claude;
|
|
53524
|
+
if (typeof command !== "string" || typeof profile !== "string")
|
|
53525
|
+
hookProjectionRefusal("SOURCE_UNVERIFIED");
|
|
53526
|
+
assertManagedAgentBridge("claude", { home, dataDir: dataDir3, projectDir: options.projectDir, profileId: profile });
|
|
53527
|
+
} catch {
|
|
53528
|
+
hookProjectionRefusal("SOURCE_UNVERIFIED");
|
|
53529
|
+
}
|
|
53530
|
+
const executable = projectedHookExecutable(command);
|
|
53531
|
+
const sourceSha256 = sha3(JSON.stringify({
|
|
53532
|
+
home,
|
|
53533
|
+
policyPath,
|
|
53534
|
+
policy: sha3(policyBytes),
|
|
53535
|
+
settingsPath,
|
|
53536
|
+
settings: sha3(settingsBytes),
|
|
53537
|
+
executable: { path: executable.resolved, sha256: executable.sha256, manifestSha256: executable.manifestSha256 }
|
|
53538
|
+
}));
|
|
53539
|
+
if (options.expectedSourceSha256 !== undefined && options.expectedSourceSha256 !== sourceSha256)
|
|
53540
|
+
hookProjectionRefusal("SOURCE_CHANGED");
|
|
53541
|
+
const replacements2 = [];
|
|
53542
|
+
const observed = [executable];
|
|
53543
|
+
if (target.hooks !== undefined) {
|
|
53544
|
+
if (!target.hooks || typeof target.hooks !== "object" || Array.isArray(target.hooks))
|
|
53545
|
+
hookProjectionRefusal("TARGET_INVALID");
|
|
53546
|
+
for (const event of [...AGENT_ADAPTERS.claude.events, "PreToolUse"]) {
|
|
53547
|
+
const groups = target.hooks[event];
|
|
53548
|
+
if (groups === undefined)
|
|
53549
|
+
continue;
|
|
53550
|
+
if (!Array.isArray(groups))
|
|
53551
|
+
hookProjectionRefusal("TARGET_INVALID");
|
|
53552
|
+
let recognized = 0;
|
|
53553
|
+
for (const [groupIndex, group] of groups.entries()) {
|
|
53554
|
+
if (!group || !Array.isArray(group.hooks))
|
|
53555
|
+
hookProjectionRefusal("TARGET_INVALID");
|
|
53556
|
+
for (const [hookIndex, hook] of group.hooks.entries()) {
|
|
53557
|
+
if (hook?.type !== "command" || typeof hook.command !== "string")
|
|
53558
|
+
continue;
|
|
53559
|
+
if (!hook.command.includes("hook user-prompt --agent claude"))
|
|
53560
|
+
continue;
|
|
53561
|
+
const match = /^('(?:[^']|'\\'')*') hook user-prompt --agent claude --selection-profile ([A-Za-z0-9][A-Za-z0-9._-]{0,127}) --event ([A-Za-z]+)$/.exec(hook.command);
|
|
53562
|
+
if (!match || match[3] !== event)
|
|
53563
|
+
hookProjectionRefusal("UNRECOGNIZED");
|
|
53564
|
+
const oldCommand = match[1].slice(1, -1).replaceAll("'\\''", "'");
|
|
53565
|
+
let rendered;
|
|
53566
|
+
try {
|
|
53567
|
+
rendered = renderAgentHookCommand(oldCommand, "claude", match[2], event);
|
|
53568
|
+
} catch {
|
|
53569
|
+
hookProjectionRefusal("UNRECOGNIZED");
|
|
53570
|
+
}
|
|
53571
|
+
if (rendered !== hook.command)
|
|
53572
|
+
hookProjectionRefusal("UNRECOGNIZED");
|
|
53573
|
+
const legacy = projectedHookExecutable(oldCommand, executable.resolved);
|
|
53574
|
+
if (legacy.resolved !== executable.resolved || legacy.sha256 !== executable.sha256 || legacy.manifestSha256 !== executable.manifestSha256)
|
|
53575
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
53576
|
+
observed.push(legacy);
|
|
53577
|
+
if (++recognized > 1)
|
|
53578
|
+
hookProjectionRefusal("AMBIGUOUS");
|
|
53579
|
+
const replacement = renderAgentHookCommand(command, "claude", profile, event);
|
|
53580
|
+
if (replacement !== hook.command)
|
|
53581
|
+
replacements2.push({ event, groupIndex, hookIndex, beforeSha256: sha3(hook.command), command: replacement });
|
|
53582
|
+
}
|
|
53583
|
+
}
|
|
53584
|
+
}
|
|
53585
|
+
}
|
|
53586
|
+
try {
|
|
53587
|
+
if (!readNativeBytes(policyPath, 1024 * 1024).equals(policyBytes) || !readNativeBytes(settingsPath, 1024 * 1024).equals(settingsBytes))
|
|
53588
|
+
hookProjectionRefusal("SOURCE_CHANGED");
|
|
53589
|
+
} catch {
|
|
53590
|
+
hookProjectionRefusal("SOURCE_CHANGED");
|
|
53591
|
+
}
|
|
53592
|
+
for (const item of observed)
|
|
53593
|
+
item.recheck();
|
|
53594
|
+
return { schema: "hasna.claude-managed-hook-projection.v1", targetSha256, sourceSha256, replacements: replacements2 };
|
|
53595
|
+
}
|
|
53344
53596
|
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
53345
53597
|
try {
|
|
53346
53598
|
if (binding.method !== "automatic" && binding.method !== "reviewed")
|
|
@@ -53357,10 +53609,10 @@ function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
|
53357
53609
|
}
|
|
53358
53610
|
function atomicWrite2(path, content) {
|
|
53359
53611
|
assertSafePath(path);
|
|
53360
|
-
mkdirSync5(
|
|
53612
|
+
mkdirSync5(dirname12(path), { recursive: true, mode: 448 });
|
|
53361
53613
|
const temporary = `${path}.skills-${randomUUID5()}`;
|
|
53362
53614
|
try {
|
|
53363
|
-
|
|
53615
|
+
writeFileSync5(temporary, content, { mode: 384, flag: "wx" });
|
|
53364
53616
|
renameSync2(temporary, path);
|
|
53365
53617
|
} finally {
|
|
53366
53618
|
if (existsSync13(temporary))
|
|
@@ -53371,10 +53623,10 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
53371
53623
|
if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3].includes(options.targetCodexVersion)))
|
|
53372
53624
|
throw new Error("Invalid Codex witness target version");
|
|
53373
53625
|
const home = resolve12(options.home ?? homedir3()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
|
|
53374
|
-
const
|
|
53375
|
-
if (!
|
|
53626
|
+
const snapshot2 = readManagedSkillPolicySnapshot(dataDir3);
|
|
53627
|
+
if (!snapshot2 || sha3(snapshot2.text) !== options.expectedPolicySha256)
|
|
53376
53628
|
throw new Error("Managed policy preimage changed");
|
|
53377
|
-
const policy =
|
|
53629
|
+
const policy = snapshot2.value, aliases = policy.bridge?.rootAliases ?? [];
|
|
53378
53630
|
recheckRootAliases(aliases);
|
|
53379
53631
|
const binding = policy.bridge?.discovery?.[options.agent];
|
|
53380
53632
|
if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
|
|
@@ -53418,7 +53670,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
53418
53670
|
const replacement = { ...binding, sources: rebound };
|
|
53419
53671
|
verifyAgentDiscovery(replacement);
|
|
53420
53672
|
const after2 = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
53421
|
-
return { dataDir: dataDir3, profileId: policy.profileId, changes: [{ path: join23(dataDir3, "agent-policy.json"), before:
|
|
53673
|
+
return { dataDir: dataDir3, profileId: policy.profileId, changes: [{ path: join23(dataDir3, "agent-policy.json"), before: snapshot2.text, after: after2 }], nativeSkills: [], observedPolicy: { path: join23(dataDir3, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
|
|
53422
53674
|
}
|
|
53423
53675
|
function applyAgentIntegration(plan) {
|
|
53424
53676
|
for (const source of plan.observedNativeSources ?? [])
|
|
@@ -53471,7 +53723,7 @@ function applyAgentIntegration(plan) {
|
|
|
53471
53723
|
for (const [index, change] of plan.changes.entries())
|
|
53472
53724
|
if (change.before !== null) {
|
|
53473
53725
|
const backup = join23(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
|
|
53474
|
-
|
|
53726
|
+
writeFileSync5(backup, change.before, { mode: 384, flag: "wx" });
|
|
53475
53727
|
if (readFileSync13(backup, "utf8") !== change.before)
|
|
53476
53728
|
throw new Error("Native configuration preservation readback failed");
|
|
53477
53729
|
backups.push(backup);
|
|
@@ -53481,7 +53733,7 @@ function applyAgentIntegration(plan) {
|
|
|
53481
53733
|
recheckRootAliases(aliases);
|
|
53482
53734
|
if (currentText(change.path) !== change.before)
|
|
53483
53735
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
53484
|
-
for (let directory =
|
|
53736
|
+
for (let directory = dirname12(change.path);!existsSync13(directory); directory = dirname12(directory))
|
|
53485
53737
|
createdDirectories.add(directory);
|
|
53486
53738
|
const after2 = change.after;
|
|
53487
53739
|
atomicWrite2(change.path, after2);
|
|
@@ -53532,11 +53784,11 @@ function applyAgentIntegration(plan) {
|
|
|
53532
53784
|
}
|
|
53533
53785
|
function syncArchiveDirectory(path) {
|
|
53534
53786
|
assertSafePath(path);
|
|
53535
|
-
const descriptor =
|
|
53787
|
+
const descriptor = openSync9(path, constants9.O_RDONLY | constants9.O_DIRECTORY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
|
|
53536
53788
|
try {
|
|
53537
|
-
|
|
53789
|
+
fsyncSync3(descriptor);
|
|
53538
53790
|
} finally {
|
|
53539
|
-
|
|
53791
|
+
closeSync9(descriptor);
|
|
53540
53792
|
}
|
|
53541
53793
|
}
|
|
53542
53794
|
function writeArchiveJournal(path, value) {
|
|
@@ -53544,18 +53796,18 @@ function writeArchiveJournal(path, value) {
|
|
|
53544
53796
|
const temporary = `${path}.skills-${randomUUID5()}`;
|
|
53545
53797
|
let descriptor;
|
|
53546
53798
|
try {
|
|
53547
|
-
descriptor =
|
|
53548
|
-
|
|
53799
|
+
descriptor = openSync9(temporary, constants9.O_WRONLY | constants9.O_CREAT | constants9.O_EXCL | constants9.O_NOFOLLOW, 384);
|
|
53800
|
+
writeFileSync5(descriptor, `${JSON.stringify(value)}
|
|
53549
53801
|
`);
|
|
53550
|
-
|
|
53551
|
-
|
|
53802
|
+
fsyncSync3(descriptor);
|
|
53803
|
+
closeSync9(descriptor);
|
|
53552
53804
|
descriptor = undefined;
|
|
53553
53805
|
renameSync2(temporary, path);
|
|
53554
|
-
syncArchiveDirectory(
|
|
53806
|
+
syncArchiveDirectory(dirname12(path));
|
|
53555
53807
|
} finally {
|
|
53556
53808
|
if (descriptor !== undefined)
|
|
53557
|
-
|
|
53558
|
-
if (
|
|
53809
|
+
closeSync9(descriptor);
|
|
53810
|
+
if (lstatSync17(temporary, { throwIfNoEntry: false }))
|
|
53559
53811
|
unlinkSync3(temporary);
|
|
53560
53812
|
}
|
|
53561
53813
|
}
|
|
@@ -53589,7 +53841,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
53589
53841
|
const moved = [];
|
|
53590
53842
|
const verifyArchive = (entry) => {
|
|
53591
53843
|
assertSafePath(entry.archive);
|
|
53592
|
-
const stat =
|
|
53844
|
+
const stat = lstatSync17(entry.archive);
|
|
53593
53845
|
if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
|
|
53594
53846
|
throw new Error("Archive type changed");
|
|
53595
53847
|
if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
@@ -53597,14 +53849,14 @@ function archiveNativeSkills(inventory, options) {
|
|
|
53597
53849
|
};
|
|
53598
53850
|
assertSafePath(archiveRoot);
|
|
53599
53851
|
const created = [];
|
|
53600
|
-
for (let path = archiveRoot;!existsSync13(path); path =
|
|
53852
|
+
for (let path = archiveRoot;!existsSync13(path); path = dirname12(path))
|
|
53601
53853
|
created.push(path);
|
|
53602
53854
|
mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
|
|
53603
53855
|
try {
|
|
53604
53856
|
for (const directory of created)
|
|
53605
53857
|
syncArchiveDirectory(directory);
|
|
53606
53858
|
if (created.length)
|
|
53607
|
-
syncArchiveDirectory(
|
|
53859
|
+
syncArchiveDirectory(dirname12(created.at(-1)));
|
|
53608
53860
|
writeArchiveJournal(receiptPath, journal);
|
|
53609
53861
|
for (const [index, entry] of selected.entries()) {
|
|
53610
53862
|
recheckRootAliases(aliases);
|
|
@@ -53616,7 +53868,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
53616
53868
|
writeArchiveJournal(receiptPath, journal);
|
|
53617
53869
|
renameSync2(move.source, move.archive);
|
|
53618
53870
|
moved.push(move);
|
|
53619
|
-
syncArchiveDirectory(
|
|
53871
|
+
syncArchiveDirectory(dirname12(move.source));
|
|
53620
53872
|
syncArchiveDirectory(archiveRoot);
|
|
53621
53873
|
verifyArchive(move);
|
|
53622
53874
|
move.status = "archived";
|
|
@@ -53637,7 +53889,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
53637
53889
|
recheckRootAliases(aliases);
|
|
53638
53890
|
assertSafePath(entry.source);
|
|
53639
53891
|
assertSafePath(entry.archive);
|
|
53640
|
-
if (
|
|
53892
|
+
if (lstatSync17(entry.source, { throwIfNoEntry: false })) {
|
|
53641
53893
|
entry.status = "recovery-required";
|
|
53642
53894
|
entry.conflict = "source-occupied";
|
|
53643
53895
|
continue;
|
|
@@ -53651,10 +53903,10 @@ function archiveNativeSkills(inventory, options) {
|
|
|
53651
53903
|
}
|
|
53652
53904
|
if (entry.discoveryOnly) {
|
|
53653
53905
|
linkSync2(entry.archive, entry.source);
|
|
53654
|
-
syncArchiveDirectory(
|
|
53906
|
+
syncArchiveDirectory(dirname12(entry.source));
|
|
53655
53907
|
unlinkSync3(entry.archive);
|
|
53656
53908
|
} else {
|
|
53657
|
-
if (
|
|
53909
|
+
if (lstatSync17(entry.source, { throwIfNoEntry: false })) {
|
|
53658
53910
|
entry.status = "recovery-required";
|
|
53659
53911
|
entry.conflict = "source-occupied";
|
|
53660
53912
|
continue;
|
|
@@ -53662,7 +53914,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
53662
53914
|
renameSync2(entry.archive, entry.source);
|
|
53663
53915
|
}
|
|
53664
53916
|
entry.status = "restored";
|
|
53665
|
-
syncArchiveDirectory(
|
|
53917
|
+
syncArchiveDirectory(dirname12(entry.source));
|
|
53666
53918
|
syncArchiveDirectory(archiveRoot);
|
|
53667
53919
|
} catch {
|
|
53668
53920
|
entry.status = "recovery-required";
|
|
@@ -53687,10 +53939,10 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
53687
53939
|
function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
|
|
53688
53940
|
const home = resolve12(options.home ?? homedir3()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
|
|
53689
53941
|
assertSafePath(join23(dataDir3, "agent-policy.json"));
|
|
53690
|
-
const
|
|
53691
|
-
if (!
|
|
53942
|
+
const snapshot2 = readManagedSkillPolicySnapshot(dataDir3);
|
|
53943
|
+
if (!snapshot2)
|
|
53692
53944
|
throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
|
|
53693
|
-
const binding =
|
|
53945
|
+
const binding = snapshot2.value.bridge;
|
|
53694
53946
|
if (!binding || binding.version !== CLI_BRIDGE_VERSION || binding.digest !== CLI_BRIDGE_DIGEST || binding.home !== home || !Array.isArray(binding.agents) || !binding.agents.includes(agent))
|
|
53695
53947
|
throw new Error("NATIVE_SKILL_DRIFT: the managed Skills bridge binding is missing or incompatible; run skills hook install");
|
|
53696
53948
|
const aliases = binding.rootAliases ?? [];
|
|
@@ -53846,7 +54098,7 @@ function assertNativeExportAllowed(dataDir3 = getDataDirReadOnly()) {
|
|
|
53846
54098
|
if (requiresCliSkillLoading(dataDir3))
|
|
53847
54099
|
throw new Error("NATIVE_SKILL_EXPORT_DISABLED: this station loads skills through the Skills CLI; use skills sync --selection-profile <id>");
|
|
53848
54100
|
}
|
|
53849
|
-
var sha3 = (data) =>
|
|
54101
|
+
var sha3 = (data) => createHash13("sha256").update(data).digest("hex"), HOOK_EVENTS, ROOTS, SKILLS_LOADING_POLICY = "Skills loading policy: discover skills with `skills list` or `skills search`, and read selected instructions with `skills load <slug>`. Use `skills sync` to refresh the shared profile. The only native skill is the owned skills-cli bridge. Create and edit payload skills in the Skills authoring workspace; do not copy payload instructions into native discovery folders. Context loading does not authorize execution; use `skills run` only when the task calls for running a skill.";
|
|
53850
54102
|
var init_agent_integration = __esm(() => {
|
|
53851
54103
|
init_codex_plugin_skill_controls();
|
|
53852
54104
|
init_codex_native_skill_catalog();
|
|
@@ -53862,12 +54114,13 @@ var init_agent_integration = __esm(() => {
|
|
|
53862
54114
|
init_agent_sumi();
|
|
53863
54115
|
init_agent_hermes();
|
|
53864
54116
|
init_claude_settings_witness();
|
|
54117
|
+
init_codex_hook_trust_files();
|
|
53865
54118
|
HOOK_EVENTS = ["UserPromptSubmit", "SessionStart", "SubagentStart"];
|
|
53866
54119
|
ROOTS = NATIVE_SKILL_ROOTS;
|
|
53867
54120
|
});
|
|
53868
54121
|
|
|
53869
54122
|
// src/lib/home-migration.ts
|
|
53870
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync6, readdirSync as readdirSync9, renameSync as renameSync3, rmSync, statSync as statSync9, writeFileSync as
|
|
54123
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, readdirSync as readdirSync9, renameSync as renameSync3, rmSync, statSync as statSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
53871
54124
|
import { join as join25 } from "path";
|
|
53872
54125
|
function layoutMigrationRecordPath(appDir) {
|
|
53873
54126
|
return join25(appDir, SKILLS_CACHE_DIRNAME, LAYOUT_MIGRATION_RECORD);
|
|
@@ -53990,7 +54243,7 @@ function migrateOwnerLayout(options = {}) {
|
|
|
53990
54243
|
moved,
|
|
53991
54244
|
note: "installed/ and legacy flat skill dirs moved into skills/; custom/ retained"
|
|
53992
54245
|
};
|
|
53993
|
-
|
|
54246
|
+
writeFileSync6(layoutMigrationRecordPath(appDir), `${JSON.stringify(record3, null, 2)}
|
|
53994
54247
|
`);
|
|
53995
54248
|
return { status: "migrated", moved, created, record: record3 };
|
|
53996
54249
|
}
|
|
@@ -54005,7 +54258,7 @@ var init_home_migration = __esm(() => {
|
|
|
54005
54258
|
import {
|
|
54006
54259
|
cpSync as cpSync2,
|
|
54007
54260
|
existsSync as existsSync15,
|
|
54008
|
-
lstatSync as
|
|
54261
|
+
lstatSync as lstatSync18,
|
|
54009
54262
|
mkdirSync as mkdirSync7,
|
|
54010
54263
|
mkdtempSync,
|
|
54011
54264
|
readFileSync as readFileSync14,
|
|
@@ -54013,17 +54266,17 @@ import {
|
|
|
54013
54266
|
renameSync as renameSync4,
|
|
54014
54267
|
rmSync as rmSync2,
|
|
54015
54268
|
statSync as statSync10,
|
|
54016
|
-
writeFileSync as
|
|
54269
|
+
writeFileSync as writeFileSync7
|
|
54017
54270
|
} from "fs";
|
|
54018
54271
|
import { homedir as homedir4 } from "os";
|
|
54019
|
-
import { basename as basename6, dirname as
|
|
54272
|
+
import { basename as basename6, dirname as dirname13, join as join26 } from "path";
|
|
54020
54273
|
function isSkillsOwnershipMarker(marker) {
|
|
54021
54274
|
return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
|
|
54022
54275
|
}
|
|
54023
54276
|
function hasSkillsOwnershipMarker(dir) {
|
|
54024
54277
|
const path = join26(dir, SYNC_MARKER_FILE);
|
|
54025
54278
|
try {
|
|
54026
|
-
if (!
|
|
54279
|
+
if (!lstatSync18(path).isFile())
|
|
54027
54280
|
return false;
|
|
54028
54281
|
const marker = JSON.parse(readFileSync14(path, "utf8"));
|
|
54029
54282
|
return isSkillsOwnershipMarker(marker);
|
|
@@ -54248,7 +54501,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
54248
54501
|
const action = dirExists ? "update" : "create";
|
|
54249
54502
|
if (options.dryRun)
|
|
54250
54503
|
return { action, path: skillMdPath };
|
|
54251
|
-
const parentDir =
|
|
54504
|
+
const parentDir = dirname13(dir);
|
|
54252
54505
|
mkdirSync7(parentDir, { recursive: true });
|
|
54253
54506
|
const transactionDir = mkdtempSync(join26(parentDir, `.hasna-skills-write-${basename6(dir)}-`));
|
|
54254
54507
|
const candidateDir = join26(transactionDir, "candidate");
|
|
@@ -54270,10 +54523,10 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
54270
54523
|
} else {
|
|
54271
54524
|
mkdirSync7(candidateDir, { recursive: true });
|
|
54272
54525
|
}
|
|
54273
|
-
|
|
54526
|
+
writeFileSync7(candidateSkillMdPath, skillMd.endsWith(`
|
|
54274
54527
|
`) ? skillMd : `${skillMd}
|
|
54275
54528
|
`);
|
|
54276
|
-
|
|
54529
|
+
writeFileSync7(candidateMarkerPath, `${JSON.stringify(marker, null, 2)}
|
|
54277
54530
|
`);
|
|
54278
54531
|
if (dirExists) {
|
|
54279
54532
|
originalMoved = true;
|
|
@@ -54356,7 +54609,7 @@ var init_skill_aliases = __esm(() => {
|
|
|
54356
54609
|
});
|
|
54357
54610
|
|
|
54358
54611
|
// src/lib/project-state.ts
|
|
54359
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as
|
|
54612
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
|
|
54360
54613
|
import { join as join27 } from "path";
|
|
54361
54614
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
54362
54615
|
return join27(targetDir, SKILLS_PROJECT_DIR);
|
|
@@ -54392,7 +54645,7 @@ function saveProjectConfig(config, targetDir = process.cwd()) {
|
|
|
54392
54645
|
const dir = getProjectStateDir(targetDir);
|
|
54393
54646
|
mkdirSync8(dir, { recursive: true });
|
|
54394
54647
|
const normalized = normalizeProjectConfig({ ...config, updatedAt: new Date().toISOString() });
|
|
54395
|
-
|
|
54648
|
+
writeFileSync8(getProjectConfigPath(targetDir), JSON.stringify(normalized, null, 2) + `
|
|
54396
54649
|
`);
|
|
54397
54650
|
}
|
|
54398
54651
|
function pinProjectSkill(name, details = {}, targetDir = process.cwd()) {
|
|
@@ -55431,7 +55684,7 @@ var init_selection_resolver = __esm(() => {
|
|
|
55431
55684
|
});
|
|
55432
55685
|
|
|
55433
55686
|
// src/lib/plugin-projection.ts
|
|
55434
|
-
import { createHash as
|
|
55687
|
+
import { createHash as createHash14 } from "crypto";
|
|
55435
55688
|
import { posix } from "path";
|
|
55436
55689
|
function pluginRefusal(message) {
|
|
55437
55690
|
throw new SkillSelectionError("PLUGIN_ADMISSION_REFUSED", message);
|
|
@@ -55624,7 +55877,7 @@ function buildPluginProjection(entries) {
|
|
|
55624
55877
|
files.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
55625
55878
|
return { manifest, payloads: manifest.payloads, files, witnesses: pluginFileWitnesses(files), originalTreeDigest, projectionTreeDigest: pluginTreeDigest(files), removed: removed.length ? pluginFileWitnesses(removed) : [] };
|
|
55626
55879
|
}
|
|
55627
|
-
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json", PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) =>
|
|
55880
|
+
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json", PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) => createHash14("sha256").update(bytes).digest("hex"), MANIFEST_FIELDS;
|
|
55628
55881
|
var init_plugin_projection = __esm(() => {
|
|
55629
55882
|
init_selection_cache();
|
|
55630
55883
|
PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -55632,12 +55885,12 @@ var init_plugin_projection = __esm(() => {
|
|
|
55632
55885
|
});
|
|
55633
55886
|
|
|
55634
55887
|
// src/lib/plugin-projection-store.ts
|
|
55635
|
-
import { createHash as
|
|
55636
|
-
import { constants as
|
|
55637
|
-
import { dirname as
|
|
55888
|
+
import { createHash as createHash15, randomUUID as randomUUID6 } from "crypto";
|
|
55889
|
+
import { constants as constants10, closeSync as closeSync10, fchmodSync, fstatSync as fstatSync10, fsyncSync as fsyncSync4, linkSync as linkSync3, lstatSync as lstatSync19, mkdirSync as mkdirSync9, openSync as openSync10, opendirSync as opendirSync4, readSync as readSync8, renameSync as renameSync5, rmdirSync as rmdirSync3, rmSync as rmSync4, unlinkSync as unlinkSync4, writeSync as writeSync2 } from "fs";
|
|
55890
|
+
import { dirname as dirname14, isAbsolute as isAbsolute12, join as join30, parse as parse5, resolve as resolve13 } from "path";
|
|
55638
55891
|
function stat(path) {
|
|
55639
55892
|
try {
|
|
55640
|
-
return
|
|
55893
|
+
return lstatSync19(path, { bigint: true });
|
|
55641
55894
|
} catch (error) {
|
|
55642
55895
|
if (missing(error))
|
|
55643
55896
|
return null;
|
|
@@ -55656,7 +55909,7 @@ function same3(left, right, identityOnly = false) {
|
|
|
55656
55909
|
}
|
|
55657
55910
|
function ancestors(path, create3 = false) {
|
|
55658
55911
|
canonical5(path);
|
|
55659
|
-
const result2 = [], parent =
|
|
55912
|
+
const result2 = [], parent = dirname14(path), filesystemRoot = parse5(parent).root;
|
|
55660
55913
|
let current = filesystemRoot;
|
|
55661
55914
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
55662
55915
|
if (segment)
|
|
@@ -55684,7 +55937,7 @@ function verifyAncestors(chain, identityOnly = true) {
|
|
|
55684
55937
|
}
|
|
55685
55938
|
function privateParent(path) {
|
|
55686
55939
|
const chain = ancestors(path, true), parent = chain.at(-1);
|
|
55687
|
-
pluginNeed(parent && parent.path ===
|
|
55940
|
+
pluginNeed(parent && parent.path === dirname14(path), "Plugin storage parent is unavailable");
|
|
55688
55941
|
owner(parent.stat);
|
|
55689
55942
|
pluginNeed((parent.stat.mode & 0o7777n) === 0o700n, "Plugin storage parent must have mode 0700");
|
|
55690
55943
|
return chain;
|
|
@@ -55713,19 +55966,19 @@ function readBytes(path, maximum, kind) {
|
|
|
55713
55966
|
fileShape(before2, maximum, kind);
|
|
55714
55967
|
let fd;
|
|
55715
55968
|
try {
|
|
55716
|
-
fd =
|
|
55717
|
-
const opened =
|
|
55969
|
+
fd = openSync10(path, READ_FLAGS);
|
|
55970
|
+
const opened = fstatSync10(fd, { bigint: true });
|
|
55718
55971
|
same3(before2, opened);
|
|
55719
55972
|
fileShape(opened, maximum, kind);
|
|
55720
55973
|
const bytes = new Uint8Array(Number(opened.size));
|
|
55721
55974
|
let offset = 0;
|
|
55722
55975
|
while (offset < bytes.byteLength) {
|
|
55723
|
-
const count =
|
|
55976
|
+
const count = readSync8(fd, bytes, offset, bytes.byteLength - offset, null);
|
|
55724
55977
|
pluginNeed(count > 0, "Plugin file changed while being read");
|
|
55725
55978
|
offset += count;
|
|
55726
55979
|
}
|
|
55727
|
-
pluginNeed(
|
|
55728
|
-
same3(opened,
|
|
55980
|
+
pluginNeed(readSync8(fd, new Uint8Array(1), 0, 1, null) === 0, "Plugin file grew while being read");
|
|
55981
|
+
same3(opened, fstatSync10(fd, { bigint: true }));
|
|
55729
55982
|
same3(opened, stat(path));
|
|
55730
55983
|
verifyAncestors(chain);
|
|
55731
55984
|
return bytes;
|
|
@@ -55735,7 +55988,7 @@ function readBytes(path, maximum, kind) {
|
|
|
55735
55988
|
return pluginRefusal("Cannot safely read plugin file");
|
|
55736
55989
|
} finally {
|
|
55737
55990
|
if (fd !== undefined)
|
|
55738
|
-
|
|
55991
|
+
closeSync10(fd);
|
|
55739
55992
|
}
|
|
55740
55993
|
}
|
|
55741
55994
|
function exactPaths(entries) {
|
|
@@ -55845,16 +56098,16 @@ function verifyPluginTree(root, witnesses) {
|
|
|
55845
56098
|
function writeExclusive(path, bytes, mode) {
|
|
55846
56099
|
let fd, created;
|
|
55847
56100
|
try {
|
|
55848
|
-
fd =
|
|
56101
|
+
fd = openSync10(path, WRITE_FLAGS, mode);
|
|
55849
56102
|
fchmodSync(fd, mode);
|
|
55850
|
-
created =
|
|
56103
|
+
created = fstatSync10(fd, { bigint: true });
|
|
55851
56104
|
let offset = 0;
|
|
55852
56105
|
while (offset < bytes.byteLength) {
|
|
55853
56106
|
const count = writeSync2(fd, bytes, offset, bytes.byteLength - offset);
|
|
55854
56107
|
pluginNeed(count > 0, "Plugin file write did not progress");
|
|
55855
56108
|
offset += count;
|
|
55856
56109
|
}
|
|
55857
|
-
|
|
56110
|
+
fsyncSync4(fd);
|
|
55858
56111
|
} catch (error) {
|
|
55859
56112
|
if (created) {
|
|
55860
56113
|
same3(created, stat(path), true);
|
|
@@ -55863,7 +56116,7 @@ function writeExclusive(path, bytes, mode) {
|
|
|
55863
56116
|
throw error;
|
|
55864
56117
|
} finally {
|
|
55865
56118
|
if (fd !== undefined)
|
|
55866
|
-
|
|
56119
|
+
closeSync10(fd);
|
|
55867
56120
|
}
|
|
55868
56121
|
}
|
|
55869
56122
|
function materializePluginTree(root, entries) {
|
|
@@ -55966,20 +56219,20 @@ function pluginExecutableDigest(path) {
|
|
|
55966
56219
|
fileShape(before2, maximum, "executable");
|
|
55967
56220
|
let fd;
|
|
55968
56221
|
try {
|
|
55969
|
-
fd =
|
|
55970
|
-
const opened =
|
|
56222
|
+
fd = openSync10(path, READ_FLAGS);
|
|
56223
|
+
const opened = fstatSync10(fd, { bigint: true });
|
|
55971
56224
|
same3(before2, opened);
|
|
55972
56225
|
fileShape(opened, maximum, "executable");
|
|
55973
|
-
const hash2 =
|
|
56226
|
+
const hash2 = createHash15("sha256"), buffer = new Uint8Array(64 * 1024);
|
|
55974
56227
|
let remaining = Number(opened.size);
|
|
55975
56228
|
while (remaining > 0) {
|
|
55976
|
-
const count =
|
|
56229
|
+
const count = readSync8(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
|
|
55977
56230
|
pluginNeed(count > 0, "Plugin executable changed during hashing");
|
|
55978
56231
|
hash2.update(buffer.subarray(0, count));
|
|
55979
56232
|
remaining -= count;
|
|
55980
56233
|
}
|
|
55981
|
-
pluginNeed(
|
|
55982
|
-
same3(opened,
|
|
56234
|
+
pluginNeed(readSync8(fd, buffer, 0, 1, null) === 0, "Plugin executable grew during hashing");
|
|
56235
|
+
same3(opened, fstatSync10(fd, { bigint: true }));
|
|
55983
56236
|
same3(opened, stat(path));
|
|
55984
56237
|
verifyAncestors(chain);
|
|
55985
56238
|
return `sha256:${hash2.digest("hex")}`;
|
|
@@ -55989,14 +56242,14 @@ function pluginExecutableDigest(path) {
|
|
|
55989
56242
|
return pluginRefusal("Cannot safely hash plugin executable");
|
|
55990
56243
|
} finally {
|
|
55991
56244
|
if (fd !== undefined)
|
|
55992
|
-
|
|
56245
|
+
closeSync10(fd);
|
|
55993
56246
|
}
|
|
55994
56247
|
}
|
|
55995
56248
|
var READ_FLAGS, WRITE_FLAGS, uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined, missing = (error) => Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
55996
56249
|
var init_plugin_projection_store = __esm(() => {
|
|
55997
56250
|
init_plugin_projection();
|
|
55998
|
-
READ_FLAGS =
|
|
55999
|
-
WRITE_FLAGS =
|
|
56251
|
+
READ_FLAGS = constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK;
|
|
56252
|
+
WRITE_FLAGS = constants10.O_WRONLY | constants10.O_CREAT | constants10.O_EXCL | constants10.O_NOFOLLOW | constants10.O_NONBLOCK;
|
|
56000
56253
|
});
|
|
56001
56254
|
|
|
56002
56255
|
// src/lib/selected-manifest.ts
|
|
@@ -56405,8 +56658,8 @@ var init_plugin_admission = __esm(() => {
|
|
|
56405
56658
|
});
|
|
56406
56659
|
|
|
56407
56660
|
// src/lib/plugin-discovery.ts
|
|
56408
|
-
import { closeSync as
|
|
56409
|
-
import { basename as basename7, dirname as
|
|
56661
|
+
import { closeSync as closeSync11, constants as constants11, fstatSync as fstatSync11, lstatSync as lstatSync20, openSync as openSync11, opendirSync as opendirSync5, readSync as readSync9 } from "fs";
|
|
56662
|
+
import { basename as basename7, dirname as dirname15, isAbsolute as isAbsolute14, join as join32, resolve as resolve15 } from "path";
|
|
56410
56663
|
function validateManagedPluginWitnesses(value) {
|
|
56411
56664
|
pluginNeed(Array.isArray(value) && value.length > 0 && value.length <= PLUGIN_PROJECTION_LIMITS.registrations, "Invalid managed plugin witness collection");
|
|
56412
56665
|
const seen = new Set;
|
|
@@ -56421,29 +56674,29 @@ function validateManagedPluginWitnesses(value) {
|
|
|
56421
56674
|
function safe3(path) {
|
|
56422
56675
|
pluginText(path);
|
|
56423
56676
|
pluginNeed(isAbsolute14(path) && resolve15(path) === path, "Invalid plugin registry path");
|
|
56424
|
-
for (let at2 = path;; at2 =
|
|
56425
|
-
pluginNeed(!
|
|
56426
|
-
if (at2 ===
|
|
56677
|
+
for (let at2 = path;; at2 = dirname15(at2)) {
|
|
56678
|
+
pluginNeed(!lstatSync20(at2, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
|
|
56679
|
+
if (at2 === dirname15(at2))
|
|
56427
56680
|
break;
|
|
56428
56681
|
}
|
|
56429
56682
|
}
|
|
56430
56683
|
function readRegistry(path) {
|
|
56431
56684
|
safe3(path);
|
|
56432
|
-
const initial2 =
|
|
56685
|
+
const initial2 = lstatSync20(path);
|
|
56433
56686
|
pluginNeed(initial2.isFile() && initial2.size <= 1024 * 1024, "Plugin registry is not a bounded regular file");
|
|
56434
|
-
const fd =
|
|
56687
|
+
const fd = openSync11(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
|
|
56435
56688
|
try {
|
|
56436
|
-
const opened =
|
|
56689
|
+
const opened = fstatSync11(fd);
|
|
56437
56690
|
pluginNeed(opened.isFile() && opened.ino === initial2.ino && opened.dev === initial2.dev, "Plugin registry changed during open");
|
|
56438
56691
|
const bytes = Buffer.alloc(1024 * 1024 + 1);
|
|
56439
56692
|
let length = 0;
|
|
56440
56693
|
while (length < bytes.length) {
|
|
56441
|
-
const count =
|
|
56694
|
+
const count = readSync9(fd, bytes, length, bytes.length - length, null);
|
|
56442
56695
|
if (!count)
|
|
56443
56696
|
break;
|
|
56444
56697
|
length += count;
|
|
56445
56698
|
}
|
|
56446
|
-
const after2 =
|
|
56699
|
+
const after2 = fstatSync11(fd), current = lstatSync20(path);
|
|
56447
56700
|
safe3(path);
|
|
56448
56701
|
pluginNeed(length <= 1024 * 1024 && length === opened.size && after2.size === opened.size && after2.mtimeMs === opened.mtimeMs && after2.ctimeMs === opened.ctimeMs && current.dev === opened.dev && current.ino === opened.ino && current.mtimeMs === opened.mtimeMs && current.ctimeMs === opened.ctimeMs, "Plugin registry changed during read");
|
|
56449
56702
|
let value;
|
|
@@ -56455,7 +56708,7 @@ function readRegistry(path) {
|
|
|
56455
56708
|
pluginNeed(pluginObject(value) && value.version === 2 && pluginObject(value.plugins), "Unsupported native plugin registry schema");
|
|
56456
56709
|
return { value, rawDigest: pluginHash(bytes.subarray(0, length)) };
|
|
56457
56710
|
} finally {
|
|
56458
|
-
|
|
56711
|
+
closeSync11(fd);
|
|
56459
56712
|
}
|
|
56460
56713
|
}
|
|
56461
56714
|
function nativeVersionMatches(version, receipt) {
|
|
@@ -56464,7 +56717,7 @@ function nativeVersionMatches(version, receipt) {
|
|
|
56464
56717
|
}
|
|
56465
56718
|
function hashManagedPluginRegistry(path, managed) {
|
|
56466
56719
|
validateManagedPluginWitnesses(managed);
|
|
56467
|
-
const
|
|
56720
|
+
const snapshot2 = readRegistry(path), plugins = snapshot2.value.plugins;
|
|
56468
56721
|
const seen = new Set, verified = new Set;
|
|
56469
56722
|
let verifiedBytes = 0;
|
|
56470
56723
|
for (const witness of managed) {
|
|
@@ -56473,7 +56726,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
56473
56726
|
seen.add(target.pluginId);
|
|
56474
56727
|
const rows = plugins[target.pluginId];
|
|
56475
56728
|
pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
|
|
56476
|
-
const [name, market] = target.pluginId.split("@"), cacheRoot = join32(
|
|
56729
|
+
const [name, market] = target.pluginId.split("@"), cacheRoot = join32(dirname15(path), "cache", market, name);
|
|
56477
56730
|
const scopes = new Set, approvals = new Map, currentFiles = new Map;
|
|
56478
56731
|
plugins[target.pluginId] = rows.map((row) => {
|
|
56479
56732
|
pluginNeed(pluginObject(row), "Invalid managed plugin registration");
|
|
@@ -56516,7 +56769,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
56516
56769
|
return Object.fromEntries(Object.entries(row).filter(([key]) => !["version", "installPath", "sourceProducerPath", "previousProducerPaths", "lastUpdated"].includes(key)));
|
|
56517
56770
|
});
|
|
56518
56771
|
safe3(cacheRoot);
|
|
56519
|
-
const before2 =
|
|
56772
|
+
const before2 = lstatSync20(cacheRoot);
|
|
56520
56773
|
pluginNeed(before2.isDirectory(), "Invalid managed plugin cache root");
|
|
56521
56774
|
const directory = opendirSync5(cacheRoot), names = [];
|
|
56522
56775
|
try {
|
|
@@ -56539,12 +56792,12 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
56539
56792
|
}
|
|
56540
56793
|
for (const row of rows)
|
|
56541
56794
|
pluginNeed(names.includes(row.version), "Managed native plugin installation is missing");
|
|
56542
|
-
const after2 =
|
|
56795
|
+
const after2 = lstatSync20(cacheRoot);
|
|
56543
56796
|
safe3(cacheRoot);
|
|
56544
56797
|
pluginNeed(before2.dev === after2.dev && before2.ino === after2.ino && before2.mtimeMs === after2.mtimeMs && before2.ctimeMs === after2.ctimeMs, "Managed cache version membership changed during verification");
|
|
56545
56798
|
}
|
|
56546
|
-
pluginNeed(readRegistry(path).rawDigest ===
|
|
56547
|
-
return pluginHash(JSON.stringify(
|
|
56799
|
+
pluginNeed(readRegistry(path).rawDigest === snapshot2.rawDigest, "Native plugin registry changed during content verification");
|
|
56800
|
+
return pluginHash(JSON.stringify(snapshot2.value));
|
|
56548
56801
|
}
|
|
56549
56802
|
var init_plugin_discovery = __esm(() => {
|
|
56550
56803
|
init_plugin_admission();
|
|
@@ -56553,50 +56806,50 @@ var init_plugin_discovery = __esm(() => {
|
|
|
56553
56806
|
});
|
|
56554
56807
|
|
|
56555
56808
|
// src/lib/claude-marketplace-registry.ts
|
|
56556
|
-
import { createHash as
|
|
56557
|
-
import { closeSync as
|
|
56558
|
-
import { basename as basename8, dirname as
|
|
56559
|
-
function
|
|
56809
|
+
import { createHash as createHash16 } from "crypto";
|
|
56810
|
+
import { closeSync as closeSync12, constants as constants12, fstatSync as fstatSync12, lstatSync as lstatSync21, openSync as openSync12, readSync as readSync10 } from "fs";
|
|
56811
|
+
import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute15, resolve as resolve16 } from "path";
|
|
56812
|
+
function need6(value, reason) {
|
|
56560
56813
|
if (!value)
|
|
56561
56814
|
throw new Error(`Claude marketplace registry ${reason}`);
|
|
56562
56815
|
}
|
|
56563
56816
|
function absolutePath3(value) {
|
|
56564
|
-
|
|
56817
|
+
need6(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve16(value) === value, "requires a normalized absolute path");
|
|
56565
56818
|
}
|
|
56566
56819
|
function parents2(path) {
|
|
56567
56820
|
const result2 = [];
|
|
56568
|
-
for (let at2 =
|
|
56569
|
-
const stat2 =
|
|
56570
|
-
|
|
56821
|
+
for (let at2 = dirname16(path);; at2 = dirname16(at2)) {
|
|
56822
|
+
const stat2 = lstatSync21(at2, { bigint: true });
|
|
56823
|
+
need6(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
56571
56824
|
result2.push([at2, stat2]);
|
|
56572
|
-
if (at2 ===
|
|
56825
|
+
if (at2 === dirname16(at2))
|
|
56573
56826
|
return result2;
|
|
56574
56827
|
}
|
|
56575
56828
|
}
|
|
56576
56829
|
function readRegistry2(path, budget) {
|
|
56577
56830
|
absolutePath3(path);
|
|
56578
|
-
|
|
56579
|
-
|
|
56580
|
-
const ancestors2 = parents2(path), initial2 =
|
|
56581
|
-
|
|
56582
|
-
|
|
56583
|
-
const fd =
|
|
56831
|
+
need6(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
|
|
56832
|
+
need6(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
|
|
56833
|
+
const ancestors2 = parents2(path), initial2 = lstatSync21(path, { bigint: true });
|
|
56834
|
+
need6(initial2.isFile() && !initial2.isSymbolicLink() && initial2.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
|
|
56835
|
+
need6(initial2.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
|
|
56836
|
+
const fd = openSync12(path, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
|
|
56584
56837
|
try {
|
|
56585
|
-
const opened =
|
|
56586
|
-
|
|
56838
|
+
const opened = fstatSync12(fd, { bigint: true });
|
|
56839
|
+
need6(opened.isFile() && sameFile2(initial2, opened), "changed during open");
|
|
56587
56840
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
56588
56841
|
let length = 0;
|
|
56589
56842
|
while (length < bytes.length) {
|
|
56590
|
-
const count =
|
|
56843
|
+
const count = readSync10(fd, bytes, length, bytes.length - length, null);
|
|
56591
56844
|
if (!count)
|
|
56592
56845
|
break;
|
|
56593
56846
|
length += count;
|
|
56594
56847
|
}
|
|
56595
|
-
const after2 =
|
|
56596
|
-
|
|
56848
|
+
const after2 = fstatSync12(fd, { bigint: true }), current = lstatSync21(path, { bigint: true });
|
|
56849
|
+
need6(current.isFile() && sameFile2(opened, after2) && sameFile2(after2, current) && BigInt(length) === opened.size, "changed during read");
|
|
56597
56850
|
for (const [ancestor, before2] of ancestors2) {
|
|
56598
|
-
const now3 =
|
|
56599
|
-
|
|
56851
|
+
const now3 = lstatSync21(ancestor, { bigint: true });
|
|
56852
|
+
need6(now3.isDirectory() && before2.dev === now3.dev && before2.ino === now3.ino && before2.mode === now3.mode, "parent changed during read");
|
|
56600
56853
|
}
|
|
56601
56854
|
budget.remaining -= length;
|
|
56602
56855
|
try {
|
|
@@ -56605,7 +56858,7 @@ function readRegistry2(path, budget) {
|
|
|
56605
56858
|
throw new Error("Claude marketplace registry is not strict UTF-8");
|
|
56606
56859
|
}
|
|
56607
56860
|
} finally {
|
|
56608
|
-
|
|
56861
|
+
closeSync12(fd);
|
|
56609
56862
|
}
|
|
56610
56863
|
}
|
|
56611
56864
|
function parse6(text2) {
|
|
@@ -56616,7 +56869,7 @@ function parse6(text2) {
|
|
|
56616
56869
|
at2++;
|
|
56617
56870
|
};
|
|
56618
56871
|
function string() {
|
|
56619
|
-
|
|
56872
|
+
need6(text2[at2] === '"', "contains invalid JSON");
|
|
56620
56873
|
const start = at2++;
|
|
56621
56874
|
while (at2 < text2.length) {
|
|
56622
56875
|
const character = text2[at2++];
|
|
@@ -56627,7 +56880,7 @@ function parse6(text2) {
|
|
|
56627
56880
|
} catch {
|
|
56628
56881
|
throw new Error("Claude marketplace registry contains invalid JSON string");
|
|
56629
56882
|
}
|
|
56630
|
-
|
|
56883
|
+
need6(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
|
|
56631
56884
|
return value2;
|
|
56632
56885
|
}
|
|
56633
56886
|
if (character === "\\")
|
|
@@ -56636,7 +56889,7 @@ function parse6(text2) {
|
|
|
56636
56889
|
throw new Error("Claude marketplace registry contains an unterminated JSON string");
|
|
56637
56890
|
}
|
|
56638
56891
|
function value(depth) {
|
|
56639
|
-
|
|
56892
|
+
need6(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
56640
56893
|
space();
|
|
56641
56894
|
const character = text2[at2];
|
|
56642
56895
|
if (character === '"')
|
|
@@ -56650,17 +56903,17 @@ function parse6(text2) {
|
|
|
56650
56903
|
if (object5) {
|
|
56651
56904
|
space();
|
|
56652
56905
|
const key = string();
|
|
56653
|
-
|
|
56906
|
+
need6(!keys2.has(key), "contains a duplicate JSON key");
|
|
56654
56907
|
keys2.add(key);
|
|
56655
56908
|
space();
|
|
56656
|
-
|
|
56909
|
+
need6(text2[at2++] === ":", "contains invalid JSON");
|
|
56657
56910
|
entries.push([key, value(depth + 1)]);
|
|
56658
56911
|
} else
|
|
56659
56912
|
items.push(value(depth + 1));
|
|
56660
56913
|
space();
|
|
56661
56914
|
if (text2[at2] === end)
|
|
56662
56915
|
break;
|
|
56663
|
-
|
|
56916
|
+
need6(text2[at2++] === ",", "contains invalid JSON");
|
|
56664
56917
|
}
|
|
56665
56918
|
at2++;
|
|
56666
56919
|
return object5 ? { kind: "object", entries } : { kind: "array", items };
|
|
@@ -56672,21 +56925,21 @@ function parse6(text2) {
|
|
|
56672
56925
|
}
|
|
56673
56926
|
numberPattern.lastIndex = at2;
|
|
56674
56927
|
const number = numberPattern.exec(text2);
|
|
56675
|
-
|
|
56928
|
+
need6(number, "contains invalid JSON");
|
|
56676
56929
|
at2 += number[0].length;
|
|
56677
56930
|
return { kind: "number", value: number[0] };
|
|
56678
56931
|
}
|
|
56679
56932
|
const root2 = value(0);
|
|
56680
56933
|
space();
|
|
56681
|
-
|
|
56682
|
-
|
|
56934
|
+
need6(at2 === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
|
|
56935
|
+
need6(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
|
|
56683
56936
|
return root2;
|
|
56684
56937
|
}
|
|
56685
56938
|
function exactKeys(value, keys2) {
|
|
56686
56939
|
return value?.kind === "object" && value.entries.length === keys2.length && value.entries.every(([key]) => keys2.includes(key));
|
|
56687
56940
|
}
|
|
56688
56941
|
function textField(value) {
|
|
56689
|
-
|
|
56942
|
+
need6(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
|
|
56690
56943
|
return value.value;
|
|
56691
56944
|
}
|
|
56692
56945
|
function normalizeRow(row, version = 1) {
|
|
@@ -56694,7 +56947,7 @@ function normalizeRow(row, version = 1) {
|
|
|
56694
56947
|
if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
56695
56948
|
return;
|
|
56696
56949
|
const autoUpdate = field(row, "autoUpdate");
|
|
56697
|
-
|
|
56950
|
+
need6(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
56698
56951
|
}
|
|
56699
56952
|
const source = field(row, "source");
|
|
56700
56953
|
if (source?.kind !== "object")
|
|
@@ -56710,23 +56963,23 @@ function normalizeRow(row, version = 1) {
|
|
|
56710
56963
|
absolutePath3(sourceLocation);
|
|
56711
56964
|
absolutePath3(textField(field(row, "installLocation")));
|
|
56712
56965
|
const updated = textField(field(row, "lastUpdated"));
|
|
56713
|
-
|
|
56966
|
+
need6(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
|
|
56714
56967
|
const millis = Date.parse(updated);
|
|
56715
|
-
|
|
56968
|
+
need6(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
56716
56969
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
56717
56970
|
}
|
|
56718
56971
|
function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
56719
56972
|
const value = parse6(readRegistry2(path, budget));
|
|
56720
56973
|
for (const [, row] of value.entries)
|
|
56721
56974
|
normalizeRow(row);
|
|
56722
|
-
const sha2562 =
|
|
56975
|
+
const sha2562 = createHash16("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
56723
56976
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
56724
56977
|
}
|
|
56725
56978
|
function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
56726
56979
|
const value = parse6(readRegistry2(path, budget));
|
|
56727
56980
|
for (const [, row] of value.entries)
|
|
56728
56981
|
normalizeRow(row, 2);
|
|
56729
|
-
const sha2562 =
|
|
56982
|
+
const sha2562 = createHash16("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
56730
56983
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
56731
56984
|
}
|
|
56732
56985
|
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS, sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs, field = (object5, key) => object5.entries.find(([name]) => name === key)?.[1];
|
|
@@ -56734,65 +56987,6 @@ var init_claude_marketplace_registry = __esm(() => {
|
|
|
56734
56987
|
CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
56735
56988
|
});
|
|
56736
56989
|
|
|
56737
|
-
// src/lib/codex-hook-trust-files.ts
|
|
56738
|
-
import { closeSync as closeSync12, constants as constants12, fstatSync as fstatSync12, fsyncSync as fsyncSync4, lstatSync as lstatSync21, openSync as openSync12, readSync as readSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
56739
|
-
import { createHash as createHash16 } from "crypto";
|
|
56740
|
-
import { dirname as dirname16 } from "path";
|
|
56741
|
-
function need6(value, code) {
|
|
56742
|
-
if (!value)
|
|
56743
|
-
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
56744
|
-
}
|
|
56745
|
-
function safeParents(file) {
|
|
56746
|
-
for (let p = dirname16(file);; p = dirname16(p)) {
|
|
56747
|
-
const s = lstatSync21(p);
|
|
56748
|
-
need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
|
|
56749
|
-
need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
56750
|
-
if (dirname16(p) === p)
|
|
56751
|
-
break;
|
|
56752
|
-
}
|
|
56753
|
-
}
|
|
56754
|
-
function snapshot(file, privateFile = false, options = {}) {
|
|
56755
|
-
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
56756
|
-
safeParents(file);
|
|
56757
|
-
const initial2 = lstatSync21(file, { bigint: true });
|
|
56758
|
-
need6(initial2.isFile() && initial2.uid === BigInt(process.getuid()) && (initial2.nlink === 1n || readOnlyPackage && initial2.nlink > 1n) && (initial2.mode & 0o022n) === 0n && (!privateFile || (initial2.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
|
|
56759
|
-
need6(initial2.size <= 8n * 1024n * 1024n, "FILE_BOUND");
|
|
56760
|
-
const fd = openSync12(file, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
|
|
56761
|
-
try {
|
|
56762
|
-
const before2 = fstatSync12(fd, { bigint: true });
|
|
56763
|
-
need6(sameStat(initial2, before2), "INPUT_CHANGED");
|
|
56764
|
-
const bytes = Buffer.alloc(Number(before2.size) + 1);
|
|
56765
|
-
let length = 0;
|
|
56766
|
-
while (length < bytes.length) {
|
|
56767
|
-
const n2 = readSync10(fd, bytes, length, bytes.length - length, null);
|
|
56768
|
-
if (!n2)
|
|
56769
|
-
break;
|
|
56770
|
-
length += n2;
|
|
56771
|
-
}
|
|
56772
|
-
need6(length === Number(before2.size) && sameStat(before2, fstatSync12(fd, { bigint: true })) && sameStat(before2, lstatSync21(file, { bigint: true })), "INPUT_CHANGED");
|
|
56773
|
-
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
56774
|
-
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before2, sha256: createHash16("sha256").update(content).digest("hex") };
|
|
56775
|
-
} finally {
|
|
56776
|
-
closeSync12(fd);
|
|
56777
|
-
}
|
|
56778
|
-
}
|
|
56779
|
-
function unchanged2(before2) {
|
|
56780
|
-
const after2 = snapshot(before2.file, before2.privateFile, before2.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
56781
|
-
need6(sameStat(before2.stat, after2.stat) && after2.sha256 === before2.sha256, "INPUT_CHANGED");
|
|
56782
|
-
}
|
|
56783
|
-
function save(file, content) {
|
|
56784
|
-
safeParents(file);
|
|
56785
|
-
const fd = openSync12(file, constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW, 384);
|
|
56786
|
-
try {
|
|
56787
|
-
writeFileSync8(fd, content);
|
|
56788
|
-
fsyncSync4(fd);
|
|
56789
|
-
} finally {
|
|
56790
|
-
closeSync12(fd);
|
|
56791
|
-
}
|
|
56792
|
-
}
|
|
56793
|
-
var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
|
|
56794
|
-
var init_codex_hook_trust_files = () => {};
|
|
56795
|
-
|
|
56796
56990
|
// src/lib/codex-hook-trust-layout.ts
|
|
56797
56991
|
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
56798
56992
|
function codexTrustTextWitness(text2, keys2, canonicalize = true) {
|
|
@@ -56931,53 +57125,53 @@ var init_codex_hook_trust_layout = () => {};
|
|
|
56931
57125
|
import { isAbsolute as isAbsolute16 } from "path";
|
|
56932
57126
|
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
56933
57127
|
function exactKeys2(value, keys2) {
|
|
56934
|
-
|
|
57128
|
+
need5(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual7(Object.keys(value).sort(), [...keys2].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
56935
57129
|
}
|
|
56936
57130
|
function readReviewedAdditionTrustStates(proof, before2, current, journalHooks) {
|
|
56937
57131
|
const captured = proofs.get(proof);
|
|
56938
|
-
|
|
57132
|
+
need5(!!captured && captured.before.file === before2.file && captured.before.sha256 === before2.sha256 && captured.current.file === current.file && captured.current.sha256 === current.sha256 && isDeepStrictEqual7(captured.journalHooks, journalHooks.map(({ key, currentHash }) => ({ key, currentHash }))), "RECONCILE_ADDITIONS_PROOF_CHANGED");
|
|
56939
57133
|
captured.recheck();
|
|
56940
57134
|
unchanged2(before2);
|
|
56941
57135
|
unchanged2(current);
|
|
56942
57136
|
return captured.states;
|
|
56943
57137
|
}
|
|
56944
57138
|
function reviewNativeHookAdditions(options) {
|
|
56945
|
-
|
|
57139
|
+
need5(isAbsolute16(options.path), "RECONCILE_ADDITIONS_REVIEW_PATH");
|
|
56946
57140
|
const reviewFile = snapshot(options.path, true), review = JSON.parse(reviewFile.text);
|
|
56947
57141
|
exactKeys2(review, ["version", "kind", "reason", "producerEvidence", "journalIntentSha256", "policySha256", "configCurrentSha256", "hooksCurrentSha256", "producerBefore", "additions"]);
|
|
56948
|
-
|
|
57142
|
+
need5((review.version === 1 || review.version === 2) && review.kind === "native-hook-additions" && text2(review.reason) && text2(review.producerEvidence), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
56949
57143
|
for (const [name, expected] of [["journalIntentSha256", options.intent.sha256], ["policySha256", options.policy.sha256], ["configCurrentSha256", options.configCurrent.sha256], ["hooksCurrentSha256", options.hooksCurrent.sha256]])
|
|
56950
|
-
|
|
57144
|
+
need5(hash2(review[name]) && review[name] === expected, "RECONCILE_ADDITIONS_REVIEW_CHANGED");
|
|
56951
57145
|
exactKeys2(review.producerBefore, ["configPath", "configSha256", "hooksPath", "hooksSha256"]);
|
|
56952
57146
|
const before2 = review.producerBefore;
|
|
56953
|
-
|
|
57147
|
+
need5(isAbsolute16(before2.configPath) && isAbsolute16(before2.hooksPath) && hash2(before2.configSha256) && hash2(before2.hooksSha256), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
56954
57148
|
const producerConfig = snapshot(before2.configPath, true), producerHooks = snapshot(before2.hooksPath, true);
|
|
56955
|
-
|
|
57149
|
+
need5(producerConfig.sha256 === before2.configSha256 && producerHooks.sha256 === before2.hooksSha256 && producerHooks.sha256 === options.hooksBefore.sha256, "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
56956
57150
|
const original = structure(options.configBefore.text), producer = structure(producerConfig.text), expectedProducer = structuredClone(original), originalKeys = options.journalHooks.map((hook) => hook.key);
|
|
56957
57151
|
expectedProducer.hooks ??= {};
|
|
56958
57152
|
expectedProducer.hooks.state ??= {};
|
|
56959
57153
|
for (const hook of options.journalHooks)
|
|
56960
57154
|
expectedProducer.hooks.state[hook.key] = { ...expectedProducer.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
56961
|
-
|
|
56962
|
-
|
|
57155
|
+
need5(isDeepStrictEqual7(expectedProducer, producer) && codexTrustReconcileWitness(options.configBefore.text, originalKeys) === codexTrustReconcileWitness(producerConfig.text, originalKeys) && codexTrustUnmanagedSemantic(options.configBefore.text, originalKeys) === codexTrustUnmanagedSemantic(producerConfig.text, originalKeys), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
57156
|
+
need5(Array.isArray(review.additions) && review.additions.length > 0 && review.additions.length <= 20, "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
56963
57157
|
const expectedHooks = JSON.parse(options.hooksBefore.text), currentHooks = JSON.parse(options.hooksCurrent.text), expectedConfig = structuredClone(producer);
|
|
56964
|
-
|
|
57158
|
+
need5(expectedHooks.hooks && typeof expectedHooks.hooks === "object" && !Array.isArray(expectedHooks.hooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56965
57159
|
const added = [], states = [], seen = new Set, commands = new Set(options.recordedNativeHooks.map((hook) => hook.command));
|
|
56966
57160
|
for (const addition of review.additions) {
|
|
56967
57161
|
exactKeys2(addition, review.version === 1 ? ["event", "nativeHook"] : ["event", "nativeHook", "trustState"]);
|
|
56968
|
-
|
|
57162
|
+
need5(Object.hasOwn(review.version === 1 ? events : matchedEvents, addition.event), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56969
57163
|
const { event, nativeHook: hook } = addition;
|
|
56970
57164
|
exactKeys2(hook, nativeKeys);
|
|
56971
57165
|
const groups = expectedHooks.hooks[event] ?? [];
|
|
56972
|
-
|
|
57166
|
+
need5(Array.isArray(groups), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56973
57167
|
const key = `${options.nativeHooksPath}:${matchedEvents[event][1]}:${groups.length}:0`;
|
|
56974
|
-
|
|
56975
|
-
|
|
57168
|
+
need5(hook.key === key && !seen.has(key) && !options.recordedNativeHooks.some((originalHook) => originalHook.key === key) && !Object.hasOwn(expectedConfig.hooks.state, key), "RECONCILE_ADDITIONS_IDENTITY");
|
|
57169
|
+
need5(hook.eventName === matchedEvents[event][0] && hook.handlerType === "command" && (review.version === 2 ? v2Command(hook.command) : text2(hook.command, 16384)) && !commands.has(hook.command) && Number.isInteger(hook.timeoutSec) && hook.timeoutSec > 0 && hook.timeoutSec <= 60 && (event === "PreToolUse" ? text2(hook.matcher) : hook.matcher === null) && hook.async === false && hook.statusMessage === null && hook.additionalContextLimit === null && hook.sourcePath === options.nativeHooksPath && hook.source === "user" && hook.pluginId === null && hook.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash) && hook.enabled === true && hook.trustStatus === "trusted", "RECONCILE_ADDITIONS_IDENTITY");
|
|
56976
57170
|
let trustState = { enabled: true, trusted_hash: hook.currentHash };
|
|
56977
57171
|
if (review.version === 2) {
|
|
56978
57172
|
const state = addition.trustState;
|
|
56979
57173
|
exactKeys2(state, state && Object.hasOwn(state, "enabled") ? ["enabled", "trusted_hash"] : ["trusted_hash"]);
|
|
56980
|
-
|
|
57174
|
+
need5(state.trusted_hash === hook.currentHash && (!Object.hasOwn(state, "enabled") || state.enabled === true), "RECONCILE_ADDITIONS_TRUST_STATE");
|
|
56981
57175
|
trustState = { ...state };
|
|
56982
57176
|
}
|
|
56983
57177
|
seen.add(key);
|
|
@@ -56987,9 +57181,9 @@ function reviewNativeHookAdditions(options) {
|
|
|
56987
57181
|
expectedHooks.hooks[event] = [...groups, { ...event === "PreToolUse" ? { matcher: hook.matcher } : {}, hooks: [{ type: "command", command: hook.command, timeout: hook.timeoutSec }] }];
|
|
56988
57182
|
expectedConfig.hooks.state[key] = trustState;
|
|
56989
57183
|
}
|
|
56990
|
-
|
|
57184
|
+
need5(isDeepStrictEqual7(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56991
57185
|
const keys2 = added.map((hook) => hook.key);
|
|
56992
|
-
|
|
57186
|
+
need5(isDeepStrictEqual7(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");
|
|
56993
57187
|
const inputs = [reviewFile, producerConfig, producerHooks, options.intent, options.policy, options.configBefore, options.configCurrent, options.hooksBefore, options.hooksCurrent];
|
|
56994
57188
|
const recheck = () => {
|
|
56995
57189
|
for (const input of inputs)
|
|
@@ -57023,16 +57217,16 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
|
|
|
57023
57217
|
const raw = binding.sources.filter((source) => source.path === current.file && source.hashMode === "bytes");
|
|
57024
57218
|
if (!raw.some((source) => source.sha256 !== current.sha256))
|
|
57025
57219
|
return;
|
|
57026
|
-
|
|
57027
|
-
|
|
57220
|
+
need5(binding.agent === "codex" && binding.method === "reviewed" && raw.length === 1 && raw[0].sha256 === before2.sha256 && before2.sha256 !== current.sha256, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
|
|
57221
|
+
need5(raw[0].format === undefined && raw[0].fields === undefined && raw[0].managedPlugins === undefined, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
|
|
57028
57222
|
const typed = binding.sources.filter((source) => source.path === current.file && source.format === "toml");
|
|
57029
|
-
|
|
57223
|
+
need5(typed.length === 1 && typed[0].hashMode === undefined && isDeepStrictEqual8(typed[0].fields, fields), "RECONCILE_DISCOVERY_TYPED_WITNESS");
|
|
57030
57224
|
const original = structure2(before2.text), actual = structure2(current.text);
|
|
57031
57225
|
const project = (value) => Object.fromEntries(fields.map((field2) => [field2, value[field2] ?? null]));
|
|
57032
57226
|
const projection = sha4(JSON.stringify(project(original)));
|
|
57033
|
-
|
|
57227
|
+
need5(typed[0].sha256 === projection && sha4(JSON.stringify(project(actual))) === projection && isDeepStrictEqual8(project(original), project(actual)), "RECONCILE_DISCOVERY_TYPED_WITNESS");
|
|
57034
57228
|
const keys2 = [...journalHooks.map((hook) => hook.key), ...additionStates.map((hook) => hook.key)];
|
|
57035
|
-
|
|
57229
|
+
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");
|
|
57036
57230
|
const expected = structuredClone(original);
|
|
57037
57231
|
expected.hooks ??= {};
|
|
57038
57232
|
expected.hooks.state ??= {};
|
|
@@ -57040,7 +57234,7 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
|
|
|
57040
57234
|
expected.hooks.state[hook.key] = { ...expected.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
57041
57235
|
for (const { key, state } of additionStates)
|
|
57042
57236
|
expected.hooks.state[key] = { ...state };
|
|
57043
|
-
|
|
57237
|
+
need5(isDeepStrictEqual8(expected, actual) && codexTrustReconcileWitness(before2.text, keys2) === codexTrustReconcileWitness(current.text, keys2) && codexTrustUnmanagedSemantic(before2.text, keys2) === codexTrustUnmanagedSemantic(current.text, keys2), "RECONCILE_DISCOVERY_TRUST_WITNESS");
|
|
57044
57238
|
unchanged2(before2);
|
|
57045
57239
|
unchanged2(current);
|
|
57046
57240
|
const witness = Object.freeze({ configPath: current.file, beforeSha256: before2.sha256, currentSha256: current.sha256, discoverySha256: sha4(JSON.stringify(binding)), typedProjectionSha256: projection });
|
|
@@ -57049,7 +57243,7 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
|
|
|
57049
57243
|
}
|
|
57050
57244
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
57051
57245
|
const proof = proofs2.get(witness);
|
|
57052
|
-
|
|
57246
|
+
need5(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
57053
57247
|
unchanged2(proof.before);
|
|
57054
57248
|
unchanged2(proof.current);
|
|
57055
57249
|
proof.recheckAdditions();
|
|
@@ -57058,7 +57252,7 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
57058
57252
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
57059
57253
|
return false;
|
|
57060
57254
|
assertCodexHookDiscoveryRecovery(binding, witness);
|
|
57061
|
-
|
|
57255
|
+
need5(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
57062
57256
|
return true;
|
|
57063
57257
|
}
|
|
57064
57258
|
var sha4 = (text3) => createHash17("sha256").update(text3).digest("hex"), fields, structure2 = (text3) => JSON.parse(JSON.stringify(Bun.TOML.parse(text3))), proofs2;
|
|
@@ -112810,40 +113004,40 @@ import { realpathSync as realpathSync17 } from "fs";
|
|
|
112810
113004
|
import { dirname as dirname33, isAbsolute as isAbsolute27, join as join60 } from "path";
|
|
112811
113005
|
import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
|
|
112812
113006
|
function inspectRecordedSkillsCli(command, expected) {
|
|
112813
|
-
|
|
113007
|
+
need5(isAbsolute27(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
|
|
112814
113008
|
const resolved = realpathSync17(command);
|
|
112815
113009
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
112816
113010
|
const manifest = snapshot(join60(dirname33(dirname33(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
112817
113011
|
const pkg = JSON.parse(manifest.text);
|
|
112818
|
-
|
|
112819
|
-
|
|
113012
|
+
need5(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join60(dirname33(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
113013
|
+
need5((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
112820
113014
|
const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
|
|
112821
|
-
|
|
113015
|
+
need5(isDeepStrictEqual13(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
112822
113016
|
return { receipt, recheck() {
|
|
112823
|
-
|
|
113017
|
+
need5(realpathSync17(command) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
112824
113018
|
unchanged2(cli);
|
|
112825
113019
|
unchanged2(manifest);
|
|
112826
113020
|
} };
|
|
112827
113021
|
}
|
|
112828
113022
|
function bindSkillsCli(command, reviewed) {
|
|
112829
113023
|
const normal = Bun.which("skills", { PATH: process.env.PATH });
|
|
112830
|
-
|
|
113024
|
+
need5(normal, "SKILLS_COMMAND_UNAVAILABLE");
|
|
112831
113025
|
const resolved = realpathSync17(normal);
|
|
112832
113026
|
const bound = isAbsolute27(command) ? command : command === "skills" ? normal : undefined;
|
|
112833
|
-
|
|
113027
|
+
need5(bound && realpathSync17(bound) === resolved, "SKILLS_COMMAND_MISMATCH");
|
|
112834
113028
|
const entrypoint = reviewed?.path ?? process.argv[1];
|
|
112835
|
-
|
|
113029
|
+
need5(entrypoint && realpathSync17(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
|
|
112836
113030
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
112837
113031
|
const manifest = snapshot(join60(dirname33(dirname33(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
112838
113032
|
const pkg = JSON.parse(manifest.text);
|
|
112839
|
-
|
|
113033
|
+
need5(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join60(dirname33(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
112840
113034
|
if (reviewed)
|
|
112841
|
-
|
|
112842
|
-
|
|
113035
|
+
need5(/^[a-f0-9]{64}$/.test(reviewed.sha256) && reviewed.sha256 === cli.sha256 && reviewed.version === pkg.version, "SKILLS_RELEASE_MISMATCH");
|
|
113036
|
+
need5((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
112843
113037
|
return {
|
|
112844
113038
|
receipt: { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 },
|
|
112845
113039
|
recheck() {
|
|
112846
|
-
|
|
113040
|
+
need5(Bun.which("skills", { PATH: process.env.PATH }) === normal && realpathSync17(normal) === resolved && realpathSync17(bound) === resolved && realpathSync17(entrypoint) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
112847
113041
|
unchanged2(cli);
|
|
112848
113042
|
unchanged2(manifest);
|
|
112849
113043
|
}
|
|
@@ -112856,17 +113050,17 @@ var init_codex_hook_trust_identity = __esm(() => {
|
|
|
112856
113050
|
// src/lib/codex-hook-policy-recovery.ts
|
|
112857
113051
|
import { isDeepStrictEqual as isDeepStrictEqual14 } from "util";
|
|
112858
113052
|
function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
|
|
112859
|
-
|
|
112860
|
-
|
|
113053
|
+
need5(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
|
|
113054
|
+
need5(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
112861
113055
|
const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
|
|
112862
113056
|
const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
|
|
112863
|
-
|
|
113057
|
+
need5(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
|
|
112864
113058
|
const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual14(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
|
|
112865
|
-
|
|
113059
|
+
need5(changes.length > 0, "RECONCILE_POLICY_CHANGED");
|
|
112866
113060
|
const compared = structuredClone(current);
|
|
112867
113061
|
compared.bridge.discovery.claude.roots = oldDiscovery.roots;
|
|
112868
113062
|
compared.bridge.discovery.claude.sources = oldDiscovery.sources;
|
|
112869
|
-
|
|
113063
|
+
need5(isDeepStrictEqual14(before2, compared), "RECONCILE_POLICY_CHANGED");
|
|
112870
113064
|
verifyAgentDiscovery(newDiscovery);
|
|
112871
113065
|
return { reason: options.reason, changes };
|
|
112872
113066
|
}
|
|
@@ -112884,15 +113078,15 @@ import { dirname as dirname34, isAbsolute as isAbsolute28, join as join61, resol
|
|
|
112884
113078
|
import { homedir as homedir12 } from "os";
|
|
112885
113079
|
import { isDeepStrictEqual as isDeepStrictEqual15 } from "util";
|
|
112886
113080
|
function verifyReleaseArtifact(proof, skillsCli) {
|
|
112887
|
-
|
|
113081
|
+
need5(isAbsolute28(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
112888
113082
|
const tarPath = realpathSync18(proof.packageTarPath);
|
|
112889
|
-
|
|
113083
|
+
need5(statSync21(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
112890
113084
|
const tarBytes = readFileSync40(tarPath);
|
|
112891
|
-
|
|
113085
|
+
need5(tarBytes.byteLength === proof.packageTarBytes && createHash31("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
|
|
112892
113086
|
const packageRoot = dirname34(dirname34(realpathSync18(skillsCli.receipt.path)));
|
|
112893
113087
|
const manifest = readFileSync40(join61(packageRoot, "package.json"));
|
|
112894
|
-
|
|
112895
|
-
|
|
113088
|
+
need5(createHash31("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
|
|
113089
|
+
need5(createHash31("sha256").update(readFileSync40(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
|
|
112896
113090
|
const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync40(skillsCli.receipt.path)]];
|
|
112897
113091
|
for (const [member, expected] of members2) {
|
|
112898
113092
|
let actual;
|
|
@@ -112901,7 +113095,7 @@ function verifyReleaseArtifact(proof, skillsCli) {
|
|
|
112901
113095
|
} catch {
|
|
112902
113096
|
throw new Error("RECONCILE_RELEASE_ARTIFACT_MEMBER_MISSING");
|
|
112903
113097
|
}
|
|
112904
|
-
|
|
113098
|
+
need5(Buffer.compare(actual, expected) === 0, "RECONCILE_RELEASE_ARTIFACT_MEMBER_CHANGED");
|
|
112905
113099
|
}
|
|
112906
113100
|
}
|
|
112907
113101
|
async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc) {
|
|
@@ -112909,48 +113103,48 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
112909
113103
|
try {
|
|
112910
113104
|
const home = resolve26(options.home ?? homedir12()), dataDir3 = resolve26(options.dataDir ?? getDataDirReadOnly());
|
|
112911
113105
|
const parent = resolve26(join61(dataDir3, "native-hook-trust")), journal = resolve26(options.journal);
|
|
112912
|
-
|
|
113106
|
+
need5(journal.startsWith(parent + "/") && /^[a-f0-9-]{36}$/.test(journal.slice(parent.length + 1)), "RECONCILE_JOURNAL_PATH");
|
|
112913
113107
|
const dir = lstatSync32(journal, { throwIfNoEntry: false });
|
|
112914
|
-
|
|
112915
|
-
|
|
112916
|
-
|
|
113108
|
+
need5(!!dir && dir.isDirectory() && !dir.isSymbolicLink() && dir.uid === process.getuid() && (dir.mode & 511) === 448, "UNSAFE_JOURNAL");
|
|
113109
|
+
need5(!lstatSync32(join61(journal, "receipt.json"), { throwIfNoEntry: false }), "RECONCILE_ALREADY_COMPLETE");
|
|
113110
|
+
need5(lstatSync32(join61(journal, "stopped.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_JOURNAL_INCOMPLETE");
|
|
112917
113111
|
const stoppedFile = snapshot(join61(journal, "stopped.json"), true);
|
|
112918
113112
|
const intentFile = snapshot(join61(journal, "intent.json"), true), intent = JSON.parse(intentFile.text);
|
|
112919
113113
|
const configBefore = snapshot(join61(journal, "config.before.toml"), true), hooksBefore = snapshot(join61(journal, "hooks.before.json"), true), policyBefore = snapshot(join61(journal, "policy.before.json"), true);
|
|
112920
113114
|
const legacyIntent = intent.home === undefined && intent.configSha256 === undefined && intent.configVersion === undefined && intent.declarations === undefined && intent.admitted === undefined && intent.nativeHooks === undefined;
|
|
112921
|
-
|
|
113115
|
+
need5(intent.version === 1 && /^[a-f0-9]{64}$/.test(intent.planDigest) && typeof intent.configPath === "string" && /^[a-f0-9]{64}$/.test(intent.policySha256) && /^[a-f0-9]{64}$/.test(intent.hooksSha256) && Array.isArray(intent.hooks) && intent.hooks.length > 0 && intent.hooks.length <= 100 && (legacyIntent || typeof intent.home === "string" && intent.home === home && /^[a-f0-9]{64}$/.test(intent.configSha256) && typeof intent.configVersion === "string" && Array.isArray(intent.declarations) && Array.isArray(intent.admitted) && Array.isArray(intent.nativeHooks)), "RECONCILE_INTENT_INVALID");
|
|
112922
113116
|
const declarations = intent.declarations ?? intent.hooks.map((hook) => ({ event: hook.event, key: `${join61(home, ".codex/hooks.json")}${hook.key.slice(hook.key.indexOf(".codex/hooks.json") + ".codex/hooks.json".length)}`, command: hook.command }));
|
|
112923
113117
|
const admitted = intent.admitted ?? intent.hooks;
|
|
112924
113118
|
const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
|
|
112925
113119
|
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 };
|
|
112926
|
-
|
|
112927
|
-
|
|
112928
|
-
|
|
113120
|
+
need5(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
|
|
113121
|
+
need5(isDeepStrictEqual15(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
|
|
113122
|
+
need5(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
|
|
112929
113123
|
const policyCurrent = snapshot(join61(dataDir3, "agent-policy.json"), true);
|
|
112930
113124
|
const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
|
|
112931
113125
|
const recovery = claudeRecovery ?? additionsRecovery;
|
|
112932
|
-
|
|
112933
|
-
|
|
112934
|
-
|
|
113126
|
+
need5(!recovery || !options.supersedeBinding, "RECONCILE_RECOVERY_MODE_CONFLICT");
|
|
113127
|
+
need5(!claudeRecovery || !additionsRecovery || claudeRecovery.reviewedPlanDigest === additionsRecovery.reviewedPlanDigest, "RECONCILE_RECOVERY_MODE_CONFLICT");
|
|
113128
|
+
need5(recovery?.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(recovery.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
112935
113129
|
const policyChange = claudeRecovery ? reviewedClaudeDiscoveryChange(policyBefore.text, policyCurrent.text, claudeRecovery) : undefined;
|
|
112936
113130
|
let supersededBinding = false;
|
|
112937
113131
|
if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
|
|
112938
|
-
|
|
113132
|
+
need5(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
|
|
112939
113133
|
const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
|
|
112940
|
-
|
|
113134
|
+
need5(isDeepStrictEqual15(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
|
|
112941
113135
|
supersededBinding = true;
|
|
112942
113136
|
}
|
|
112943
113137
|
if (supersededBinding || recovery) {
|
|
112944
|
-
|
|
113138
|
+
need5(Array.isArray(intent.nativeHooks) && intent.nativeHooks.length > 0, "RECONCILE_NATIVE_INVENTORY_REQUIRED");
|
|
112945
113139
|
const proof = options.releaseProof;
|
|
112946
|
-
|
|
113140
|
+
need5(!!proof && isAbsolute28(proof.packageTarPath) && /^[a-f0-9]{64}$/.test(proof.packageTarSha256) && Number.isInteger(proof.packageTarBytes) && proof.packageTarBytes > 0 && /^[a-f0-9]{64}$/.test(proof.manifestSha256) && /^[a-f0-9]{64}$/.test(proof.executableSha256), "RECONCILE_RELEASE_PROVENANCE_REQUIRED");
|
|
112947
113141
|
}
|
|
112948
113142
|
const currentConfig = snapshot(intent.configPath, true);
|
|
112949
113143
|
const policy = parseManagedSkillPolicy(policyCurrent.text), binding = policy.bridge;
|
|
112950
113144
|
const alias = binding.rootAliases?.find((item) => item.agent === "codex"), physicalRoot = alias?.target ?? join61(home, ".codex"), hooksPath = join61(physicalRoot, "hooks.json");
|
|
112951
|
-
|
|
113145
|
+
need5(resolve26(intent.configPath) === resolve26(join61(physicalRoot, "config.toml")), "RECONCILE_CONFIG_PATH_CHANGED");
|
|
112952
113146
|
const nativeConfigPath = intent.nativeConfigPath ?? intent.configPath;
|
|
112953
|
-
|
|
113147
|
+
need5([join61(home, ".codex/config.toml"), join61(physicalRoot, "config.toml")].includes(nativeConfigPath), "RECONCILE_NATIVE_CONFIG_PATH_CHANGED");
|
|
112954
113148
|
const codexHome = nativeConfigPath !== intent.configPath ? undefined : dirname34(nativeConfigPath);
|
|
112955
113149
|
const hooksCurrent = snapshot(hooksPath);
|
|
112956
113150
|
const additions = additionsRecovery ? reviewNativeHookAdditions({ path: additionsRecovery.path, intent: intentFile, policy: policyCurrent, configBefore, configCurrent: currentConfig, hooksBefore, hooksCurrent, nativeHooksPath: join61(dirname34(nativeConfigPath), "hooks.json"), journalHooks: intent.hooks, recordedNativeHooks: intent.nativeHooks }) : undefined;
|
|
@@ -112958,7 +113152,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
112958
113152
|
const managedKeys = managedHooks.map((h) => h.key);
|
|
112959
113153
|
const witnessSame = codexTrustReconcileWitness(configBefore.text, managedKeys) === codexTrustReconcileWitness(currentConfig.text, managedKeys);
|
|
112960
113154
|
const unmanagedSame = codexTrustUnmanagedSemantic(configBefore.text, managedKeys) === codexTrustUnmanagedSemantic(currentConfig.text, managedKeys);
|
|
112961
|
-
|
|
113155
|
+
need5(unmanagedSame && (witnessSame || supersededBinding), "RECONCILE_UNRELATED_CONFIG_CHANGED");
|
|
112962
113156
|
const before2 = structure3(configBefore.text), current = structure3(currentConfig.text), expectedConfig = structuredClone(before2);
|
|
112963
113157
|
expectedConfig.hooks ??= {};
|
|
112964
113158
|
expectedConfig.hooks.state ??= {};
|
|
@@ -112970,10 +113164,10 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
112970
113164
|
if (supersededBinding) {
|
|
112971
113165
|
for (const hook of intent.hooks) {
|
|
112972
113166
|
const state = current.hooks?.state?.[hook.key];
|
|
112973
|
-
|
|
113167
|
+
need5(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
|
|
112974
113168
|
}
|
|
112975
113169
|
} else
|
|
112976
|
-
|
|
113170
|
+
need5(isDeepStrictEqual15(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
|
|
112977
113171
|
const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
|
|
112978
113172
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
|
|
112979
113173
|
const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
|
|
@@ -112983,28 +113177,28 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
112983
113177
|
if (supersededBinding)
|
|
112984
113178
|
verifyReleaseArtifact(options.releaseProof, skillsCli);
|
|
112985
113179
|
const skillsBindingChanged = !isDeepStrictEqual15(skillsCli.receipt, intent.skillsCli);
|
|
112986
|
-
|
|
113180
|
+
need5(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
112987
113181
|
if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
|
|
112988
|
-
|
|
113182
|
+
need5(supersededBinding, "RECONCILE_HOOKS_CHANGED");
|
|
112989
113183
|
const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
|
|
112990
113184
|
for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
|
|
112991
113185
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
112992
|
-
|
|
113186
|
+
need5(isDeepStrictEqual15(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
112993
113187
|
}
|
|
112994
113188
|
}
|
|
112995
113189
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
112996
|
-
|
|
113190
|
+
need5(rpc.version === intent.nativeVersion, "RECONCILE_NATIVE_VERSION_CHANGED");
|
|
112997
113191
|
const discovered = await rpc.request("hooks/list", { cwds: [home] });
|
|
112998
|
-
|
|
113192
|
+
need5(Array.isArray(discovered?.data) && discovered.data.length === 1 && discovered.data[0].cwd === home && !discovered.data[0].errors?.length && !discovered.data[0].warnings?.length, "RECONCILE_NATIVE_DISCOVERY_REFUSED");
|
|
112999
113193
|
const nativeHooks = discovered.data[0].hooks;
|
|
113000
113194
|
const recordedNativeHooks = intent.nativeHooks;
|
|
113001
113195
|
if (recordedNativeHooks)
|
|
113002
|
-
|
|
113196
|
+
need5(nativeHooks.length === recordedNativeHooks.length + (additions?.hooks.length ?? 0), "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
|
|
113003
113197
|
const plannedKeys = new Set(intent.hooks.map((hook) => hook.key));
|
|
113004
113198
|
let nativeTrustPending = false;
|
|
113005
113199
|
for (const expected of [...recordedNativeHooks ?? [], ...additions?.hooks ?? []]) {
|
|
113006
113200
|
const matches2 = nativeHooks.filter((hook) => hook.key === expected.key);
|
|
113007
|
-
|
|
113201
|
+
need5(matches2.length === 1, "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
|
|
113008
113202
|
const actual = safeNativeHook(matches2[0]);
|
|
113009
113203
|
const stable = (hook) => {
|
|
113010
113204
|
const { enabled, trustStatus, ...identity5 } = hook;
|
|
@@ -113012,35 +113206,35 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
113012
113206
|
delete identity5.command;
|
|
113013
113207
|
return identity5;
|
|
113014
113208
|
};
|
|
113015
|
-
|
|
113209
|
+
need5(isDeepStrictEqual15(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
113016
113210
|
if (!plannedKeys.has(expected.key))
|
|
113017
|
-
|
|
113211
|
+
need5(isDeepStrictEqual15(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
|
|
113018
113212
|
}
|
|
113019
113213
|
for (const wanted of intent.hooks) {
|
|
113020
113214
|
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join61(home, ".codex/hooks.json"), hooksPath));
|
|
113021
|
-
|
|
113215
|
+
need5(matches2.length === 1, "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
113022
113216
|
const h = matches2[0];
|
|
113023
113217
|
const trusted = h.trustStatus === "trusted";
|
|
113024
|
-
|
|
113218
|
+
need5(h.enabled === true && (trusted || supersededBinding && h.trustStatus === "modified") && (h.currentHash === wanted.currentHash || supersededBinding && /^sha256:[a-f0-9]{64}$/.test(h.currentHash)), "RECONCILE_NATIVE_STATE_INCOMPLETE");
|
|
113025
113219
|
if (!trusted)
|
|
113026
113220
|
nativeTrustPending = true;
|
|
113027
113221
|
if (supersededBinding) {
|
|
113028
113222
|
const nativeToCliEvent = { userPromptSubmit: "UserPromptSubmit", sessionStart: "SessionStart", subagentStart: "SubagentStart" };
|
|
113029
|
-
|
|
113223
|
+
need5(h.command === renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, nativeToCliEvent[wanted.event] ?? ""), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
113030
113224
|
}
|
|
113031
113225
|
}
|
|
113032
113226
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
113033
113227
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
113034
|
-
|
|
113228
|
+
need5(layers?.length === 1 && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
|
|
113035
113229
|
const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
|
|
113036
|
-
|
|
113230
|
+
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 || isDeepStrictEqual15(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
|
|
113037
113231
|
const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
113038
113232
|
const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
113039
|
-
|
|
113233
|
+
need5(finalLayers?.length === 1 && !finalLayers[0].disabledReason && codexNativeConfigEqual(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
|
|
113040
113234
|
if (recovery) {
|
|
113041
113235
|
for (const response of [config2, finalConfig]) {
|
|
113042
113236
|
const layerIndex = response.layers.findIndex((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
113043
|
-
|
|
113237
|
+
need5(response.config && response.config.features?.hooks !== false && response.layers.slice(0, layerIndex).every((layer) => layer.config?.hooks === undefined && layer.config?.features?.hooks === undefined), "RECONCILE_NATIVE_CONFIG_OVERRIDDEN");
|
|
113044
113238
|
}
|
|
113045
113239
|
recoveryCli.recheck();
|
|
113046
113240
|
verifyReleaseArtifact(options.releaseProof, recoveryCli);
|
|
@@ -113061,7 +113255,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
113061
113255
|
const recoveryPlan = recovery ? { version: 1, kind: additions ? claudeRecovery ? "claude-discovery-and-native-hook-additions" : "native-hook-additions" : "claude-discovery-only", journal, journalIntentSha256: intentFile.sha256, stoppedSha256: stoppedFile.sha256, originalPlanDigest: intent.planDigest, policyBeforeSha256: policyBefore.sha256, policyCurrentSha256: policyCurrent.sha256, configBeforeSha256: configBefore.sha256, configCurrentSha256: currentConfig.sha256, ...configDiscoveryRecovery ? { configDiscoveryRecovery } : {}, ...additions ? { nativeHookAdditions: additions.receipt } : {}, hooksSha256: hooksCurrent.sha256, nativeConfigPath, nativeVersion: rpc.version, nativeInventorySha256: makePlanDigest(nativeHooks.map(safeNativeHook)), recordedNativeInventorySha256: makePlanDigest(recordedNativeHooks), boundSkillsCli: skillsCli.receipt, recoveryCli: recoveryCli.receipt, releaseProof: options.releaseProof, ...policyChange } : undefined;
|
|
113062
113256
|
const recoveryPlanDigest = recoveryPlan ? makePlanDigest(recoveryPlan) : undefined;
|
|
113063
113257
|
if (recovery?.reviewedPlanDigest !== undefined)
|
|
113064
|
-
|
|
113258
|
+
need5(recovery.reviewedPlanDigest === recoveryPlanDigest, "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
113065
113259
|
const preview2 = !!recovery && recovery.reviewedPlanDigest === undefined;
|
|
113066
113260
|
const receipt = { version: 1, status: preview2 ? "recovery-review-required" : supersededBinding ? "superseded" : "reconciled", reconciled: !preview2, supersededBinding, automaticRollback: false, journal, planDigest: intent.planDigest, nativeVersion: rpc.version, nativeStateVerified: !nativeTrustPending, nativeTrustPending, bindingRefreshRequired: !!configDiscoveryRecovery, nativeExecutionVerified: false, configSha256: currentConfig.sha256, hooksSha256: hooksCurrent.sha256, policySha256: policyBefore.sha256, policyBeforeSha256: policyBefore.sha256, policyCurrentSha256: policyCurrent.sha256, previousSkillsCli: intent.skillsCli, skillsCli: skillsCli.receipt, releaseProof: options.releaseProof, nativeInventoryScope: recordedNativeHooks ? "complete" : "declared-managed-hooks-only", unrelatedNativeHooksPreserved: !!recordedNativeHooks, unrelatedSettingsAndCommentsPreserved: true, ...recoveryPlan ? { recoveryPlan, recoveryPlanDigest } : {} };
|
|
113067
113261
|
if (preview2)
|
|
@@ -113084,7 +113278,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
113084
113278
|
const binding = policy.bridge, alias = binding.rootAliases?.find((item) => item.agent === "codex");
|
|
113085
113279
|
const skillsCli = bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
113086
113280
|
const physicalRoot = alias?.target ?? join61(home, ".codex"), hooksPath = join61(physicalRoot, "hooks.json"), configPath = join61(physicalRoot, "config.toml");
|
|
113087
|
-
|
|
113281
|
+
need5(options.codexHome === undefined || isAbsolute28(options.codexHome) && [join61(home, ".codex"), physicalRoot].includes(options.codexHome), "NATIVE_HOME_NOT_ADMITTED");
|
|
113088
113282
|
const codexHome = options.codexHome === undefined ? undefined : physicalRoot;
|
|
113089
113283
|
const nativeConfigPath = join61(codexHome ?? join61(home, ".codex"), "config.toml");
|
|
113090
113284
|
const hooksFile = snapshot(hooksPath), configFile = snapshot(configPath, true), original = structure3(configFile.text);
|
|
@@ -113092,38 +113286,38 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
113092
113286
|
for (const [event, [nativeEvent, label]] of Object.entries(eventNames)) {
|
|
113093
113287
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
113094
113288
|
const groups = declarations[event];
|
|
113095
|
-
|
|
113289
|
+
need5(Array.isArray(groups), "DECLARATION_CHANGED");
|
|
113096
113290
|
const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
|
|
113097
|
-
|
|
113291
|
+
need5(matches2.length === 1 && isDeepStrictEqual15(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
|
|
113098
113292
|
expected.push({ event: nativeEvent, key: `${join61(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
|
|
113099
113293
|
}
|
|
113100
113294
|
codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
|
|
113101
113295
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
113102
|
-
|
|
113296
|
+
need5(SUPPORTED_CODEX_HOOK_VERSIONS.includes(rpc.version), "NATIVE_UNSUPPORTED_VERSION");
|
|
113103
113297
|
const list2 = async () => {
|
|
113104
113298
|
const response = await rpc.request("hooks/list", { cwds: [home] });
|
|
113105
113299
|
const nativeRoots = [...new Set([physicalRoot, join61(home, ".codex")])];
|
|
113106
113300
|
const coexistenceWarnings = nativeRoots.flatMap((hooksRoot) => nativeRoots.map((configRoot) => `loading hooks from both ${join61(hooksRoot, "hooks.json")} and ${join61(configRoot, "config.toml")}; prefer a single representation for this layer`));
|
|
113107
113301
|
const entry = response?.data?.[0];
|
|
113108
|
-
|
|
113302
|
+
need5(Array.isArray(response?.data) && response.data.length === 1 && entry.cwd === home && Array.isArray(entry.hooks) && entry.hooks.length <= 1e4 && Array.isArray(entry.errors) && entry.errors.length === 0 && Array.isArray(entry.warnings) && entry.warnings.length <= 1 && entry.warnings.every((warning) => typeof warning === "string" && coexistenceWarnings.includes(warning)), "NATIVE_DISCOVERY_REFUSED");
|
|
113109
113303
|
return response.data[0].hooks;
|
|
113110
113304
|
};
|
|
113111
113305
|
const admit = (hooks) => expected.map((wanted) => {
|
|
113112
113306
|
const canonicalKey = wanted.key.replace(join61(home, ".codex/hooks.json"), hooksPath);
|
|
113113
113307
|
const matches2 = hooks.filter((h2) => h2.key === wanted.key || h2.key === canonicalKey);
|
|
113114
|
-
|
|
113308
|
+
need5(matches2.length === 1, "AMBIGUOUS_IDENTITY");
|
|
113115
113309
|
const h = matches2[0];
|
|
113116
|
-
|
|
113117
|
-
|
|
113118
|
-
|
|
113310
|
+
need5(h.eventName === wanted.event && h.handlerType === "command" && h.command === wanted.command && h.matcher === null && h.timeoutSec === 15 && h.async === false && h.statusMessage === null && h.additionalContextLimit === null && [hooksPath, join61(home, ".codex/hooks.json")].includes(h.sourcePath) && h.source === "user" && h.pluginId === null && h.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(h.currentHash) && typeof h.enabled === "boolean", "NATIVE_IDENTITY_CHANGED");
|
|
113311
|
+
need5(hooks.filter((other) => other.command === wanted.command).length === 1, "AMBIGUOUS_IDENTITY");
|
|
113312
|
+
need5(["trusted", "untrusted", "modified"].includes(h.trustStatus), "UNKNOWN_TRUST_STATUS");
|
|
113119
113313
|
return { key: h.key, event: wanted.event, command: wanted.command, handlerType: h.handlerType, timeoutSec: h.timeoutSec, sourcePath: h.sourcePath, currentHash: h.currentHash, enabled: h.enabled, trustStatus: h.trustStatus };
|
|
113120
113314
|
});
|
|
113121
113315
|
const discovered = await list2(), admitted = admit(discovered);
|
|
113122
113316
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
113123
113317
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
113124
|
-
|
|
113125
|
-
|
|
113126
|
-
|
|
113318
|
+
need5(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, original), "NATIVE_CONFIG_MISMATCH");
|
|
113319
|
+
need5(config2.config && config2.config.features?.hooks !== false, "NATIVE_HOOKS_DISABLED");
|
|
113320
|
+
need5(config2.layers.slice(0, config2.layers.indexOf(layers[0])).every((layer) => layer.config?.hooks === undefined && layer.config?.features?.hooks === undefined), "NATIVE_CONFIG_OVERRIDDEN");
|
|
113127
113321
|
const keys3 = admitted.map((h) => h.key), planned = admitted.filter((h) => !h.enabled || h.trustStatus !== "trusted");
|
|
113128
113322
|
const preservationText = codexTrustTextWitness(configFile.text, keys3);
|
|
113129
113323
|
const checkInputs = () => {
|
|
@@ -113138,16 +113332,16 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
113138
113332
|
const planDigest = makePlanDigest(digestInput);
|
|
113139
113333
|
const baseReceipt = { planDigest, skillsCli: skillsCli.receipt, nativeVersion: rpc.version, nativeConfigPath, ownedNativeProcessId: rpc.processId, agent: "codex", applied: false, planned, nativeEligible: planned.length === 0, bindingRefreshRequired: false, transport: "owned-stdio-process", existingSessionsReloaded: false, nativeExecutionVerified: false };
|
|
113140
113334
|
if (options.apply)
|
|
113141
|
-
|
|
113335
|
+
need5(options.reviewedPlanDigest === planDigest, "PLAN_CHANGED: run the dry-run again and pass its reviewed --plan-digest with --apply");
|
|
113142
113336
|
const parent = join61(dataDir3, "native-hook-trust");
|
|
113143
113337
|
const st = lstatSync32(parent, { throwIfNoEntry: false });
|
|
113144
113338
|
if (st) {
|
|
113145
|
-
|
|
113339
|
+
need5(st.isDirectory() && !st.isSymbolicLink() && st.uid === process.getuid() && (st.mode & 511) === 448, "UNSAFE_JOURNAL");
|
|
113146
113340
|
const priorJournals = readdirSync23(parent);
|
|
113147
|
-
|
|
113341
|
+
need5(priorJournals.length < 1000, "JOURNAL_BOUND");
|
|
113148
113342
|
for (const name of priorJournals) {
|
|
113149
|
-
|
|
113150
|
-
|
|
113343
|
+
need5(/^[a-f0-9-]{36}$/.test(name), "UNSAFE_JOURNAL");
|
|
113344
|
+
need5(lstatSync32(join61(parent, name, "receipt.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_REQUIRED: inspect the incomplete private journal before applying again");
|
|
113151
113345
|
}
|
|
113152
113346
|
}
|
|
113153
113347
|
if (!options.apply || !planned.length)
|
|
@@ -113160,7 +113354,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
113160
113354
|
save(join61(journal, "hooks.before.json"), hooksFile.bytes);
|
|
113161
113355
|
save(join61(journal, "policy.before.json"), policyFile.bytes);
|
|
113162
113356
|
save(join61(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
|
|
113163
|
-
|
|
113357
|
+
need5(isDeepStrictEqual15(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
|
|
113164
113358
|
checkInputs();
|
|
113165
113359
|
let result2;
|
|
113166
113360
|
try {
|
|
@@ -113168,22 +113362,22 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
113168
113362
|
} catch {
|
|
113169
113363
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_WRITE_FAILED: reconcile the private journal before retrying");
|
|
113170
113364
|
}
|
|
113171
|
-
|
|
113365
|
+
need5(result2?.status === "ok" && result2.filePath === configPath && /^sha256:[a-f0-9]{64}$/.test(result2.version), "NATIVE_WRITE_OVERRIDDEN");
|
|
113172
113366
|
const after2 = snapshot(configPath, true), expectedConfig = structuredClone(original);
|
|
113173
113367
|
expectedConfig.hooks ??= {};
|
|
113174
113368
|
expectedConfig.hooks.state ??= {};
|
|
113175
113369
|
for (const h of planned)
|
|
113176
113370
|
expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
|
|
113177
|
-
|
|
113371
|
+
need5(isDeepStrictEqual15(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
|
|
113178
113372
|
const savedConfig = snapshot(join61(journal, "config.before.toml"), true);
|
|
113179
|
-
|
|
113373
|
+
need5(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
|
|
113180
113374
|
const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
|
|
113181
113375
|
unchanged2(policyFile);
|
|
113182
113376
|
unchanged2(hooksFile);
|
|
113183
113377
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
|
|
113184
113378
|
const current = await list2(), confirmed = admit(current);
|
|
113185
|
-
|
|
113186
|
-
|
|
113379
|
+
need5(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual15(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
|
|
113380
|
+
need5(isDeepStrictEqual15(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
|
|
113187
113381
|
unchanged2(after2);
|
|
113188
113382
|
unchanged2(savedConfig);
|
|
113189
113383
|
unchanged2(hooksFile);
|
|
@@ -113346,7 +113540,7 @@ var exports_agent_integration = {};
|
|
|
113346
113540
|
__export(exports_agent_integration, {
|
|
113347
113541
|
registerAgentIntegration: () => registerAgentIntegration
|
|
113348
113542
|
});
|
|
113349
|
-
import { existsSync as existsSync36, lstatSync as lstatSync33, readFileSync as readFileSync41, writeFileSync as writeFileSync26 } from "fs";
|
|
113543
|
+
import { existsSync as existsSync36, lstatSync as lstatSync33, readFileSync as readFileSync41, readSync as readSync17, writeFileSync as writeFileSync26 } from "fs";
|
|
113350
113544
|
import { createHash as createHash32 } from "crypto";
|
|
113351
113545
|
import { homedir as homedir13 } from "os";
|
|
113352
113546
|
import { isAbsolute as isAbsolute29 } from "path";
|
|
@@ -113416,6 +113610,32 @@ function agents(value) {
|
|
|
113416
113610
|
}
|
|
113417
113611
|
function registerAgentIntegration(parent) {
|
|
113418
113612
|
const hook = parent.command("hook").description("Load selected Skills context through agent lifecycle hooks");
|
|
113613
|
+
hook.command("project-claude-settings").requiredOption("--expected-target-sha256 <sha256>", "SHA-256 of the exact target settings bytes supplied on stdin").option("--expected-source-sha256 <sha256>", "Source witness from the preceding projection, for revalidation").option("--json", "Return only command-leaf replacements and content witnesses", false).description("Read-only projection of verified Skills hooks into copied Claude settings supplied on stdin").action(async (options) => {
|
|
113614
|
+
try {
|
|
113615
|
+
const bytes = Buffer.alloc(1024 * 1024 + 1);
|
|
113616
|
+
let length = 0;
|
|
113617
|
+
while (length < bytes.length) {
|
|
113618
|
+
const count = readSync17(0, bytes, length, bytes.length - length, null);
|
|
113619
|
+
if (!count)
|
|
113620
|
+
break;
|
|
113621
|
+
length += count;
|
|
113622
|
+
}
|
|
113623
|
+
if (length > 1024 * 1024)
|
|
113624
|
+
throw new Error("CLAUDE_MANAGED_HOOK_TARGET_INVALID");
|
|
113625
|
+
const targetSettings = new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, length));
|
|
113626
|
+
const plan = planClaudeManagedHookProjection({
|
|
113627
|
+
targetSettings,
|
|
113628
|
+
expectedTargetSha256: options.expectedTargetSha256,
|
|
113629
|
+
expectedSourceSha256: options.expectedSourceSha256,
|
|
113630
|
+
projectDir: process.cwd()
|
|
113631
|
+
});
|
|
113632
|
+
await writeCliOutput(JSON.stringify(plan));
|
|
113633
|
+
} catch (error61) {
|
|
113634
|
+
const code2 = error61 instanceof Error && /^CLAUDE_MANAGED_HOOK_[A-Z_]+$/.test(error61.message) ? error61.message : "CLAUDE_MANAGED_HOOK_PROJECTION_FAILED";
|
|
113635
|
+
console.error(code2);
|
|
113636
|
+
process.exitCode = 1;
|
|
113637
|
+
}
|
|
113638
|
+
});
|
|
113419
113639
|
hook.command("native-catalog").requiredOption("--cwd <path>", "Absolute project directory observed by native Codex skills/list").requiredOption("--output <file>", "New private file for the projected native catalog").option("--codex-command <path>", "Installed Codex executable used by the native client", "codex").option("--json", "Output bounded capture metadata as JSON", false).description("Capture a reviewed Codex native skill catalog for exact hook enrollment").action(async (options) => {
|
|
113420
113640
|
try {
|
|
113421
113641
|
if (!isAbsolute29(options.cwd) || !isAbsolute29(options.output))
|
|
@@ -113827,7 +114047,7 @@ var exports_profiles = {};
|
|
|
113827
114047
|
__export(exports_profiles, {
|
|
113828
114048
|
registerProfiles: () => registerProfiles
|
|
113829
114049
|
});
|
|
113830
|
-
import { closeSync as closeSync21, constants as constants20, fstatSync as fstatSync21, openSync as openSync21, readSync as
|
|
114050
|
+
import { closeSync as closeSync21, constants as constants20, fstatSync as fstatSync21, openSync as openSync21, readSync as readSync18, writeFileSync as writeFileSync27 } from "fs";
|
|
113831
114051
|
function readProfileInput(path) {
|
|
113832
114052
|
const fd = openSync21(path, constants20.O_RDONLY | constants20.O_NONBLOCK);
|
|
113833
114053
|
try {
|
|
@@ -113837,7 +114057,7 @@ function readProfileInput(path) {
|
|
|
113837
114057
|
const bytes = Buffer.alloc(MAX_PROFILE_DOCUMENT_BYTES + 1);
|
|
113838
114058
|
let size2 = 0;
|
|
113839
114059
|
while (size2 < bytes.length) {
|
|
113840
|
-
const read3 =
|
|
114060
|
+
const read3 = readSync18(fd, bytes, size2, bytes.length - size2, null);
|
|
113841
114061
|
if (!read3)
|
|
113842
114062
|
break;
|
|
113843
114063
|
size2 += read3;
|
|
@@ -113901,7 +114121,7 @@ import {
|
|
|
113901
114121
|
closeSync as closeSync23,
|
|
113902
114122
|
fstatSync as fstatSync23,
|
|
113903
114123
|
openSync as openSync23,
|
|
113904
|
-
readSync as
|
|
114124
|
+
readSync as readSync19,
|
|
113905
114125
|
writeFileSync as writeFileSync28
|
|
113906
114126
|
} from "fs";
|
|
113907
114127
|
function readInput(path) {
|
|
@@ -113913,7 +114133,7 @@ function readInput(path) {
|
|
|
113913
114133
|
const bytes = Buffer.alloc(MAX_EXECUTION_GRANT_BYTES + 1);
|
|
113914
114134
|
let size2 = 0;
|
|
113915
114135
|
while (size2 < bytes.length) {
|
|
113916
|
-
const count =
|
|
114136
|
+
const count = readSync19(fd, bytes, size2, bytes.length - size2, null);
|
|
113917
114137
|
if (!count)
|
|
113918
114138
|
break;
|
|
113919
114139
|
size2 += count;
|