@hasna/skills 0.9.14 → 0.9.16
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 +7 -0
- package/bin/index.js +291 -230
- package/bin/mcp.js +41 -15
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +39 -6
- package/dist/lib/agent-codex-trust.d.ts +2 -1
- package/dist/lib/agent-discovery.d.ts +1 -0
- package/dist/lib/agent-integration.d.ts +5 -0
- package/dist/lib/codex-hook-additions-review.d.ts +22 -1
- package/dist/lib/codex-hook-discovery-recovery.d.ts +2 -1
- package/dist/sdk/index.js +64 -31
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.9.
|
|
36871
|
+
version: "0.9.16",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -51146,7 +51146,15 @@ function planAgentIntegration(options) {
|
|
|
51146
51146
|
validateProfile(profileId2);
|
|
51147
51147
|
return [agent, { command, profileId: profileId2 }];
|
|
51148
51148
|
}));
|
|
51149
|
-
const
|
|
51149
|
+
const retainedAgents = [];
|
|
51150
|
+
const discoveries = [...new Set(options.agents)].map((agent) => {
|
|
51151
|
+
const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? policy.bridge.discovery[agent] : undefined;
|
|
51152
|
+
if (retainedReview) {
|
|
51153
|
+
assertManagedAgentBridge(agent, { home, dataDir: dataDir3, projectDir: options.projectDir ?? home });
|
|
51154
|
+
retainedAgents.push(agent);
|
|
51155
|
+
}
|
|
51156
|
+
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
51157
|
+
});
|
|
51150
51158
|
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 });
|
|
51151
51159
|
const changes = [];
|
|
51152
51160
|
for (const agent of [...new Set(options.agents)]) {
|
|
@@ -51231,7 +51239,7 @@ function planAgentIntegration(options) {
|
|
|
51231
51239
|
if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
|
|
51232
51240
|
changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
|
|
51233
51241
|
recheckRootAliases(aliases);
|
|
51234
|
-
return { dataDir: dataDir3, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
51242
|
+
return { dataDir: dataDir3, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
|
|
51235
51243
|
}
|
|
51236
51244
|
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
51237
51245
|
try {
|
|
@@ -51269,6 +51277,8 @@ function applyAgentIntegration(plan) {
|
|
|
51269
51277
|
parseManagedSkillPolicy(change.after);
|
|
51270
51278
|
const aliases = plan.rootAliases ?? [];
|
|
51271
51279
|
recheckRootAliases(aliases);
|
|
51280
|
+
for (const agent of plan.retainedReviewChecks?.agents ?? [])
|
|
51281
|
+
assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
|
|
51272
51282
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
51273
51283
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
51274
51284
|
for (const binding of plan.discoveryBefore ?? []) {
|
|
@@ -54696,39 +54706,132 @@ var fail2 = () => new Error('CODEX_HOOK_TRUST_UNSUPPORTED_LAYOUT: preserve and c
|
|
|
54696
54706
|
};
|
|
54697
54707
|
var init_codex_hook_trust_layout = () => {};
|
|
54698
54708
|
|
|
54709
|
+
// src/lib/codex-hook-additions-review.ts
|
|
54710
|
+
import { isAbsolute as isAbsolute14 } from "path";
|
|
54711
|
+
import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
|
|
54712
|
+
function exactKeys2(value, keys2) {
|
|
54713
|
+
need4(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual5(Object.keys(value).sort(), [...keys2].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
54714
|
+
}
|
|
54715
|
+
function readReviewedAdditionTrustStates(proof, before2, current, journalHooks) {
|
|
54716
|
+
const captured = proofs.get(proof);
|
|
54717
|
+
need4(!!captured && captured.before.file === before2.file && captured.before.sha256 === before2.sha256 && captured.current.file === current.file && captured.current.sha256 === current.sha256 && isDeepStrictEqual5(captured.journalHooks, journalHooks.map(({ key, currentHash }) => ({ key, currentHash }))), "RECONCILE_ADDITIONS_PROOF_CHANGED");
|
|
54718
|
+
captured.recheck();
|
|
54719
|
+
unchanged2(before2);
|
|
54720
|
+
unchanged2(current);
|
|
54721
|
+
return captured.states;
|
|
54722
|
+
}
|
|
54723
|
+
function reviewNativeHookAdditions(options) {
|
|
54724
|
+
need4(isAbsolute14(options.path), "RECONCILE_ADDITIONS_REVIEW_PATH");
|
|
54725
|
+
const reviewFile = snapshot(options.path, true), review = JSON.parse(reviewFile.text);
|
|
54726
|
+
exactKeys2(review, ["version", "kind", "reason", "producerEvidence", "journalIntentSha256", "policySha256", "configCurrentSha256", "hooksCurrentSha256", "producerBefore", "additions"]);
|
|
54727
|
+
need4((review.version === 1 || review.version === 2) && review.kind === "native-hook-additions" && text2(review.reason) && text2(review.producerEvidence), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
54728
|
+
for (const [name, expected] of [["journalIntentSha256", options.intent.sha256], ["policySha256", options.policy.sha256], ["configCurrentSha256", options.configCurrent.sha256], ["hooksCurrentSha256", options.hooksCurrent.sha256]])
|
|
54729
|
+
need4(hash2(review[name]) && review[name] === expected, "RECONCILE_ADDITIONS_REVIEW_CHANGED");
|
|
54730
|
+
exactKeys2(review.producerBefore, ["configPath", "configSha256", "hooksPath", "hooksSha256"]);
|
|
54731
|
+
const before2 = review.producerBefore;
|
|
54732
|
+
need4(isAbsolute14(before2.configPath) && isAbsolute14(before2.hooksPath) && hash2(before2.configSha256) && hash2(before2.hooksSha256), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
54733
|
+
const producerConfig = snapshot(before2.configPath, true), producerHooks = snapshot(before2.hooksPath, true);
|
|
54734
|
+
need4(producerConfig.sha256 === before2.configSha256 && producerHooks.sha256 === before2.hooksSha256 && producerHooks.sha256 === options.hooksBefore.sha256, "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
54735
|
+
const original = structure(options.configBefore.text), producer = structure(producerConfig.text), expectedProducer = structuredClone(original), originalKeys = options.journalHooks.map((hook) => hook.key);
|
|
54736
|
+
expectedProducer.hooks ??= {};
|
|
54737
|
+
expectedProducer.hooks.state ??= {};
|
|
54738
|
+
for (const hook of options.journalHooks)
|
|
54739
|
+
expectedProducer.hooks.state[hook.key] = { ...expectedProducer.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
54740
|
+
need4(isDeepStrictEqual5(expectedProducer, producer) && codexTrustReconcileWitness(options.configBefore.text, originalKeys) === codexTrustReconcileWitness(producerConfig.text, originalKeys) && codexTrustUnmanagedSemantic(options.configBefore.text, originalKeys) === codexTrustUnmanagedSemantic(producerConfig.text, originalKeys), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
54741
|
+
need4(Array.isArray(review.additions) && review.additions.length > 0 && review.additions.length <= 20, "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
54742
|
+
const expectedHooks = JSON.parse(options.hooksBefore.text), currentHooks = JSON.parse(options.hooksCurrent.text), expectedConfig = structuredClone(producer);
|
|
54743
|
+
need4(expectedHooks.hooks && typeof expectedHooks.hooks === "object" && !Array.isArray(expectedHooks.hooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
54744
|
+
const added = [], states = [], seen = new Set, commands = new Set(options.recordedNativeHooks.map((hook) => hook.command));
|
|
54745
|
+
for (const addition of review.additions) {
|
|
54746
|
+
exactKeys2(addition, review.version === 1 ? ["event", "nativeHook"] : ["event", "nativeHook", "trustState"]);
|
|
54747
|
+
need4(Object.hasOwn(review.version === 1 ? events : matchedEvents, addition.event), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
54748
|
+
const { event, nativeHook: hook } = addition;
|
|
54749
|
+
exactKeys2(hook, nativeKeys);
|
|
54750
|
+
const groups = expectedHooks.hooks[event] ?? [];
|
|
54751
|
+
need4(Array.isArray(groups), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
54752
|
+
const key = `${options.nativeHooksPath}:${matchedEvents[event][1]}:${groups.length}:0`;
|
|
54753
|
+
need4(hook.key === key && !seen.has(key) && !options.recordedNativeHooks.some((originalHook) => originalHook.key === key) && !Object.hasOwn(expectedConfig.hooks.state, key), "RECONCILE_ADDITIONS_IDENTITY");
|
|
54754
|
+
need4(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");
|
|
54755
|
+
let trustState = { enabled: true, trusted_hash: hook.currentHash };
|
|
54756
|
+
if (review.version === 2) {
|
|
54757
|
+
const state = addition.trustState;
|
|
54758
|
+
exactKeys2(state, state && Object.hasOwn(state, "enabled") ? ["enabled", "trusted_hash"] : ["trusted_hash"]);
|
|
54759
|
+
need4(state.trusted_hash === hook.currentHash && (!Object.hasOwn(state, "enabled") || state.enabled === true), "RECONCILE_ADDITIONS_TRUST_STATE");
|
|
54760
|
+
trustState = { ...state };
|
|
54761
|
+
}
|
|
54762
|
+
seen.add(key);
|
|
54763
|
+
commands.add(hook.command);
|
|
54764
|
+
added.push(hook);
|
|
54765
|
+
states.push(Object.freeze({ key, state: Object.freeze(trustState) }));
|
|
54766
|
+
expectedHooks.hooks[event] = [...groups, { ...event === "PreToolUse" ? { matcher: hook.matcher } : {}, hooks: [{ type: "command", command: hook.command, timeout: hook.timeoutSec }] }];
|
|
54767
|
+
expectedConfig.hooks.state[key] = trustState;
|
|
54768
|
+
}
|
|
54769
|
+
need4(isDeepStrictEqual5(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
54770
|
+
const keys2 = added.map((hook) => hook.key);
|
|
54771
|
+
need4(isDeepStrictEqual5(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");
|
|
54772
|
+
const inputs = [reviewFile, producerConfig, producerHooks, options.intent, options.policy, options.configBefore, options.configCurrent, options.hooksBefore, options.hooksCurrent];
|
|
54773
|
+
const recheck = () => {
|
|
54774
|
+
for (const input of inputs)
|
|
54775
|
+
unchanged2(input);
|
|
54776
|
+
};
|
|
54777
|
+
recheck();
|
|
54778
|
+
const trustProof = Object.freeze({ reviewSha256: reviewFile.sha256 });
|
|
54779
|
+
proofs.set(trustProof, { before: options.configBefore, current: options.configCurrent, journalHooks: options.journalHooks.map(({ key, currentHash }) => ({ key, currentHash })), states: Object.freeze(states), recheck });
|
|
54780
|
+
return { hooks: added, recheck, trustProof, receipt: { ...review.version === 2 ? { version: 2 } : {}, reviewPath: reviewFile.file, reviewSha256: reviewFile.sha256, reason: review.reason, producerEvidence: review.producerEvidence, producerBefore: { configPath: producerConfig.file, configSha256: producerConfig.sha256, hooksPath: producerHooks.file, hooksSha256: producerHooks.sha256 }, additions: review.version === 1 ? added : review.additions } };
|
|
54781
|
+
}
|
|
54782
|
+
var events, matchedEvents, hash2 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), text2 = (value, max2 = 2048) => typeof value === "string" && value.trim().length > 0 && value.length <= max2 && !/[\0\r\n]/.test(value), v2Command = (value) => typeof value === "string" && value.trim().length > 0 && value.length <= 16384 && !/[\0\r]/.test(value), structure = (value) => JSON.parse(JSON.stringify(Bun.TOML.parse(value))), nativeKeys, proofs;
|
|
54783
|
+
var init_codex_hook_additions_review = __esm(() => {
|
|
54784
|
+
init_codex_hook_trust_files();
|
|
54785
|
+
init_codex_hook_trust_layout();
|
|
54786
|
+
events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
54787
|
+
matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
54788
|
+
nativeKeys = ["key", "eventName", "handlerType", "command", "matcher", "timeoutSec", "async", "statusMessage", "additionalContextLimit", "sourcePath", "source", "pluginId", "isManaged", "currentHash", "enabled", "trustStatus"];
|
|
54789
|
+
proofs = new WeakMap;
|
|
54790
|
+
});
|
|
54791
|
+
|
|
54699
54792
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
54700
54793
|
import { createHash as createHash15 } from "crypto";
|
|
54701
|
-
import { isDeepStrictEqual as
|
|
54702
|
-
function createCodexHookDiscoveryRecovery(binding, before2, current, hooks) {
|
|
54794
|
+
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
54795
|
+
function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, additions) {
|
|
54796
|
+
const journalHooks = hooks.map(({ key, currentHash }) => ({ key, currentHash }));
|
|
54797
|
+
const additionStates = additions ? readReviewedAdditionTrustStates(additions, before2, current, journalHooks) : [];
|
|
54798
|
+
const recheckAdditions = () => {
|
|
54799
|
+
if (additions)
|
|
54800
|
+
readReviewedAdditionTrustStates(additions, before2, current, journalHooks);
|
|
54801
|
+
};
|
|
54703
54802
|
const raw = binding.sources.filter((source) => source.path === current.file && source.hashMode === "bytes");
|
|
54704
54803
|
if (!raw.some((source) => source.sha256 !== current.sha256))
|
|
54705
54804
|
return;
|
|
54706
54805
|
need4(binding.agent === "codex" && binding.method === "reviewed" && raw.length === 1 && raw[0].sha256 === before2.sha256 && before2.sha256 !== current.sha256, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
|
|
54707
54806
|
need4(raw[0].format === undefined && raw[0].fields === undefined && raw[0].managedPlugins === undefined, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
|
|
54708
54807
|
const typed = binding.sources.filter((source) => source.path === current.file && source.format === "toml");
|
|
54709
|
-
need4(typed.length === 1 && typed[0].hashMode === undefined &&
|
|
54710
|
-
const original =
|
|
54808
|
+
need4(typed.length === 1 && typed[0].hashMode === undefined && isDeepStrictEqual6(typed[0].fields, fields), "RECONCILE_DISCOVERY_TYPED_WITNESS");
|
|
54809
|
+
const original = structure2(before2.text), actual = structure2(current.text);
|
|
54711
54810
|
const project = (value) => Object.fromEntries(fields.map((field2) => [field2, value[field2] ?? null]));
|
|
54712
54811
|
const projection = sha2(JSON.stringify(project(original)));
|
|
54713
|
-
need4(typed[0].sha256 === projection && sha2(JSON.stringify(project(actual))) === projection &&
|
|
54714
|
-
|
|
54715
|
-
|
|
54812
|
+
need4(typed[0].sha256 === projection && sha2(JSON.stringify(project(actual))) === projection && isDeepStrictEqual6(project(original), project(actual)), "RECONCILE_DISCOVERY_TYPED_WITNESS");
|
|
54813
|
+
const keys2 = [...journalHooks.map((hook) => hook.key), ...additionStates.map((hook) => hook.key)];
|
|
54814
|
+
need4(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");
|
|
54815
|
+
const expected = structuredClone(original);
|
|
54716
54816
|
expected.hooks ??= {};
|
|
54717
54817
|
expected.hooks.state ??= {};
|
|
54718
|
-
for (const hook of
|
|
54818
|
+
for (const hook of journalHooks)
|
|
54719
54819
|
expected.hooks.state[hook.key] = { ...expected.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
54720
|
-
|
|
54820
|
+
for (const { key, state } of additionStates)
|
|
54821
|
+
expected.hooks.state[key] = { ...state };
|
|
54822
|
+
need4(isDeepStrictEqual6(expected, actual) && codexTrustReconcileWitness(before2.text, keys2) === codexTrustReconcileWitness(current.text, keys2) && codexTrustUnmanagedSemantic(before2.text, keys2) === codexTrustUnmanagedSemantic(current.text, keys2), "RECONCILE_DISCOVERY_TRUST_WITNESS");
|
|
54721
54823
|
unchanged2(before2);
|
|
54722
54824
|
unchanged2(current);
|
|
54723
54825
|
const witness = Object.freeze({ configPath: current.file, beforeSha256: before2.sha256, currentSha256: current.sha256, discoverySha256: sha2(JSON.stringify(binding)), typedProjectionSha256: projection });
|
|
54724
|
-
|
|
54826
|
+
proofs2.set(witness, { before: before2, current, recheckAdditions });
|
|
54725
54827
|
return witness;
|
|
54726
54828
|
}
|
|
54727
54829
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
54728
|
-
const proof =
|
|
54830
|
+
const proof = proofs2.get(witness);
|
|
54729
54831
|
need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha2(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
54730
54832
|
unchanged2(proof.before);
|
|
54731
54833
|
unchanged2(proof.current);
|
|
54834
|
+
proof.recheckAdditions();
|
|
54732
54835
|
}
|
|
54733
54836
|
function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
|
|
54734
54837
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
@@ -54737,21 +54840,23 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
54737
54840
|
need4(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
54738
54841
|
return true;
|
|
54739
54842
|
}
|
|
54740
|
-
var sha2 = (
|
|
54843
|
+
var sha2 = (text3) => createHash15("sha256").update(text3).digest("hex"), fields, structure2 = (text3) => JSON.parse(JSON.stringify(Bun.TOML.parse(text3))), proofs2;
|
|
54741
54844
|
var init_codex_hook_discovery_recovery = __esm(() => {
|
|
54742
54845
|
init_codex_hook_trust_files();
|
|
54743
54846
|
init_codex_hook_trust_layout();
|
|
54847
|
+
init_codex_hook_additions_review();
|
|
54744
54848
|
fields = ["plugins", "marketplaces", "skills"];
|
|
54745
|
-
|
|
54849
|
+
proofs2 = new WeakMap;
|
|
54746
54850
|
});
|
|
54747
54851
|
|
|
54748
54852
|
// src/lib/agent-discovery.ts
|
|
54749
54853
|
import { createHash as createHash16 } from "crypto";
|
|
54750
54854
|
import { lstatSync as lstatSync20, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync4 } from "fs";
|
|
54751
|
-
import { basename as basename8, dirname as dirname16, isAbsolute as
|
|
54752
|
-
|
|
54855
|
+
import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute15, join as join31, resolve as resolve15, sep as sep5 } from "path";
|
|
54856
|
+
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
54857
|
+
function parseConfig(text3, path, toml = false) {
|
|
54753
54858
|
try {
|
|
54754
|
-
const value = toml ? Bun.TOML.parse(
|
|
54859
|
+
const value = toml ? Bun.TOML.parse(text3) : JSON.parse(text3);
|
|
54755
54860
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
54756
54861
|
throw new Error;
|
|
54757
54862
|
return value;
|
|
@@ -54760,7 +54865,7 @@ function parseConfig(text2, path, toml = false) {
|
|
|
54760
54865
|
}
|
|
54761
54866
|
}
|
|
54762
54867
|
function safe4(path) {
|
|
54763
|
-
if (!
|
|
54868
|
+
if (!isAbsolute15(path) || path.includes("\x00"))
|
|
54764
54869
|
throw new Error("Expected an absolute native discovery path");
|
|
54765
54870
|
for (let cursor = path;; cursor = dirname16(cursor)) {
|
|
54766
54871
|
if (lstatSync20(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
@@ -54813,12 +54918,12 @@ function projected(source, changes, budget = discoveryByteBudget()) {
|
|
|
54813
54918
|
return hashDiscoveryPathFile(source.path, budget, changes);
|
|
54814
54919
|
return hashRawDiscoveryFile(source.path, budget, changes);
|
|
54815
54920
|
}
|
|
54816
|
-
const
|
|
54817
|
-
if (
|
|
54921
|
+
const text3 = read2(source.path, changes);
|
|
54922
|
+
if (text3 === null)
|
|
54818
54923
|
return null;
|
|
54819
54924
|
if (!source.format)
|
|
54820
|
-
return digest(
|
|
54821
|
-
const object4 = source.format === "yaml" ? parseHermesConfig(
|
|
54925
|
+
return digest(text3);
|
|
54926
|
+
const object4 = source.format === "yaml" ? parseHermesConfig(text3) : parseConfig(text3, source.path, source.format === "toml");
|
|
54822
54927
|
if (!object4 || typeof object4 !== "object" || Array.isArray(object4))
|
|
54823
54928
|
throw new Error("Expected native discovery configuration object");
|
|
54824
54929
|
return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field2) => [field2, object4[field2] ?? null]))));
|
|
@@ -54858,7 +54963,10 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
54858
54963
|
const budget = discoveryByteBudget();
|
|
54859
54964
|
return { ...binding, sources: binding.sources.map((source) => {
|
|
54860
54965
|
assertMarketplaceBinding(binding, source);
|
|
54861
|
-
|
|
54966
|
+
const sha2562 = projected(source, changes, budget);
|
|
54967
|
+
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
54968
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
54969
|
+
return { ...source, sha256: sha2562 };
|
|
54862
54970
|
}) };
|
|
54863
54971
|
}
|
|
54864
54972
|
function assertProjectDiscovery(agent, directories, home, canonical4 = resolve15, reviewed) {
|
|
@@ -54962,14 +55070,27 @@ function resolveAgentDiscovery(options) {
|
|
|
54962
55070
|
if (!lstatSync20(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
54963
55071
|
unresolved("installed Gemini builtin root is missing");
|
|
54964
55072
|
for (const name of readdirSync10(builtinRoot).sort()) {
|
|
54965
|
-
const
|
|
54966
|
-
const match =
|
|
55073
|
+
const text3 = witness(join31(builtinRoot, name, "SKILL.md"));
|
|
55074
|
+
const match = text3?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
|
|
54967
55075
|
if (!match?.[1] || builtinNames.includes(match[1]))
|
|
54968
55076
|
unresolved("installed Gemini builtin name is missing or ambiguous");
|
|
54969
55077
|
builtinNames.push(match[1]);
|
|
54970
55078
|
}
|
|
54971
55079
|
}
|
|
54972
55080
|
}
|
|
55081
|
+
if (options.reviewed === undefined && options.retainedReview !== undefined) {
|
|
55082
|
+
const retained = options.retainedReview;
|
|
55083
|
+
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
55084
|
+
unresolved("invalid retained review");
|
|
55085
|
+
verifyAgentDiscovery(retained);
|
|
55086
|
+
if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
|
|
55087
|
+
unresolved("retained review is missing the full agent configuration source");
|
|
55088
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2") && source.path !== canonical4(configPath)))
|
|
55089
|
+
unresolved("retained settings witness names another configuration source");
|
|
55090
|
+
if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual7(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
55091
|
+
unresolved("retained review is missing current runtime discovery coverage");
|
|
55092
|
+
return retained;
|
|
55093
|
+
}
|
|
54973
55094
|
if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
|
|
54974
55095
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
54975
55096
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
@@ -55041,7 +55162,7 @@ function resolveAgentDiscovery(options) {
|
|
|
55041
55162
|
unresolved("unsupported plugin enablement");
|
|
55042
55163
|
const split2 = id.lastIndexOf("@"), name = id.slice(0, split2), marketplace = id.slice(split2 + 1);
|
|
55043
55164
|
const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
55044
|
-
if (split2 < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !
|
|
55165
|
+
if (split2 < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute15(market.source))
|
|
55045
55166
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
55046
55167
|
const catalogText = witness(join31(market.source, ".agents/plugins/marketplace.json"));
|
|
55047
55168
|
if (catalogText === null)
|
|
@@ -55057,7 +55178,7 @@ function resolveAgentDiscovery(options) {
|
|
|
55057
55178
|
if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
|
|
55058
55179
|
unresolved("external plugin or remote skill source");
|
|
55059
55180
|
for (const path of config.skills?.paths ?? []) {
|
|
55060
|
-
if (typeof path !== "string" || !
|
|
55181
|
+
if (typeof path !== "string" || !isAbsolute15(path))
|
|
55061
55182
|
unresolved("relative or malformed added skill root");
|
|
55062
55183
|
roots.add(path);
|
|
55063
55184
|
}
|
|
@@ -55095,7 +55216,7 @@ function resolveAgentDiscovery(options) {
|
|
|
55095
55216
|
}
|
|
55096
55217
|
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join31(home, ".hermes/plugins"), join31(home, ".hermes/hermes-agent")].map((path) => canonical4(path))) } : {} };
|
|
55097
55218
|
}
|
|
55098
|
-
var digest = (
|
|
55219
|
+
var digest = (text3) => createHash16("sha256").update(text3).digest("hex");
|
|
55099
55220
|
var init_agent_discovery = __esm(() => {
|
|
55100
55221
|
init_agent_policy_limits();
|
|
55101
55222
|
init_agent_hermes();
|
|
@@ -55227,7 +55348,7 @@ import {
|
|
|
55227
55348
|
statSync as statSync10,
|
|
55228
55349
|
writeFileSync as writeFileSync9
|
|
55229
55350
|
} from "fs";
|
|
55230
|
-
import { basename as basename10, dirname as dirname18, isAbsolute as
|
|
55351
|
+
import { basename as basename10, dirname as dirname18, isAbsolute as isAbsolute16, join as join33, normalize as normalize2, relative as relative5, resolve as resolve17, sep as sep7 } from "path";
|
|
55231
55352
|
function getPortableSkillsRoot(options = {}) {
|
|
55232
55353
|
return resolvePortableSkillsRoot(options, true);
|
|
55233
55354
|
}
|
|
@@ -55385,7 +55506,7 @@ function assertDisjointImportPaths(source, destination) {
|
|
|
55385
55506
|
const destinationPath = physicalImportPath(destination);
|
|
55386
55507
|
const contains2 = (parent, child) => {
|
|
55387
55508
|
const path = relative5(parent, child);
|
|
55388
|
-
return path === "" || !
|
|
55509
|
+
return path === "" || !isAbsolute16(path) && path !== ".." && !path.startsWith(`..${sep7}`);
|
|
55389
55510
|
};
|
|
55390
55511
|
if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
|
|
55391
55512
|
throw new Error("Source and destination skill directories must not overlap");
|
|
@@ -55602,7 +55723,7 @@ function safeIsDirectory(path) {
|
|
|
55602
55723
|
}
|
|
55603
55724
|
}
|
|
55604
55725
|
function isSafeRelativePath2(value) {
|
|
55605
|
-
if (!value.trim() ||
|
|
55726
|
+
if (!value.trim() || isAbsolute16(value))
|
|
55606
55727
|
return false;
|
|
55607
55728
|
const normalized = normalize2(value).replace(/\\/g, "/");
|
|
55608
55729
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
@@ -57626,8 +57747,8 @@ function getPublicSkillDiscovery(skill) {
|
|
|
57626
57747
|
function publicDiscoveryTags(tags) {
|
|
57627
57748
|
return tags.filter((tag) => !VENDOR_TAGS.has(tag.toLowerCase()));
|
|
57628
57749
|
}
|
|
57629
|
-
function sanitizePublicDiscoveryText(
|
|
57630
|
-
let sanitized =
|
|
57750
|
+
function sanitizePublicDiscoveryText(text3) {
|
|
57751
|
+
let sanitized = text3.replace(vendorPattern, "hosted AI").replace(/\bLLM\b/g, "AI").replace(/\s{2,}/g, " ");
|
|
57631
57752
|
let previous;
|
|
57632
57753
|
do {
|
|
57633
57754
|
previous = sanitized;
|
|
@@ -66247,8 +66368,8 @@ function lt_default() {
|
|
|
66247
66368
|
localeError: error33()
|
|
66248
66369
|
};
|
|
66249
66370
|
}
|
|
66250
|
-
var capitalizeFirstCharacter = (
|
|
66251
|
-
return
|
|
66371
|
+
var capitalizeFirstCharacter = (text3) => {
|
|
66372
|
+
return text3.charAt(0).toUpperCase() + text3.slice(1);
|
|
66252
66373
|
}, error33 = () => {
|
|
66253
66374
|
const Sizable = {
|
|
66254
66375
|
string: {
|
|
@@ -74081,7 +74202,7 @@ __export(exports_schemas2, {
|
|
|
74081
74202
|
httpUrl: () => httpUrl,
|
|
74082
74203
|
hostname: () => hostname2,
|
|
74083
74204
|
hex: () => hex2,
|
|
74084
|
-
hash: () =>
|
|
74205
|
+
hash: () => hash3,
|
|
74085
74206
|
guid: () => guid2,
|
|
74086
74207
|
function: () => _function,
|
|
74087
74208
|
float64: () => float64,
|
|
@@ -74287,7 +74408,7 @@ function hostname2(_params) {
|
|
|
74287
74408
|
function hex2(_params) {
|
|
74288
74409
|
return _stringFormat(ZodCustomStringFormat, "hex", exports_regexes.hex, _params);
|
|
74289
74410
|
}
|
|
74290
|
-
function
|
|
74411
|
+
function hash3(alg, params) {
|
|
74291
74412
|
const enc = params?.enc ?? "hex";
|
|
74292
74413
|
const format = `${alg}_${enc}`;
|
|
74293
74414
|
const regex2 = exports_regexes[format];
|
|
@@ -76547,7 +76668,7 @@ __export(exports_external, {
|
|
|
76547
76668
|
httpUrl: () => httpUrl,
|
|
76548
76669
|
hostname: () => hostname2,
|
|
76549
76670
|
hex: () => hex2,
|
|
76550
|
-
hash: () =>
|
|
76671
|
+
hash: () => hash3,
|
|
76551
76672
|
guid: () => guid2,
|
|
76552
76673
|
gte: () => _gte,
|
|
76553
76674
|
gt: () => _gt,
|
|
@@ -76782,8 +76903,8 @@ function safeAvailabilityCode(code) {
|
|
|
76782
76903
|
return;
|
|
76783
76904
|
return /^[A-Z0-9_]+$/.test(code) ? code : undefined;
|
|
76784
76905
|
}
|
|
76785
|
-
function sanitizeAvailabilityText(
|
|
76786
|
-
return secretValuePatterns.reduce((value, pattern) => value.replace(pattern, "credential"), sanitizePublicDiscoveryText(
|
|
76906
|
+
function sanitizeAvailabilityText(text3) {
|
|
76907
|
+
return secretValuePatterns.reduce((value, pattern) => value.replace(pattern, "credential"), sanitizePublicDiscoveryText(text3).replace(/\b[A-Z0-9_]*(?:API_KEY|SECRET|TOKEN|CREDENTIAL)[A-Z0-9_]*\b/g, "credential")).replace(/\s{2,}/g, " ").trim();
|
|
76787
76908
|
}
|
|
76788
76909
|
function parseRemoteRegistryPayload(payload) {
|
|
76789
76910
|
const parsed = parseRemoteContract(remoteRegistrySchema, payload, "Remote registry payload did not match the expected skills contract");
|
|
@@ -76988,11 +77109,11 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
76988
77109
|
throw new Error("Invalid canonical JSON depth bound");
|
|
76989
77110
|
let bytes = 0;
|
|
76990
77111
|
const active = new Set;
|
|
76991
|
-
const piece = (
|
|
76992
|
-
bytes += Buffer.byteLength(
|
|
77112
|
+
const piece = (text3) => {
|
|
77113
|
+
bytes += Buffer.byteLength(text3);
|
|
76993
77114
|
if (bytes > maximumBytes)
|
|
76994
77115
|
throw new Error("Canonical JSON exceeds its bound");
|
|
76995
|
-
return
|
|
77116
|
+
return text3;
|
|
76996
77117
|
};
|
|
76997
77118
|
const visit3 = (v, depth) => {
|
|
76998
77119
|
if (depth > maximumDepth)
|
|
@@ -77131,9 +77252,9 @@ function requireResult(value) {
|
|
|
77131
77252
|
if (!value)
|
|
77132
77253
|
throw new RemoteRecurringReadError;
|
|
77133
77254
|
}
|
|
77134
|
-
function termsIntegrity(terms,
|
|
77255
|
+
function termsIntegrity(terms, hash4, identity4, own2) {
|
|
77135
77256
|
requireResult(terms.organizationId === identity4.organization.id && (!own2 || terms.approvedByUserId === identity4.user.id && terms.approvedMembershipId === identity4.user.membershipId));
|
|
77136
|
-
requireResult(canonicalJsonSha256(terms) ===
|
|
77257
|
+
requireResult(canonicalJsonSha256(terms) === hash4);
|
|
77137
77258
|
requireResult(canonicalJsonSha256({ skill: terms.skill, input: terms.input, args: terms.args, runtime: terms.runtime }) === terms.payloadSha256);
|
|
77138
77259
|
requireResult(Date.parse(terms.expiresAt) > Date.parse(terms.startsAt));
|
|
77139
77260
|
}
|
|
@@ -77529,7 +77650,7 @@ function invalid3() {
|
|
|
77529
77650
|
throw new Error(invalidWorkspaceResult);
|
|
77530
77651
|
}
|
|
77531
77652
|
function organization(v) {
|
|
77532
|
-
if (!record4(v) || !uuid8(v.id) || !
|
|
77653
|
+
if (!record4(v) || !uuid8(v.id) || !text3(v.slug) || !text3(v.name))
|
|
77533
77654
|
return invalid3();
|
|
77534
77655
|
return { id: v.id, slug: v.slug, name: v.name };
|
|
77535
77656
|
}
|
|
@@ -77549,7 +77670,7 @@ function parseWorkspaceIdentity(value, expectedUserId) {
|
|
|
77549
77670
|
if (!record4(value))
|
|
77550
77671
|
return invalid3();
|
|
77551
77672
|
const user = value.user;
|
|
77552
|
-
if (!record4(user) || !uuid8(user.id) || !uuid8(user.membershipId) || !
|
|
77673
|
+
if (!record4(user) || !uuid8(user.id) || !uuid8(user.membershipId) || !text3(user.email, 320) || !(user.displayName === null || text3(user.displayName)) || !role2(user.role))
|
|
77553
77674
|
return invalid3();
|
|
77554
77675
|
if (user.id !== expectedUserId)
|
|
77555
77676
|
throw new WorkspaceIdentityMismatchError;
|
|
@@ -77580,7 +77701,7 @@ function workspaceSelectionFailure(value, status) {
|
|
|
77580
77701
|
const code = value.code;
|
|
77581
77702
|
return workspaceSelectionFailures[code][0] === status ? code : null;
|
|
77582
77703
|
}
|
|
77583
|
-
var record4 = (v) => !!v && typeof v === "object" && !Array.isArray(v), uuid8 = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v),
|
|
77704
|
+
var record4 = (v) => !!v && typeof v === "object" && !Array.isArray(v), uuid8 = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), text3 = (v, max2 = 1024) => typeof v === "string" && !!v.trim() && v.length <= max2 && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(v), role2 = (v) => typeof v === "string" && ["owner", "admin", "member", "viewer"].includes(v), invalidWorkspaceResult = "The server returned an invalid workspace selection result.", WorkspaceContextInputError, WorkspaceIdentityMismatchError, workspaceSelectionFailures;
|
|
77584
77705
|
var init_remote_workspace_selection = __esm(() => {
|
|
77585
77706
|
WorkspaceContextInputError = class WorkspaceContextInputError extends Error {
|
|
77586
77707
|
constructor() {
|
|
@@ -80394,17 +80515,17 @@ var init_install = __esm(() => {
|
|
|
80394
80515
|
|
|
80395
80516
|
// src/lib/compact-output.ts
|
|
80396
80517
|
function truncateText(value, maxChars = 96) {
|
|
80397
|
-
const
|
|
80398
|
-
if (
|
|
80399
|
-
return
|
|
80400
|
-
return `${
|
|
80518
|
+
const text4 = String(value ?? "").replace(/\s+/g, " ").trim();
|
|
80519
|
+
if (text4.length <= maxChars)
|
|
80520
|
+
return text4;
|
|
80521
|
+
return `${text4.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
80401
80522
|
}
|
|
80402
80523
|
function previewText(value, maxChars = DEFAULT_PREVIEW_CHARS) {
|
|
80403
|
-
const
|
|
80524
|
+
const text4 = String(value ?? "");
|
|
80404
80525
|
return {
|
|
80405
|
-
text:
|
|
80406
|
-
length:
|
|
80407
|
-
truncated:
|
|
80526
|
+
text: text4.length > maxChars ? `${text4.slice(0, Math.max(0, maxChars - 3))}...` : text4,
|
|
80527
|
+
length: text4.length,
|
|
80528
|
+
truncated: text4.length > maxChars
|
|
80408
80529
|
};
|
|
80409
80530
|
}
|
|
80410
80531
|
function parsePageLimit(value, fallback, options = {}) {
|
|
@@ -80523,10 +80644,10 @@ function handleBrowseError(error61) {
|
|
|
80523
80644
|
process.exitCode = 1;
|
|
80524
80645
|
}
|
|
80525
80646
|
async function writeJson(value, space) {
|
|
80526
|
-
const
|
|
80647
|
+
const text4 = `${JSON.stringify(value, null, space)}
|
|
80527
80648
|
`;
|
|
80528
80649
|
await new Promise((resolve19, reject2) => {
|
|
80529
|
-
process.stdout.write(
|
|
80650
|
+
process.stdout.write(text4, (error61) => {
|
|
80530
80651
|
if (error61)
|
|
80531
80652
|
reject2(error61);
|
|
80532
80653
|
else
|
|
@@ -81085,7 +81206,7 @@ function handleDiff(name, options) {
|
|
|
81085
81206
|
const present = existsSync22(dir);
|
|
81086
81207
|
const managed = existsSync22(join37(dir, SYNC_MARKER_FILE));
|
|
81087
81208
|
const skillMdPath = join37(dir, "SKILL.md");
|
|
81088
|
-
const
|
|
81209
|
+
const hash4 = present && existsSync22(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : undefined;
|
|
81089
81210
|
let stub;
|
|
81090
81211
|
if (present && existsSync22(skillMdPath)) {
|
|
81091
81212
|
try {
|
|
@@ -81094,13 +81215,13 @@ function handleDiff(name, options) {
|
|
|
81094
81215
|
stub = undefined;
|
|
81095
81216
|
}
|
|
81096
81217
|
}
|
|
81097
|
-
const diverged = present && canonical4.present && managed &&
|
|
81218
|
+
const diverged = present && canonical4.present && managed && hash4 !== canonical4.hash;
|
|
81098
81219
|
homes.push({
|
|
81099
81220
|
agent,
|
|
81100
81221
|
path: dir,
|
|
81101
81222
|
present,
|
|
81102
81223
|
managed,
|
|
81103
|
-
...
|
|
81224
|
+
...hash4 ? { hash: hash4 } : {},
|
|
81104
81225
|
...stub !== undefined ? { stub } : {},
|
|
81105
81226
|
...diverged !== undefined ? { diverged } : {}
|
|
81106
81227
|
});
|
|
@@ -81956,13 +82077,13 @@ function scanUnmarkedHomes(options = {}) {
|
|
|
81956
82077
|
const skillMdPath = join39(dir, "SKILL.md");
|
|
81957
82078
|
if (!existsSync24(skillMdPath))
|
|
81958
82079
|
continue;
|
|
81959
|
-
const
|
|
82080
|
+
const hash4 = hashSkillMarkdownFile(skillMdPath);
|
|
81960
82081
|
const mtime = statSync12(skillMdPath).mtime.toISOString();
|
|
81961
|
-
const entry = { agent, skill, home, path: dir, hash:
|
|
82082
|
+
const entry = { agent, skill, home, path: dir, hash: hash4, mtime };
|
|
81962
82083
|
const canonicalHash = index.get(skill);
|
|
81963
82084
|
if (canonicalHash === undefined) {
|
|
81964
82085
|
scan.unknown.push({ ...entry });
|
|
81965
|
-
} else if (canonicalHash ===
|
|
82086
|
+
} else if (canonicalHash === hash4) {
|
|
81966
82087
|
scan.adoptable.push(entry);
|
|
81967
82088
|
} else {
|
|
81968
82089
|
scan.conflicts.push({ ...entry, canonicalHash });
|
|
@@ -82050,10 +82171,10 @@ function pruneOwnership(dir) {
|
|
|
82050
82171
|
const after2 = fstatSync15(descriptor), current = lstatSync22(dir), markerPathAfter = lstatSync22(markerPath);
|
|
82051
82172
|
if (!markerPathAfter.isFile() || markerPathAfter.dev !== before2.dev || markerPathAfter.ino !== before2.ino || markerPathAfter.size !== before2.size || markerPathAfter.mtimeMs !== before2.mtimeMs || markerPathAfter.ctimeMs !== before2.ctimeMs || length !== before2.size || after2.size !== before2.size || after2.mtimeMs !== before2.mtimeMs || after2.ctimeMs !== before2.ctimeMs || !current.isDirectory() || current.dev !== directory.dev || current.ino !== directory.ino)
|
|
82052
82173
|
return;
|
|
82053
|
-
const
|
|
82174
|
+
const text4 = bytes.subarray(0, length).toString("utf8"), marker = JSON.parse(text4);
|
|
82054
82175
|
if (!isSkillsOwnershipMarker(marker))
|
|
82055
82176
|
return;
|
|
82056
|
-
return { marker, identity: JSON.stringify([directory.dev, directory.ino, before2.dev, before2.ino, before2.mtimeMs, before2.ctimeMs,
|
|
82177
|
+
return { marker, identity: JSON.stringify([directory.dev, directory.ino, before2.dev, before2.ino, before2.mtimeMs, before2.ctimeMs, text4]) };
|
|
82057
82178
|
} catch {
|
|
82058
82179
|
return;
|
|
82059
82180
|
} finally {
|
|
@@ -82099,15 +82220,15 @@ function pruneStrayHomes(options = {}) {
|
|
|
82099
82220
|
const marker = owned.marker;
|
|
82100
82221
|
ownership.set(dir, owned.identity);
|
|
82101
82222
|
const skillMdPath = join39(dir, "SKILL.md");
|
|
82102
|
-
const
|
|
82103
|
-
candidates.push({ agent, skill, home, path: dir, hash:
|
|
82223
|
+
const hash4 = existsSync24(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : "";
|
|
82224
|
+
candidates.push({ agent, skill, home, path: dir, hash: hash4, marker });
|
|
82104
82225
|
}
|
|
82105
82226
|
}
|
|
82106
82227
|
if (!options.apply) {
|
|
82107
82228
|
return { candidates, pruned: 0, dryRun: true };
|
|
82108
82229
|
}
|
|
82109
82230
|
const appDir = options.homeDir ? join39(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
82110
|
-
const rollbackFile = writeRollbackRecord("prune", candidates.map(({ agent, skill, path, hash:
|
|
82231
|
+
const rollbackFile = writeRollbackRecord("prune", candidates.map(({ agent, skill, path, hash: hash4, marker }) => ({ agent, skill, path, hash: hash4, marker })), appDir);
|
|
82111
82232
|
let pruned = 0;
|
|
82112
82233
|
for (const candidate2 of candidates) {
|
|
82113
82234
|
if (pruneOwnership(candidate2.path)?.identity !== ownership.get(candidate2.path))
|
|
@@ -82338,10 +82459,10 @@ function setEnvAssignment(path, assignment) {
|
|
|
82338
82459
|
}
|
|
82339
82460
|
let prepared = initial2;
|
|
82340
82461
|
if (previous) {
|
|
82341
|
-
const original = readFileSync27(descriptor),
|
|
82342
|
-
if (!Buffer.from(
|
|
82462
|
+
const original = readFileSync27(descriptor), text4 = original.toString("utf8");
|
|
82463
|
+
if (!Buffer.from(text4, "utf8").equals(original))
|
|
82343
82464
|
throw new EnvAssignmentError(INVALID_LAYOUT);
|
|
82344
|
-
prepared = prepareEnvAssignment(assignment,
|
|
82465
|
+
prepared = prepareEnvAssignment(assignment, text4);
|
|
82345
82466
|
}
|
|
82346
82467
|
const bytes = Buffer.from(prepared.content, "utf8");
|
|
82347
82468
|
let written = 0;
|
|
@@ -82881,7 +83002,7 @@ import { closeSync as closeSync17, fstatSync as fstatSync17, openSync as openSyn
|
|
|
82881
83002
|
import { O_NOFOLLOW as O_NOFOLLOW3, O_NONBLOCK as O_NONBLOCK3, O_RDONLY as O_RDONLY3 } from "constants";
|
|
82882
83003
|
import { createRequire as createRequire2 } from "module";
|
|
82883
83004
|
import { hostname as osHostname2 } from "os";
|
|
82884
|
-
import { isAbsolute as
|
|
83005
|
+
import { isAbsolute as isAbsolute17, join as join42 } from "path";
|
|
82885
83006
|
import { createRequire as createRequire22 } from "module";
|
|
82886
83007
|
function envToken2(name) {
|
|
82887
83008
|
return name.toUpperCase().replace(/-/g, "_");
|
|
@@ -82905,7 +83026,7 @@ function homeDir2(env3) {
|
|
|
82905
83026
|
}
|
|
82906
83027
|
function absoluteOverride2(env3, key) {
|
|
82907
83028
|
const value = env3[key]?.trim();
|
|
82908
|
-
return value &&
|
|
83029
|
+
return value && isAbsolute17(value) ? value : null;
|
|
82909
83030
|
}
|
|
82910
83031
|
function hasnaHomeDir2(env3) {
|
|
82911
83032
|
const override = absoluteOverride2(env3, HASNA_HOME_ENV_KEY2);
|
|
@@ -82936,10 +83057,10 @@ function credentialDiskSources2(name, env3) {
|
|
|
82936
83057
|
function profileDiskSources2(name, env3, profile) {
|
|
82937
83058
|
return credentialDiskSourceList2(name, env3, profile).map((s) => s.path);
|
|
82938
83059
|
}
|
|
82939
|
-
function parseEnvFile2(
|
|
83060
|
+
function parseEnvFile2(text4) {
|
|
82940
83061
|
const values2 = new Map;
|
|
82941
83062
|
const unusable = new Set;
|
|
82942
|
-
for (const rawLine of
|
|
83063
|
+
for (const rawLine of text4.split(/\r?\n/)) {
|
|
82943
83064
|
const line = rawLine.trim();
|
|
82944
83065
|
if (line.length === 0 || line.startsWith("#"))
|
|
82945
83066
|
continue;
|
|
@@ -83179,8 +83300,8 @@ function keychainAccount2(env3, options) {
|
|
|
83179
83300
|
const user = env3.USER?.trim();
|
|
83180
83301
|
return user || null;
|
|
83181
83302
|
}
|
|
83182
|
-
function keychainFailureHint2(
|
|
83183
|
-
const line =
|
|
83303
|
+
function keychainFailureHint2(text4) {
|
|
83304
|
+
const line = text4.split(/\r?\n/).find((entry) => entry.trim().length > 0)?.trim() ?? "";
|
|
83184
83305
|
const clean = line.replace(/[\u0000-\u001f\u007f]/g, "").slice(0, 200);
|
|
83185
83306
|
return clean ? `: ${clean}` : "";
|
|
83186
83307
|
}
|
|
@@ -83758,12 +83879,12 @@ function createHasnaHttpTransportInternal(options, requestBindingProvider) {
|
|
|
83758
83879
|
await response.body?.cancel();
|
|
83759
83880
|
} catch {}
|
|
83760
83881
|
} else {
|
|
83761
|
-
const
|
|
83762
|
-
if (
|
|
83882
|
+
const text4 = await response.text();
|
|
83883
|
+
if (text4.length > 0) {
|
|
83763
83884
|
try {
|
|
83764
|
-
parsed = JSON.parse(
|
|
83885
|
+
parsed = JSON.parse(text4);
|
|
83765
83886
|
} catch {
|
|
83766
|
-
parsed =
|
|
83887
|
+
parsed = text4;
|
|
83767
83888
|
}
|
|
83768
83889
|
}
|
|
83769
83890
|
}
|
|
@@ -83866,13 +83987,13 @@ class SecretsClient {
|
|
|
83866
83987
|
headers,
|
|
83867
83988
|
redirect: "manual"
|
|
83868
83989
|
});
|
|
83869
|
-
const
|
|
83990
|
+
const text4 = await response.text();
|
|
83870
83991
|
let body = undefined;
|
|
83871
|
-
if (
|
|
83992
|
+
if (text4) {
|
|
83872
83993
|
try {
|
|
83873
|
-
body = JSON.parse(
|
|
83994
|
+
body = JSON.parse(text4);
|
|
83874
83995
|
} catch {
|
|
83875
|
-
body =
|
|
83996
|
+
body = text4;
|
|
83876
83997
|
}
|
|
83877
83998
|
}
|
|
83878
83999
|
if (!response.ok) {
|
|
@@ -84201,7 +84322,7 @@ function refuse(code, message) {
|
|
|
84201
84322
|
function record7(value) {
|
|
84202
84323
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
84203
84324
|
}
|
|
84204
|
-
function
|
|
84325
|
+
function exactKeys3(value, keys2) {
|
|
84205
84326
|
return Object.keys(value).length === keys2.length && keys2.every((key) => Object.hasOwn(value, key));
|
|
84206
84327
|
}
|
|
84207
84328
|
function identifier2(value) {
|
|
@@ -84241,7 +84362,7 @@ function readSelectedSecretBindings(path) {
|
|
|
84241
84362
|
}
|
|
84242
84363
|
}
|
|
84243
84364
|
function validateSelectedSecretBindings(value, scope, names) {
|
|
84244
|
-
if (!record7(value) || !
|
|
84365
|
+
if (!record7(value) || !exactKeys3(value, ["schema", "selection", "consumer", "secretsAuthority", "bindings"]) || value.schema !== "hasna.skills-secret-bindings.v1" || !record7(value.selection) || !exactKeys3(value.selection, ["authority", "workspaceId", "profileId", "profileRevision", "slug", "version", "bundleDigest"]) || !record7(value.consumer) || !exactKeys3(value.consumer, ["stationId", "workspaceDirectory"]) || !record7(value.bindings)) {
|
|
84245
84366
|
return refuse("INVALID_SECRET_BINDINGS", "Secret bindings do not match the hasna.skills-secret-bindings.v1 contract.");
|
|
84246
84367
|
}
|
|
84247
84368
|
for (const [key, expected] of Object.entries({ authority: scope.selection.authority, workspaceId: scope.selection.workspaceId, profileId: scope.profileId, profileRevision: scope.selection.profileRevision, slug: scope.selection.slug, version: scope.selection.version, bundleDigest: scope.selection.bundleDigest })) {
|
|
@@ -84266,7 +84387,7 @@ function validateSelectedSecretBindings(value, scope, names) {
|
|
|
84266
84387
|
} catch {
|
|
84267
84388
|
return refuse("INVALID_SECRET_BINDINGS", "Secret bindings require a canonical HTTPS Secrets /v1 authority (loopback HTTP is allowed).");
|
|
84268
84389
|
}
|
|
84269
|
-
if (!
|
|
84390
|
+
if (!exactKeys3(value.bindings, names) || Object.values(value.bindings).some((key) => !identifier2(key) || !/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(key) || key.split("/").some((part) => !part || part === "." || part === ".."))) {
|
|
84270
84391
|
return refuse("INVALID_SECRET_BINDINGS", "Bind every declared environment name exactly once to a vault key; undeclared bindings and values are refused.");
|
|
84271
84392
|
}
|
|
84272
84393
|
return structuredClone(value);
|
|
@@ -84296,7 +84417,7 @@ async function resolveSelectedSecrets(bindings, createClient2 = createSecretsCli
|
|
|
84296
84417
|
return refuse("LOCAL_SECRET_UNAVAILABLE", "A declared secret could not be resolved, was invalid, or has expired. The skill was not started; no fallback was attempted.");
|
|
84297
84418
|
}
|
|
84298
84419
|
}
|
|
84299
|
-
function redactExecutionSecrets(
|
|
84420
|
+
function redactExecutionSecrets(text4, env3) {
|
|
84300
84421
|
const values2 = new Set(Object.values(env3).flatMap((value) => {
|
|
84301
84422
|
let encoded = "";
|
|
84302
84423
|
try {
|
|
@@ -84305,8 +84426,8 @@ function redactExecutionSecrets(text3, env3) {
|
|
|
84305
84426
|
return [value, JSON.stringify(value).slice(1, -1), Buffer.from(value).toString("base64"), encoded];
|
|
84306
84427
|
}));
|
|
84307
84428
|
for (const value of [...values2].filter(Boolean).sort((a, b) => b.length - a.length))
|
|
84308
|
-
|
|
84309
|
-
return
|
|
84429
|
+
text4 = text4.split(value).join("[REDACTED]");
|
|
84430
|
+
return text4;
|
|
84310
84431
|
}
|
|
84311
84432
|
var init_execution_secrets = __esm(() => {
|
|
84312
84433
|
init_sdk();
|
|
@@ -84444,7 +84565,7 @@ var init_execution_grants = __esm(() => {
|
|
|
84444
84565
|
});
|
|
84445
84566
|
|
|
84446
84567
|
// src/lib/execution-grant-client.ts
|
|
84447
|
-
import { isDeepStrictEqual as
|
|
84568
|
+
import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
|
|
84448
84569
|
function id(value) {
|
|
84449
84570
|
if (!grantIdentifier(value))
|
|
84450
84571
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -84496,7 +84617,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
|
|
|
84496
84617
|
headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
|
|
84497
84618
|
body: JSON.stringify({ grants: snapshot2 })
|
|
84498
84619
|
}));
|
|
84499
|
-
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !
|
|
84620
|
+
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual8(policy.grants, snapshot2)) {
|
|
84500
84621
|
throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
|
|
84501
84622
|
}
|
|
84502
84623
|
return policy;
|
|
@@ -84507,7 +84628,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
84507
84628
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
84508
84629
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
84509
84630
|
}
|
|
84510
|
-
if (!
|
|
84631
|
+
if (!isDeepStrictEqual8(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual8(value.bindings.consumer, snapshot2.consumer)) {
|
|
84511
84632
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
84512
84633
|
}
|
|
84513
84634
|
return value;
|
|
@@ -93602,10 +93723,10 @@ var require_util = __commonJS((exports) => {
|
|
|
93602
93723
|
var codegen_1 = require_codegen();
|
|
93603
93724
|
var code_1 = require_code();
|
|
93604
93725
|
function toHash(arr) {
|
|
93605
|
-
const
|
|
93726
|
+
const hash4 = {};
|
|
93606
93727
|
for (const item of arr)
|
|
93607
|
-
|
|
93608
|
-
return
|
|
93728
|
+
hash4[item] = true;
|
|
93729
|
+
return hash4;
|
|
93609
93730
|
}
|
|
93610
93731
|
exports.toHash = toHash;
|
|
93611
93732
|
function alwaysValidSchema(it, schema) {
|
|
@@ -97067,7 +97188,7 @@ var require_core = __commonJS((exports) => {
|
|
|
97067
97188
|
errorsText(errors5 = this.errors, { separator = ", ", dataVar = "data" } = {}) {
|
|
97068
97189
|
if (!errors5 || errors5.length === 0)
|
|
97069
97190
|
return "No errors";
|
|
97070
|
-
return errors5.map((e2) => `${dataVar}${e2.instancePath} ${e2.message}`).reduce((
|
|
97191
|
+
return errors5.map((e2) => `${dataVar}${e2.instancePath} ${e2.message}`).reduce((text4, msg) => text4 + separator + msg);
|
|
97071
97192
|
}
|
|
97072
97193
|
$dataMetaSchema(metaSchema, keywordsJsonPointers) {
|
|
97073
97194
|
const rules = this.RULES.all;
|
|
@@ -101145,11 +101266,11 @@ var init_remote_invitation_recovery = __esm(() => {
|
|
|
101145
101266
|
// src/lib/remote-private-publications.ts
|
|
101146
101267
|
import { createHash as createHash23 } from "crypto";
|
|
101147
101268
|
function checkedPublicationDeclaration(value) {
|
|
101148
|
-
if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !
|
|
101269
|
+
if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash4(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
|
|
101149
101270
|
return bad();
|
|
101150
101271
|
try {
|
|
101151
101272
|
const manifest = JSON.parse(value.manifestText);
|
|
101152
|
-
if (!record10(manifest) || manifest.version !== value.version || validatePortableManifestContract(manifest, { strict: true }).length || !
|
|
101273
|
+
if (!record10(manifest) || manifest.version !== value.version || validatePortableManifestContract(manifest, { strict: true }).length || !hash4(manifest.provenance?.content_hash))
|
|
101153
101274
|
return bad();
|
|
101154
101275
|
} catch {
|
|
101155
101276
|
return bad();
|
|
@@ -101157,7 +101278,7 @@ function checkedPublicationDeclaration(value) {
|
|
|
101157
101278
|
return Object.freeze({ ...value });
|
|
101158
101279
|
}
|
|
101159
101280
|
function checkedPublicationView(value, skillId, intentId, declaration) {
|
|
101160
|
-
if (!exact(value, ["id", "skillId", "version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize", "state", "expiresAt", "createdAt", "versionId"]) || !publicationUuid(value.id) || value.skillId !== skillId || intentId !== undefined && value.id !== intentId || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || !
|
|
101281
|
+
if (!exact(value, ["id", "skillId", "version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize", "state", "expiresAt", "createdAt", "versionId"]) || !publicationUuid(value.id) || value.skillId !== skillId || intentId !== undefined && value.id !== intentId || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || !hash4(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES || typeof value.state !== "string" || !["awaiting_upload", "queued", "verifying", "needs_attention", "committed", "rejected", "cancelled", "expired"].includes(value.state) || !date6(value.expiresAt) || !date6(value.createdAt) || !(value.versionId === null || publicationUuid(value.versionId)) || value.state === "committed" && value.versionId === null)
|
|
101161
101282
|
return invalid5();
|
|
101162
101283
|
if (declaration && ["version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize"].some((k) => value[k] !== declaration[k]))
|
|
101163
101284
|
return invalid5();
|
|
@@ -101404,7 +101525,7 @@ var PRIVATE_PUBLICATION_MAX_BYTES, failures2, PrivatePublicationError, bad = ()
|
|
|
101404
101525
|
throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
|
|
101405
101526
|
}, invalid5 = () => {
|
|
101406
101527
|
throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
|
|
101407
|
-
}, publicationUuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v),
|
|
101528
|
+
}, publicationUuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), hash4 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), record10 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v, keys3) => record10(v) && Object.keys(v).sort().join(",") === keys3.sort().join(","), date6 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v)), publicationSha256 = (bytes) => createHash23("sha256").update(bytes).digest("hex");
|
|
101408
101529
|
var init_remote_private_publications = __esm(() => {
|
|
101409
101530
|
init_fleet_credentials();
|
|
101410
101531
|
init_remote_workspace_selection();
|
|
@@ -101465,8 +101586,8 @@ async function requestAuthApi(instance, path, options) {
|
|
|
101465
101586
|
apiUrl: safeUrl
|
|
101466
101587
|
});
|
|
101467
101588
|
}
|
|
101468
|
-
const
|
|
101469
|
-
const body =
|
|
101589
|
+
const text4 = await res.text();
|
|
101590
|
+
const body = text4 ? parseJsonBody(text4) : {};
|
|
101470
101591
|
if (!res.ok) {
|
|
101471
101592
|
const record11 = isRecord5(body) ? body : {};
|
|
101472
101593
|
const detail = typeof record11.detail === "string" ? record11.detail : undefined;
|
|
@@ -101482,15 +101603,15 @@ async function requestAuthApi(instance, path, options) {
|
|
|
101482
101603
|
}
|
|
101483
101604
|
return body;
|
|
101484
101605
|
}
|
|
101485
|
-
function parseJsonBody(
|
|
101606
|
+
function parseJsonBody(text4) {
|
|
101486
101607
|
try {
|
|
101487
|
-
return JSON.parse(
|
|
101608
|
+
return JSON.parse(text4);
|
|
101488
101609
|
} catch {
|
|
101489
|
-
return { detail: condenseErrorBody(
|
|
101610
|
+
return { detail: condenseErrorBody(text4) };
|
|
101490
101611
|
}
|
|
101491
101612
|
}
|
|
101492
|
-
function condenseErrorBody(
|
|
101493
|
-
const stripped = /<[a-z!/]/i.test(
|
|
101613
|
+
function condenseErrorBody(text4) {
|
|
101614
|
+
const stripped = /<[a-z!/]/i.test(text4) ? text4.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text4;
|
|
101494
101615
|
const collapsed = stripped.replace(/\s+/g, " ").trim();
|
|
101495
101616
|
if (collapsed.length <= MAX_ERROR_DETAIL_LENGTH)
|
|
101496
101617
|
return collapsed;
|
|
@@ -101706,12 +101827,12 @@ function safeText(file2) {
|
|
|
101706
101827
|
}
|
|
101707
101828
|
}
|
|
101708
101829
|
function checkIdentityMetadata(file2, identity4) {
|
|
101709
|
-
const
|
|
101710
|
-
if (
|
|
101830
|
+
const text4 = safeText(file2);
|
|
101831
|
+
if (text4 === null)
|
|
101711
101832
|
return;
|
|
101712
101833
|
let value;
|
|
101713
101834
|
try {
|
|
101714
|
-
value = JSON.parse(
|
|
101835
|
+
value = JSON.parse(text4);
|
|
101715
101836
|
} catch {
|
|
101716
101837
|
return fail3("The profile identity metadata is invalid. Sign in again before managing this workspace.");
|
|
101717
101838
|
}
|
|
@@ -102050,7 +102171,7 @@ var init_recurring_customer = __esm(() => {
|
|
|
102050
102171
|
|
|
102051
102172
|
// src/lib/recurring-recovery.ts
|
|
102052
102173
|
import { constants as constants17, closeSync as closeSync20, fsyncSync as fsyncSync5, fstatSync as fstatSync20, lstatSync as lstatSync25, mkdirSync as mkdirSync17, openSync as openSync20, readSync as readSync14, realpathSync as realpathSync9, renameSync as renameSync10, unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "fs";
|
|
102053
|
-
import { dirname as dirname23, isAbsolute as
|
|
102174
|
+
import { dirname as dirname23, isAbsolute as isAbsolute18, join as join46, resolve as resolve20 } from "path";
|
|
102054
102175
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
102055
102176
|
|
|
102056
102177
|
class RecoveryOwner {
|
|
@@ -102058,7 +102179,7 @@ class RecoveryOwner {
|
|
|
102058
102179
|
parents = [];
|
|
102059
102180
|
constructor(directory) {
|
|
102060
102181
|
this.directory = directory;
|
|
102061
|
-
if (!
|
|
102182
|
+
if (!isAbsolute18(directory) || resolve20(directory) !== directory || realpathSync9(directory) !== directory)
|
|
102062
102183
|
invalid6();
|
|
102063
102184
|
for (let path = directory;; path = dirname23(path)) {
|
|
102064
102185
|
const stat3 = lstatSync25(path);
|
|
@@ -102193,7 +102314,7 @@ function base2(customer, organizationId) {
|
|
|
102193
102314
|
};
|
|
102194
102315
|
}
|
|
102195
102316
|
function create3(directory, intent) {
|
|
102196
|
-
if (!
|
|
102317
|
+
if (!isAbsolute18(directory) || resolve20(directory) !== directory || realpathSync9(dirname23(directory)) !== dirname23(directory))
|
|
102197
102318
|
invalid6();
|
|
102198
102319
|
mkdirSync17(directory, { mode: 448 });
|
|
102199
102320
|
const owner2 = new RecoveryOwner(directory), checked = intentSchema.parse(intent), intentSha256 = canonicalJsonSha256(checked);
|
|
@@ -102356,12 +102477,12 @@ var init_recurring_recovery = __esm(() => {
|
|
|
102356
102477
|
});
|
|
102357
102478
|
|
|
102358
102479
|
// src/lib/recurring-surface.ts
|
|
102359
|
-
import { isAbsolute as
|
|
102480
|
+
import { isAbsolute as isAbsolute19 } from "path";
|
|
102360
102481
|
function recurringMcpSchema(action) {
|
|
102361
102482
|
return action === "activate" ? recurringSurfaceSchemas.activate.extend({ code, confirm: exports_external.literal(true) }) : recurringSurfaceSchemas[action];
|
|
102362
102483
|
}
|
|
102363
|
-
function expectedDraft(draft,
|
|
102364
|
-
if (!draft || draft.termsSha256 !==
|
|
102484
|
+
function expectedDraft(draft, hash5) {
|
|
102485
|
+
if (!draft || draft.termsSha256 !== hash5)
|
|
102365
102486
|
throw new RemoteRecurringError("RECURRING_TERMS_UNAVAILABLE");
|
|
102366
102487
|
return draft;
|
|
102367
102488
|
}
|
|
@@ -102461,7 +102582,7 @@ var init_recurring_surface = __esm(() => {
|
|
|
102461
102582
|
uuid13 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
102462
102583
|
context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
|
|
102463
102584
|
page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
|
|
102464
|
-
directory = exports_external.string().max(4096).refine(
|
|
102585
|
+
directory = exports_external.string().max(4096).refine(isAbsolute19, "Use an absolute recovery directory");
|
|
102465
102586
|
email4 = exports_external.string().trim().toLowerCase().email().max(254);
|
|
102466
102587
|
code = exports_external.string().regex(/^\d{6}$/);
|
|
102467
102588
|
recurringSurfaceSchemas = {
|
|
@@ -105263,10 +105384,10 @@ var init_private_publication_customer = __esm(() => {
|
|
|
105263
105384
|
|
|
105264
105385
|
// src/lib/private-publication-recovery.ts
|
|
105265
105386
|
import { constants as constants18, closeSync as closeSync21, fsyncSync as fsyncSync6, fstatSync as fstatSync21, lstatSync as lstatSync26, mkdirSync as mkdirSync21, openSync as openSync21, readSync as readSync15, realpathSync as realpathSync10, renameSync as renameSync11, unlinkSync as unlinkSync7, writeFileSync as writeFileSync20 } from "fs";
|
|
105266
|
-
import { dirname as dirname26, isAbsolute as
|
|
105387
|
+
import { dirname as dirname26, isAbsolute as isAbsolute20, join as join51, resolve as resolve21 } from "path";
|
|
105267
105388
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
105268
105389
|
function safeDirectory(directory2) {
|
|
105269
|
-
if (!
|
|
105390
|
+
if (!isAbsolute20(directory2) || directory2 !== resolve21(directory2) || realpathSync10(directory2) !== directory2)
|
|
105270
105391
|
return fail4();
|
|
105271
105392
|
for (let path = directory2;; path = dirname26(path)) {
|
|
105272
105393
|
const stat3 = lstatSync26(path);
|
|
@@ -105404,7 +105525,7 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
|
|
|
105404
105525
|
phase: "prepared",
|
|
105405
105526
|
intent: null
|
|
105406
105527
|
};
|
|
105407
|
-
if (!
|
|
105528
|
+
if (!isAbsolute20(recoveryDirectory) || resolve21(recoveryDirectory) !== recoveryDirectory || realpathSync10(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
|
|
105408
105529
|
return fail4();
|
|
105409
105530
|
mkdirSync21(recoveryDirectory, { mode: 448 });
|
|
105410
105531
|
safeDirectory(recoveryDirectory);
|
|
@@ -105505,10 +105626,10 @@ var init_private_publication_recovery = __esm(() => {
|
|
|
105505
105626
|
});
|
|
105506
105627
|
|
|
105507
105628
|
// src/mcp/private-publication-tools.ts
|
|
105508
|
-
import { isAbsolute as
|
|
105629
|
+
import { isAbsolute as isAbsolute21 } from "path";
|
|
105509
105630
|
function registerPrivatePublicationTools(server) {
|
|
105510
105631
|
const uuid9 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
105511
|
-
const directory2 = exports_external.string().max(4096).refine(
|
|
105632
|
+
const directory2 = exports_external.string().max(4096).refine(isAbsolute21, "Use an absolute local directory");
|
|
105512
105633
|
const verification2 = { email: exports_external.string().email().max(254), code: exports_external.string().regex(/^\d{6}$/), userId: uuid9, membershipId: uuid9, recoveryDirectory: directory2 };
|
|
105513
105634
|
const description = "Manage a private source publication using fresh workspace-bound verification and a host-local recovery directory. Publishing requires explicit UUID current-version comparison and upload consent. Resume reuses immutable saved bytes and intent; an uncertain PUT is finalized for server verification, never uploaded twice. Sessions and signed URLs are not returned or persisted. Private execution is unavailable. Host request history may contain the supplied verification code.";
|
|
105514
105635
|
const handler = (action) => async (value) => {
|
|
@@ -106154,7 +106275,7 @@ __export(exports_runtime, {
|
|
|
106154
106275
|
});
|
|
106155
106276
|
import { createHash as createHash25, randomUUID as randomUUID9 } from "crypto";
|
|
106156
106277
|
import { lstatSync as lstatSync27, mkdirSync as mkdirSync23, readFileSync as readFileSync35, realpathSync as realpathSync11, writeFileSync as writeFileSync22 } from "fs";
|
|
106157
|
-
import { basename as basename12, isAbsolute as
|
|
106278
|
+
import { basename as basename12, isAbsolute as isAbsolute22, join as join53 } from "path";
|
|
106158
106279
|
import { createInterface } from "readline";
|
|
106159
106280
|
function registerRuntime(parent) {
|
|
106160
106281
|
parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill; use -- to end Skills options").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--remote", "Run on the configured server, using its catalog and quote", false).option("--target <target>", "Execution target: local or cloud").option("--secret-bindings <path>", "Reviewed, exact-selection local execution vault bindings (JSON file)").option("--secret-bindings-template", "Print a scoped bindings template without resolving secrets or running the skill", false).option("--input <json>", "Structured cloud execution input").option("--skill-version <version>", "Exact selected skill version").option("--selection-profile <id>", "Selected Skills profile").option("--session <id>", "Pinned Skills session").option("--cached", "Use the explicit verified selected cache", false).option("--yes", "Approve the server's quoted credit cost for this run", false).option("--idempotency-key <key>", "Reuse this key when retrying the same remote submission").option("--file <path>", "Attach a local input file to a remote run (repeatable)", (value, prior) => [...prior, value], []).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options, command) => {
|
|
@@ -106281,7 +106402,7 @@ Updating ${name}...
|
|
|
106281
106402
|
async function readUpdatedVersion(bunExecutable) {
|
|
106282
106403
|
const output2 = await readUpdateCommand([bunExecutable, "pm", "bin", "-g"]);
|
|
106283
106404
|
const globalBin = output2.replace(/\r?\n$/, "");
|
|
106284
|
-
if (!globalBin || !
|
|
106405
|
+
if (!globalBin || !isAbsolute22(globalBin) || /[\r\n\0]/.test(globalBin))
|
|
106285
106406
|
throw new Error("Update bin discovery failed");
|
|
106286
106407
|
const installed = Bun.which("skills", { PATH: globalBin, cwd: globalBin });
|
|
106287
106408
|
const selected = Bun.which("skills");
|
|
@@ -107247,13 +107368,13 @@ import { emitKeypressEvents } from "readline";
|
|
|
107247
107368
|
async function readCode() {
|
|
107248
107369
|
if (process.stdin.isTTY)
|
|
107249
107370
|
throw new NameInputError("Pipe a fresh six-digit verification code when using --code-stdin.");
|
|
107250
|
-
let
|
|
107371
|
+
let text4 = "";
|
|
107251
107372
|
for await (const chunk2 of process.stdin) {
|
|
107252
|
-
|
|
107253
|
-
if (
|
|
107373
|
+
text4 += chunk2.toString();
|
|
107374
|
+
if (text4.length > 32)
|
|
107254
107375
|
throw new NameInputError("Supply only a six-digit verification code on stdin.");
|
|
107255
107376
|
}
|
|
107256
|
-
const code2 =
|
|
107377
|
+
const code2 = text4.trim();
|
|
107257
107378
|
if (!/^\d{6}$/.test(code2))
|
|
107258
107379
|
throw new NameInputError("Supply only a six-digit verification code on stdin.");
|
|
107259
107380
|
return code2;
|
|
@@ -107281,7 +107402,7 @@ function promptCode() {
|
|
|
107281
107402
|
resolve22(answer);
|
|
107282
107403
|
};
|
|
107283
107404
|
const cancel = () => finish(null);
|
|
107284
|
-
const keypress = (
|
|
107405
|
+
const keypress = (text4, key) => {
|
|
107285
107406
|
if (key.ctrl && ["c", "d"].includes(key.name ?? "") || key.name === "escape")
|
|
107286
107407
|
return cancel();
|
|
107287
107408
|
if (key.name === "return" || key.name === "enter") {
|
|
@@ -107297,8 +107418,8 @@ Enter all six digits: `);
|
|
|
107297
107418
|
value = value.slice(0, -1);
|
|
107298
107419
|
output2.write("\b \b");
|
|
107299
107420
|
}
|
|
107300
|
-
} else if (/^[0-9]$/.test(
|
|
107301
|
-
value +=
|
|
107421
|
+
} else if (/^[0-9]$/.test(text4) && value.length < 6) {
|
|
107422
|
+
value += text4;
|
|
107302
107423
|
output2.write("*");
|
|
107303
107424
|
}
|
|
107304
107425
|
};
|
|
@@ -107780,7 +107901,7 @@ import {
|
|
|
107780
107901
|
statSync as statSync19,
|
|
107781
107902
|
writeFileSync as writeFileSync23
|
|
107782
107903
|
} from "fs";
|
|
107783
|
-
import { dirname as dirname29, isAbsolute as
|
|
107904
|
+
import { dirname as dirname29, isAbsolute as isAbsolute23, relative as relative8, resolve as resolve22, sep as sep10 } from "path";
|
|
107784
107905
|
function validateStationId(stationId) {
|
|
107785
107906
|
if (!/^[a-z0-9-]+$/.test(stationId)) {
|
|
107786
107907
|
throw new StationSnapshotError("INVALID_STATION", `station id must be a slug, got: ${stationId}`);
|
|
@@ -107886,7 +108007,7 @@ function writeStationSnapshot(options) {
|
|
|
107886
108007
|
for (const plan of plans) {
|
|
107887
108008
|
const destination = resolve22(repoRoot, plan.destination);
|
|
107888
108009
|
const destinationRelative = relative8(repoRoot, destination);
|
|
107889
|
-
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep10) ||
|
|
108010
|
+
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep10) || isAbsolute23(destinationRelative)) {
|
|
107890
108011
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
107891
108012
|
}
|
|
107892
108013
|
let existingDigest = null;
|
|
@@ -108361,10 +108482,10 @@ var init_create_sync_config = __esm(() => {
|
|
|
108361
108482
|
// src/lib/codex-hook-rpc.ts
|
|
108362
108483
|
import { execFile, spawn as spawn3 } from "child_process";
|
|
108363
108484
|
import { realpathSync as realpathSync12, statSync as statSync20 } from "fs";
|
|
108364
|
-
import { isAbsolute as
|
|
108485
|
+
import { isAbsolute as isAbsolute24 } from "path";
|
|
108365
108486
|
import { promisify } from "util";
|
|
108366
108487
|
async function connectCodexHookRpc(options) {
|
|
108367
|
-
const found =
|
|
108488
|
+
const found = isAbsolute24(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
|
|
108368
108489
|
if (!found)
|
|
108369
108490
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE: install Codex or pass --codex-command");
|
|
108370
108491
|
const binary = realpathSync12(found), stat3 = statSync20(binary);
|
|
@@ -108500,10 +108621,10 @@ var init_codex_hook_rpc = __esm(() => {
|
|
|
108500
108621
|
|
|
108501
108622
|
// src/lib/codex-hook-trust-identity.ts
|
|
108502
108623
|
import { realpathSync as realpathSync13 } from "fs";
|
|
108503
|
-
import { dirname as dirname30, isAbsolute as
|
|
108504
|
-
import { isDeepStrictEqual as
|
|
108624
|
+
import { dirname as dirname30, isAbsolute as isAbsolute25, join as join56 } from "path";
|
|
108625
|
+
import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
|
|
108505
108626
|
function inspectRecordedSkillsCli(command, expected) {
|
|
108506
|
-
need4(
|
|
108627
|
+
need4(isAbsolute25(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
|
|
108507
108628
|
const resolved = realpathSync13(command);
|
|
108508
108629
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
108509
108630
|
const manifest = snapshot(join56(dirname30(dirname30(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
@@ -108511,7 +108632,7 @@ function inspectRecordedSkillsCli(command, expected) {
|
|
|
108511
108632
|
need4(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join56(dirname30(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
108512
108633
|
need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
108513
108634
|
const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
|
|
108514
|
-
need4(
|
|
108635
|
+
need4(isDeepStrictEqual9(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
108515
108636
|
return { receipt, recheck() {
|
|
108516
108637
|
need4(realpathSync13(command) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
108517
108638
|
unchanged2(cli);
|
|
@@ -108522,7 +108643,7 @@ function bindSkillsCli(command, reviewed) {
|
|
|
108522
108643
|
const normal = Bun.which("skills", { PATH: process.env.PATH });
|
|
108523
108644
|
need4(normal, "SKILLS_COMMAND_UNAVAILABLE");
|
|
108524
108645
|
const resolved = realpathSync13(normal);
|
|
108525
|
-
const bound =
|
|
108646
|
+
const bound = isAbsolute25(command) ? command : command === "skills" ? normal : undefined;
|
|
108526
108647
|
need4(bound && realpathSync13(bound) === resolved, "SKILLS_COMMAND_MISMATCH");
|
|
108527
108648
|
const entrypoint = reviewed?.path ?? process.argv[1];
|
|
108528
108649
|
need4(entrypoint && realpathSync13(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
|
|
@@ -108547,19 +108668,19 @@ var init_codex_hook_trust_identity = __esm(() => {
|
|
|
108547
108668
|
});
|
|
108548
108669
|
|
|
108549
108670
|
// src/lib/codex-hook-policy-recovery.ts
|
|
108550
|
-
import { isDeepStrictEqual as
|
|
108671
|
+
import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
|
|
108551
108672
|
function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
|
|
108552
108673
|
need4(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
|
|
108553
108674
|
need4(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
108554
108675
|
const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
|
|
108555
108676
|
const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
|
|
108556
108677
|
need4(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
|
|
108557
|
-
const changes = ["roots", "sources"].filter((key) => !
|
|
108678
|
+
const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual10(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
|
|
108558
108679
|
need4(changes.length > 0, "RECONCILE_POLICY_CHANGED");
|
|
108559
108680
|
const compared = structuredClone(current);
|
|
108560
108681
|
compared.bridge.discovery.claude.roots = oldDiscovery.roots;
|
|
108561
108682
|
compared.bridge.discovery.claude.sources = oldDiscovery.sources;
|
|
108562
|
-
need4(
|
|
108683
|
+
need4(isDeepStrictEqual10(before2, compared), "RECONCILE_POLICY_CHANGED");
|
|
108563
108684
|
verifyAgentDiscovery(newDiscovery);
|
|
108564
108685
|
return { reason: options.reason, changes };
|
|
108565
108686
|
}
|
|
@@ -108569,76 +108690,13 @@ var init_codex_hook_policy_recovery = __esm(() => {
|
|
|
108569
108690
|
init_agent_discovery();
|
|
108570
108691
|
});
|
|
108571
108692
|
|
|
108572
|
-
// src/lib/codex-hook-additions-review.ts
|
|
108573
|
-
import { isAbsolute as isAbsolute25 } from "path";
|
|
108574
|
-
import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
|
|
108575
|
-
function exactKeys3(value, keys3) {
|
|
108576
|
-
need4(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual9(Object.keys(value).sort(), [...keys3].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
108577
|
-
}
|
|
108578
|
-
function reviewNativeHookAdditions(options) {
|
|
108579
|
-
need4(isAbsolute25(options.path), "RECONCILE_ADDITIONS_REVIEW_PATH");
|
|
108580
|
-
const reviewFile = snapshot(options.path, true), review = JSON.parse(reviewFile.text);
|
|
108581
|
-
exactKeys3(review, ["version", "kind", "reason", "producerEvidence", "journalIntentSha256", "policySha256", "configCurrentSha256", "hooksCurrentSha256", "producerBefore", "additions"]);
|
|
108582
|
-
need4(review.version === 1 && review.kind === "native-hook-additions" && text3(review.reason) && text3(review.producerEvidence), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
108583
|
-
for (const [name, expected] of [["journalIntentSha256", options.intent.sha256], ["policySha256", options.policy.sha256], ["configCurrentSha256", options.configCurrent.sha256], ["hooksCurrentSha256", options.hooksCurrent.sha256]])
|
|
108584
|
-
need4(hash4(review[name]) && review[name] === expected, "RECONCILE_ADDITIONS_REVIEW_CHANGED");
|
|
108585
|
-
exactKeys3(review.producerBefore, ["configPath", "configSha256", "hooksPath", "hooksSha256"]);
|
|
108586
|
-
const before2 = review.producerBefore;
|
|
108587
|
-
need4(isAbsolute25(before2.configPath) && isAbsolute25(before2.hooksPath) && hash4(before2.configSha256) && hash4(before2.hooksSha256), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
108588
|
-
const producerConfig = snapshot(before2.configPath, true), producerHooks = snapshot(before2.hooksPath, true);
|
|
108589
|
-
need4(producerConfig.sha256 === before2.configSha256 && producerHooks.sha256 === before2.hooksSha256 && producerHooks.sha256 === options.hooksBefore.sha256, "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
108590
|
-
const original = structure2(options.configBefore.text), producer = structure2(producerConfig.text), expectedProducer = structuredClone(original), originalKeys = options.journalHooks.map((hook) => hook.key);
|
|
108591
|
-
expectedProducer.hooks ??= {};
|
|
108592
|
-
expectedProducer.hooks.state ??= {};
|
|
108593
|
-
for (const hook of options.journalHooks)
|
|
108594
|
-
expectedProducer.hooks.state[hook.key] = { ...expectedProducer.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
108595
|
-
need4(isDeepStrictEqual9(expectedProducer, producer) && codexTrustReconcileWitness(options.configBefore.text, originalKeys) === codexTrustReconcileWitness(producerConfig.text, originalKeys) && codexTrustUnmanagedSemantic(options.configBefore.text, originalKeys) === codexTrustUnmanagedSemantic(producerConfig.text, originalKeys), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
108596
|
-
need4(Array.isArray(review.additions) && review.additions.length > 0 && review.additions.length <= 20, "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
108597
|
-
const expectedHooks = JSON.parse(options.hooksBefore.text), currentHooks = JSON.parse(options.hooksCurrent.text), expectedConfig = structuredClone(producer);
|
|
108598
|
-
need4(expectedHooks.hooks && typeof expectedHooks.hooks === "object" && !Array.isArray(expectedHooks.hooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
108599
|
-
const added = [], seen = new Set, commands = new Set(options.recordedNativeHooks.map((hook) => hook.command));
|
|
108600
|
-
for (const addition of review.additions) {
|
|
108601
|
-
exactKeys3(addition, ["event", "nativeHook"]);
|
|
108602
|
-
need4(Object.hasOwn(events, addition.event), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
108603
|
-
const { event, nativeHook: hook } = addition;
|
|
108604
|
-
exactKeys3(hook, nativeKeys);
|
|
108605
|
-
const groups = expectedHooks.hooks[event] ?? [];
|
|
108606
|
-
need4(Array.isArray(groups), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
108607
|
-
const key = `${options.nativeHooksPath}:${events[event][1]}:${groups.length}:0`;
|
|
108608
|
-
need4(hook.key === key && !seen.has(key) && !options.recordedNativeHooks.some((originalHook) => originalHook.key === key) && !Object.hasOwn(expectedConfig.hooks.state, key), "RECONCILE_ADDITIONS_IDENTITY");
|
|
108609
|
-
need4(hook.eventName === events[event][0] && hook.handlerType === "command" && text3(hook.command, 16384) && !commands.has(hook.command) && Number.isInteger(hook.timeoutSec) && hook.timeoutSec > 0 && hook.timeoutSec <= 60 && 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");
|
|
108610
|
-
seen.add(key);
|
|
108611
|
-
commands.add(hook.command);
|
|
108612
|
-
added.push(hook);
|
|
108613
|
-
expectedHooks.hooks[event] = [...groups, { hooks: [{ type: "command", command: hook.command, timeout: hook.timeoutSec }] }];
|
|
108614
|
-
expectedConfig.hooks.state[key] = { enabled: true, trusted_hash: hook.currentHash };
|
|
108615
|
-
}
|
|
108616
|
-
need4(isDeepStrictEqual9(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
108617
|
-
const keys3 = added.map((hook) => hook.key);
|
|
108618
|
-
need4(isDeepStrictEqual9(expectedConfig, structure2(options.configCurrent.text)) && codexTrustReconcileWitness(producerConfig.text, keys3) === codexTrustReconcileWitness(options.configCurrent.text, keys3) && codexTrustUnmanagedSemantic(producerConfig.text, keys3) === codexTrustUnmanagedSemantic(options.configCurrent.text, keys3), "RECONCILE_ADDITIONS_CONFIG");
|
|
108619
|
-
const recheck = () => {
|
|
108620
|
-
unchanged2(reviewFile);
|
|
108621
|
-
unchanged2(producerConfig);
|
|
108622
|
-
unchanged2(producerHooks);
|
|
108623
|
-
};
|
|
108624
|
-
recheck();
|
|
108625
|
-
return { hooks: added, recheck, receipt: { reviewPath: reviewFile.file, reviewSha256: reviewFile.sha256, reason: review.reason, producerEvidence: review.producerEvidence, producerBefore: { configPath: producerConfig.file, configSha256: producerConfig.sha256, hooksPath: producerHooks.file, hooksSha256: producerHooks.sha256 }, additions: added } };
|
|
108626
|
-
}
|
|
108627
|
-
var events, hash4 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), text3 = (value, max2 = 2048) => typeof value === "string" && value.trim().length > 0 && value.length <= max2 && !/[\0\r\n]/.test(value), structure2 = (value) => JSON.parse(JSON.stringify(Bun.TOML.parse(value))), nativeKeys;
|
|
108628
|
-
var init_codex_hook_additions_review = __esm(() => {
|
|
108629
|
-
init_codex_hook_trust_files();
|
|
108630
|
-
init_codex_hook_trust_layout();
|
|
108631
|
-
events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
108632
|
-
nativeKeys = ["key", "eventName", "handlerType", "command", "matcher", "timeoutSec", "async", "statusMessage", "additionalContextLimit", "sourcePath", "source", "pluginId", "isManaged", "currentHash", "enabled", "trustStatus"];
|
|
108633
|
-
});
|
|
108634
|
-
|
|
108635
108693
|
// src/lib/agent-codex-trust.ts
|
|
108636
108694
|
import { lstatSync as lstatSync29, mkdirSync as mkdirSync25, readdirSync as readdirSync20, readFileSync as readFileSync37, realpathSync as realpathSync14, statSync as statSync21 } from "fs";
|
|
108637
108695
|
import { createHash as createHash27, randomUUID as randomUUID10 } from "crypto";
|
|
108638
108696
|
import { execFileSync } from "child_process";
|
|
108639
108697
|
import { dirname as dirname31, isAbsolute as isAbsolute26, join as join57, resolve as resolve23 } from "path";
|
|
108640
108698
|
import { homedir as homedir12 } from "os";
|
|
108641
|
-
import { isDeepStrictEqual as
|
|
108699
|
+
import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
|
|
108642
108700
|
function verifyReleaseArtifact(proof, skillsCli) {
|
|
108643
108701
|
need4(isAbsolute26(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
108644
108702
|
const tarPath = realpathSync14(proof.packageTarPath);
|
|
@@ -108680,7 +108738,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108680
108738
|
const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
|
|
108681
108739
|
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 };
|
|
108682
108740
|
need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
|
|
108683
|
-
need4(
|
|
108741
|
+
need4(isDeepStrictEqual11(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
|
|
108684
108742
|
need4(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
|
|
108685
108743
|
const policyCurrent = snapshot(join57(dataDir3, "agent-policy.json"), true);
|
|
108686
108744
|
const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
|
|
@@ -108693,7 +108751,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108693
108751
|
if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
|
|
108694
108752
|
need4(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
|
|
108695
108753
|
const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
|
|
108696
|
-
need4(
|
|
108754
|
+
need4(isDeepStrictEqual11(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
|
|
108697
108755
|
supersededBinding = true;
|
|
108698
108756
|
}
|
|
108699
108757
|
if (supersededBinding || recovery) {
|
|
@@ -108718,16 +108776,19 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108718
108776
|
const before2 = structure3(configBefore.text), current = structure3(currentConfig.text), expectedConfig = structuredClone(before2);
|
|
108719
108777
|
expectedConfig.hooks ??= {};
|
|
108720
108778
|
expectedConfig.hooks.state ??= {};
|
|
108721
|
-
for (const hook of
|
|
108779
|
+
for (const hook of intent.hooks)
|
|
108722
108780
|
expectedConfig.hooks.state[hook.key] = { ...expectedConfig.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
108781
|
+
if (additions)
|
|
108782
|
+
for (const { key, state } of readReviewedAdditionTrustStates(additions.trustProof, configBefore, currentConfig, intent.hooks))
|
|
108783
|
+
expectedConfig.hooks.state[key] = { ...state };
|
|
108723
108784
|
if (supersededBinding) {
|
|
108724
108785
|
for (const hook of intent.hooks) {
|
|
108725
108786
|
const state = current.hooks?.state?.[hook.key];
|
|
108726
108787
|
need4(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
|
|
108727
108788
|
}
|
|
108728
108789
|
} else
|
|
108729
|
-
need4(
|
|
108730
|
-
const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig,
|
|
108790
|
+
need4(isDeepStrictEqual11(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
|
|
108791
|
+
const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
|
|
108731
108792
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
|
|
108732
108793
|
const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
|
|
108733
108794
|
if (recoveryCli)
|
|
@@ -108735,14 +108796,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108735
108796
|
const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
108736
108797
|
if (supersededBinding)
|
|
108737
108798
|
verifyReleaseArtifact(options.releaseProof, skillsCli);
|
|
108738
|
-
const skillsBindingChanged = !
|
|
108799
|
+
const skillsBindingChanged = !isDeepStrictEqual11(skillsCli.receipt, intent.skillsCli);
|
|
108739
108800
|
need4(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
108740
108801
|
if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
|
|
108741
108802
|
need4(supersededBinding, "RECONCILE_HOOKS_CHANGED");
|
|
108742
108803
|
const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
|
|
108743
108804
|
for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
|
|
108744
108805
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
108745
|
-
need4(
|
|
108806
|
+
need4(isDeepStrictEqual11(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
108746
108807
|
}
|
|
108747
108808
|
}
|
|
108748
108809
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
@@ -108765,9 +108826,9 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108765
108826
|
delete identity4.command;
|
|
108766
108827
|
return identity4;
|
|
108767
108828
|
};
|
|
108768
|
-
need4(
|
|
108829
|
+
need4(isDeepStrictEqual11(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
108769
108830
|
if (!plannedKeys.has(expected.key))
|
|
108770
|
-
need4(
|
|
108831
|
+
need4(isDeepStrictEqual11(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
|
|
108771
108832
|
}
|
|
108772
108833
|
for (const wanted of intent.hooks) {
|
|
108773
108834
|
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join57(home, ".codex/hooks.json"), hooksPath));
|
|
@@ -108784,12 +108845,12 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108784
108845
|
}
|
|
108785
108846
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
108786
108847
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
108787
|
-
need4(layers?.length === 1 && !layers[0].disabledReason &&
|
|
108848
|
+
need4(layers?.length === 1 && !layers[0].disabledReason && isDeepStrictEqual11(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
|
|
108788
108849
|
const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
|
|
108789
|
-
need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks ||
|
|
108850
|
+
need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual11(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
|
|
108790
108851
|
const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
108791
108852
|
const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
108792
|
-
need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason &&
|
|
108853
|
+
need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason && isDeepStrictEqual11(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
|
|
108793
108854
|
if (recovery) {
|
|
108794
108855
|
for (const response of [config2, finalConfig]) {
|
|
108795
108856
|
const layerIndex = response.layers.findIndex((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
@@ -108847,7 +108908,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108847
108908
|
const groups = declarations[event];
|
|
108848
108909
|
need4(Array.isArray(groups), "DECLARATION_CHANGED");
|
|
108849
108910
|
const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
|
|
108850
|
-
need4(matches2.length === 1 &&
|
|
108911
|
+
need4(matches2.length === 1 && isDeepStrictEqual11(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
|
|
108851
108912
|
expected.push({ event: nativeEvent, key: `${join57(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
|
|
108852
108913
|
}
|
|
108853
108914
|
codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
|
|
@@ -108871,7 +108932,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108871
108932
|
const discovered = await list2(), admitted = admit(discovered);
|
|
108872
108933
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
108873
108934
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
108874
|
-
need4(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason &&
|
|
108935
|
+
need4(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason && isDeepStrictEqual11(layers[0].config, original), "NATIVE_CONFIG_MISMATCH");
|
|
108875
108936
|
need4(config2.config && config2.config.features?.hooks !== false, "NATIVE_HOOKS_DISABLED");
|
|
108876
108937
|
need4(config2.layers.slice(0, config2.layers.indexOf(layers[0])).every((layer) => layer.config?.hooks === undefined && layer.config?.features?.hooks === undefined), "NATIVE_CONFIG_OVERRIDDEN");
|
|
108877
108938
|
const keys3 = admitted.map((h) => h.key), planned = admitted.filter((h) => !h.enabled || h.trustStatus !== "trusted");
|
|
@@ -108910,7 +108971,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108910
108971
|
save(join57(journal, "hooks.before.json"), hooksFile.bytes);
|
|
108911
108972
|
save(join57(journal, "policy.before.json"), policyFile.bytes);
|
|
108912
108973
|
save(join57(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
|
|
108913
|
-
need4(
|
|
108974
|
+
need4(isDeepStrictEqual11(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
|
|
108914
108975
|
checkInputs();
|
|
108915
108976
|
let result2;
|
|
108916
108977
|
try {
|
|
@@ -108924,7 +108985,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108924
108985
|
expectedConfig.hooks.state ??= {};
|
|
108925
108986
|
for (const h of planned)
|
|
108926
108987
|
expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
|
|
108927
|
-
need4(
|
|
108988
|
+
need4(isDeepStrictEqual11(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
|
|
108928
108989
|
const savedConfig = snapshot(join57(journal, "config.before.toml"), true);
|
|
108929
108990
|
need4(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
|
|
108930
108991
|
const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
|
|
@@ -108932,8 +108993,8 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108932
108993
|
unchanged2(hooksFile);
|
|
108933
108994
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
|
|
108934
108995
|
const current = await list2(), confirmed = admit(current);
|
|
108935
|
-
need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") &&
|
|
108936
|
-
need4(
|
|
108996
|
+
need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual11(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
|
|
108997
|
+
need4(isDeepStrictEqual11(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
|
|
108937
108998
|
unchanged2(after2);
|
|
108938
108999
|
unchanged2(savedConfig);
|
|
108939
109000
|
unchanged2(hooksFile);
|