@hasna/skills 0.10.63 → 0.10.65
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 +32 -0
- package/bin/index.js +2732 -1877
- package/bin/mcp.js +1472 -586
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1798 -896
- package/dist/lib/agent-discovery.d.ts +26 -2
- package/dist/lib/agent-integration.d.ts +14 -1
- package/dist/lib/codex-plugin-skill-controls.d.ts +9 -0
- package/dist/lib/codex-semantic-cache-witness.d.ts +57 -0
- package/dist/lib/codex-settings-witness.d.ts +14 -0
- package/dist/lib/hook-diagnostics.d.ts +2 -0
- package/dist/runtime-prerequisites.js +146 -14
- package/dist/sdk/index.js +2093 -1193
- package/package.json +1 -1
package/bin/mcp.js
CHANGED
|
@@ -23,7 +23,7 @@ var package_default;
|
|
|
23
23
|
var init_package = __esm(() => {
|
|
24
24
|
package_default = {
|
|
25
25
|
name: "@hasna/skills",
|
|
26
|
-
version: "0.10.
|
|
26
|
+
version: "0.10.65",
|
|
27
27
|
description: "Skills library for AI coding agents",
|
|
28
28
|
type: "module",
|
|
29
29
|
bin: {
|
|
@@ -23088,6 +23088,53 @@ function assertAgentPolicyCollections(policy) {
|
|
|
23088
23088
|
requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
|
|
23089
23089
|
}
|
|
23090
23090
|
}
|
|
23091
|
+
if (value.codexSemanticCache !== undefined) {
|
|
23092
|
+
const proof = record2(value.codexSemanticCache, 8), witness = record2(proof.witness, 6);
|
|
23093
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).length === 8 && proof.schema === "hasna.codex-semantic-cache-discovery.v1" && typeof proof.previousPolicySha256 === "string" && /^[a-f0-9]{64}$/.test(proof.previousPolicySha256) && Object.keys(witness).length === 6 && witness.schema === "hasna.codex-semantic-cache-witness.v1" && typeof witness.sha256 === "string" && /^[a-f0-9]{64}$/.test(witness.sha256));
|
|
23094
|
+
text(proof.configPath);
|
|
23095
|
+
requireBound(isAbsolute5(proof.configPath) && resolve4(proof.configPath) === proof.configPath && basename5(proof.configPath) === "config.toml");
|
|
23096
|
+
text(witness.cacheRoot);
|
|
23097
|
+
requireBound(isAbsolute5(witness.cacheRoot) && resolve4(witness.cacheRoot) === witness.cacheRoot);
|
|
23098
|
+
if (proof.hookRootAlias !== null) {
|
|
23099
|
+
requireBound(object2(proof.hookRootAlias) && Object.keys(proof.hookRootAlias).length === 2);
|
|
23100
|
+
for (const key of ["alias", "target"]) {
|
|
23101
|
+
text(proof.hookRootAlias[key]);
|
|
23102
|
+
requireBound(isAbsolute5(proof.hookRootAlias[key]) && resolve4(proof.hookRootAlias[key]) === proof.hookRootAlias[key]);
|
|
23103
|
+
}
|
|
23104
|
+
}
|
|
23105
|
+
const parents2 = array2(proof.appOnlyParents, 4096);
|
|
23106
|
+
for (const parent of parents2) {
|
|
23107
|
+
requireBound(object2(parent) && Object.keys(parent).length === 5 && parent.role === "app-only");
|
|
23108
|
+
for (const key of ["pluginId", "namespace", "pluginParent", "remotePluginId"])
|
|
23109
|
+
text(parent[key]);
|
|
23110
|
+
requireBound(isAbsolute5(parent.pluginParent) && resolve4(parent.pluginParent) === parent.pluginParent);
|
|
23111
|
+
}
|
|
23112
|
+
for (const row of [...array2(witness.controls, 4096), ...array2(witness.appOnlyParents, 4096)]) {
|
|
23113
|
+
requireBound(object2(row));
|
|
23114
|
+
for (const key of ["pluginId", "namespace", "pluginParent"])
|
|
23115
|
+
text(row[key]);
|
|
23116
|
+
for (const key of ["manifestSha256", "appSha256", "mcpSha256", "receiptSha256"])
|
|
23117
|
+
if (row[key] !== undefined)
|
|
23118
|
+
requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
|
|
23119
|
+
}
|
|
23120
|
+
for (const skill of array2(witness.skills, 4096)) {
|
|
23121
|
+
requireBound(object2(skill));
|
|
23122
|
+
for (const key of ["name", "pluginId", "namespace", "pluginParent"])
|
|
23123
|
+
text(skill[key]);
|
|
23124
|
+
for (const rule of array2(skill.disabledRules, 4096))
|
|
23125
|
+
text(rule);
|
|
23126
|
+
}
|
|
23127
|
+
for (const source of array2(proof.supersededSources, AGENT_POLICY_LIMITS.discoverySources)) {
|
|
23128
|
+
requireBound(object2(source));
|
|
23129
|
+
text(source.path);
|
|
23130
|
+
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
23131
|
+
}
|
|
23132
|
+
for (const directory of array2(proof.supersededDirectories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
23133
|
+
requireBound(object2(directory));
|
|
23134
|
+
text(directory.path);
|
|
23135
|
+
requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
|
|
23136
|
+
}
|
|
23137
|
+
}
|
|
23091
23138
|
for (const root of array2(value.roots, AGENT_POLICY_LIMITS.discoveryRoots))
|
|
23092
23139
|
text(root);
|
|
23093
23140
|
for (const source of array2(value.sources, AGENT_POLICY_LIMITS.discoverySources)) {
|
|
@@ -23095,12 +23142,12 @@ function assertAgentPolicyCollections(policy) {
|
|
|
23095
23142
|
text(source.path);
|
|
23096
23143
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
23097
23144
|
if (source.hashMode !== undefined)
|
|
23098
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1", "claude-marketplace-entry-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
23145
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "codex-settings-v5", "sumi-settings-v1", "claude-marketplace-entry-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
23099
23146
|
if (source.hashMode === "claude-plugin-manifest-v1")
|
|
23100
23147
|
requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute5(source.path) && resolve4(source.path) === source.path && source.path.endsWith(join10(sep3, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
|
|
23101
23148
|
if (source.hashMode === "sumi-settings-v1")
|
|
23102
23149
|
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute5(source.path) && resolve4(source.path) === source.path && basename5(source.path) === "sumi.json");
|
|
23103
|
-
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
23150
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5"))
|
|
23104
23151
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute5(source.path) && resolve4(source.path) === source.path && basename5(source.path) === "config.toml");
|
|
23105
23152
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
|
|
23106
23153
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute5(source.path) && resolve4(source.path) === source.path && basename5(source.path) === "settings.json");
|
|
@@ -32731,6 +32778,7 @@ var init_codex_hook_rpc = __esm(() => {
|
|
|
32731
32778
|
});
|
|
32732
32779
|
|
|
32733
32780
|
// src/lib/codex-native-skill-catalog.ts
|
|
32781
|
+
import { realpathSync as realpathSync4 } from "fs";
|
|
32734
32782
|
import { isAbsolute as isAbsolute10, normalize as normalize2 } from "path";
|
|
32735
32783
|
function codexPluginSourceIsConfigControlled(plugin) {
|
|
32736
32784
|
const remoteRealms = ["openai-curated-remote", "created-by-me-remote", "workspace-directory", "workspace-shared-with-me", "workspace-shared-with-me-private", "workspace-shared-with-me-unlisted"];
|
|
@@ -32779,6 +32827,43 @@ function projectCodexInstalledPluginEntries(value) {
|
|
|
32779
32827
|
};
|
|
32780
32828
|
});
|
|
32781
32829
|
}
|
|
32830
|
+
function canonicalPath(path) {
|
|
32831
|
+
try {
|
|
32832
|
+
return realpathSync4(path);
|
|
32833
|
+
} catch {
|
|
32834
|
+
return normalize2(path);
|
|
32835
|
+
}
|
|
32836
|
+
}
|
|
32837
|
+
function isCodexNativeSkillDisabled(skill, rules) {
|
|
32838
|
+
if (!scalar(skill.name, MAX_NAME_BYTES) || !absolutePath2(skill.path))
|
|
32839
|
+
refuse();
|
|
32840
|
+
if (rules === undefined)
|
|
32841
|
+
return false;
|
|
32842
|
+
if (!Array.isArray(rules) || rules.length > MAX_SKILLS)
|
|
32843
|
+
refuse();
|
|
32844
|
+
let disabled = false;
|
|
32845
|
+
for (const rule of rules) {
|
|
32846
|
+
if (!object5(rule) || typeof rule.enabled !== "boolean")
|
|
32847
|
+
refuse();
|
|
32848
|
+
const hasName = rule.name !== undefined && rule.name !== null;
|
|
32849
|
+
const hasPath = rule.path !== undefined && rule.path !== null;
|
|
32850
|
+
if (hasName === hasPath)
|
|
32851
|
+
refuse();
|
|
32852
|
+
let matches;
|
|
32853
|
+
if (hasName) {
|
|
32854
|
+
if (typeof rule.name !== "string" || !scalar(rule.name.trim(), MAX_NAME_BYTES))
|
|
32855
|
+
refuse();
|
|
32856
|
+
matches = rule.name.trim() === skill.name;
|
|
32857
|
+
} else {
|
|
32858
|
+
if (!absolutePath2(rule.path))
|
|
32859
|
+
refuse();
|
|
32860
|
+
matches = canonicalPath(rule.path) === canonicalPath(skill.path);
|
|
32861
|
+
}
|
|
32862
|
+
if (matches)
|
|
32863
|
+
disabled = !rule.enabled;
|
|
32864
|
+
}
|
|
32865
|
+
return disabled;
|
|
32866
|
+
}
|
|
32782
32867
|
var MAX_SKILLS = 4096, MAX_NAME_BYTES = 1024, MAX_PATH_BYTES = 16384, MAX_PLUGIN_ID_BYTES = 1024, object5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value), remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value), absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute10(value);
|
|
32783
32868
|
var init_codex_native_skill_catalog = __esm(() => {
|
|
32784
32869
|
init_codex_hook_rpc();
|
|
@@ -32788,13 +32873,13 @@ var init_codex_native_skill_catalog = __esm(() => {
|
|
|
32788
32873
|
|
|
32789
32874
|
// src/lib/codex-plugin-skill-controls.ts
|
|
32790
32875
|
import { basename as basename6, dirname as dirname12, join as join18, relative as relative5, resolve as resolve11, sep as sep6 } from "path";
|
|
32791
|
-
import { lstatSync as lstatSync12, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as
|
|
32876
|
+
import { lstatSync as lstatSync12, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "fs";
|
|
32792
32877
|
function refuse2() {
|
|
32793
32878
|
throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
|
|
32794
32879
|
}
|
|
32795
32880
|
function remoteInstallationMatches(parent, remotePluginId, read) {
|
|
32796
32881
|
try {
|
|
32797
|
-
if (!remotePluginIdentifier(remotePluginId) || !lstatSync12(parent).isDirectory() ||
|
|
32882
|
+
if (!remotePluginIdentifier(remotePluginId) || !lstatSync12(parent).isDirectory() || realpathSync5(parent) !== parent)
|
|
32798
32883
|
return false;
|
|
32799
32884
|
const path = join18(parent, ".codex-remote-plugin-install.json"), stat = lstatSync12(path, { throwIfNoEntry: false });
|
|
32800
32885
|
if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
|
|
@@ -32871,6 +32956,9 @@ function isBundledCleanupHooks(root, namespace, value) {
|
|
|
32871
32956
|
}
|
|
32872
32957
|
return true;
|
|
32873
32958
|
}
|
|
32959
|
+
function readCodexPluginRootControls(root, read) {
|
|
32960
|
+
return rootControls(root, read);
|
|
32961
|
+
}
|
|
32874
32962
|
function rootControls(root, read) {
|
|
32875
32963
|
const manifestPath = join18(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync12(manifestPath, { throwIfNoEntry: false });
|
|
32876
32964
|
if (!lstatSync12(join18(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
|
|
@@ -32893,7 +32981,7 @@ function rootControls(root, read) {
|
|
|
32893
32981
|
const mcp = join18(root, ".mcp.json"), mcpPresent = lstatSync12(mcp, { throwIfNoEntry: false });
|
|
32894
32982
|
if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
|
|
32895
32983
|
refuse2();
|
|
32896
|
-
if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 ||
|
|
32984
|
+
if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync5(mcp) !== mcp))
|
|
32897
32985
|
refuse2();
|
|
32898
32986
|
if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
|
|
32899
32987
|
refuse2();
|
|
@@ -32906,6 +32994,76 @@ function rootControls(root, read) {
|
|
|
32906
32994
|
const appSha256 = present ? appControls(app, read) : undefined;
|
|
32907
32995
|
return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
|
|
32908
32996
|
}
|
|
32997
|
+
function documentIdentity(document2, cache, read) {
|
|
32998
|
+
if (!document2.startsWith(cache + sep6) || !document2.endsWith(sep6 + "SKILL.md"))
|
|
32999
|
+
refuse2();
|
|
33000
|
+
const text2 = read(document2);
|
|
33001
|
+
if (Buffer.byteLength(text2) > 1024 * 1024)
|
|
33002
|
+
refuse2();
|
|
33003
|
+
const lines = text2.split(/\r?\n/);
|
|
33004
|
+
if (lines[0]?.trim() !== "---")
|
|
33005
|
+
refuse2();
|
|
33006
|
+
const end = lines.findIndex((line, i) => i > 0 && line.trim() === "---");
|
|
33007
|
+
if (end < 2)
|
|
33008
|
+
refuse2();
|
|
33009
|
+
const yaml = lines.slice(1, end).join(`
|
|
33010
|
+
`);
|
|
33011
|
+
if (/(?:^|\s)[&*!]|<<\s*:/.test(yaml) || lines.slice(1, end).filter((line) => /^name\s*:/.test(line)).length !== 1)
|
|
33012
|
+
refuse2();
|
|
33013
|
+
let front;
|
|
33014
|
+
try {
|
|
33015
|
+
front = Bun.YAML.parse(yaml);
|
|
33016
|
+
} catch {
|
|
33017
|
+
refuse2();
|
|
33018
|
+
}
|
|
33019
|
+
if (!front || typeof front !== "object" || !identifier2(front.name) || typeof front.description !== "string" || !front.description.trim())
|
|
33020
|
+
refuse2();
|
|
33021
|
+
for (let root = dirname12(document2), depth = 0;root.startsWith(cache + sep6) && depth < 12; root = dirname12(root), depth++) {
|
|
33022
|
+
const path = join18(root, ".codex-plugin", "plugin.json");
|
|
33023
|
+
if (!lstatSync12(path, { throwIfNoEntry: false }))
|
|
33024
|
+
continue;
|
|
33025
|
+
const manifestFile = lstatSync12(path);
|
|
33026
|
+
if (!lstatSync12(join18(root, ".codex-plugin")).isDirectory() || !manifestFile.isFile() || manifestFile.size > 1024 * 1024)
|
|
33027
|
+
refuse2();
|
|
33028
|
+
let manifest;
|
|
33029
|
+
try {
|
|
33030
|
+
const text3 = read(path);
|
|
33031
|
+
hashNativeJsonControls(text3);
|
|
33032
|
+
manifest = JSON.parse(text3);
|
|
33033
|
+
} catch {
|
|
33034
|
+
refuse2();
|
|
33035
|
+
}
|
|
33036
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
33037
|
+
refuse2();
|
|
33038
|
+
const declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
33039
|
+
if (declaredVersion === null || (declaredVersion ? declaredVersion !== basename6(root) : basename6(root) !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
33040
|
+
refuse2();
|
|
33041
|
+
return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent: dirname12(root), root };
|
|
33042
|
+
}
|
|
33043
|
+
refuse2();
|
|
33044
|
+
}
|
|
33045
|
+
function identity3(document2, cache, read) {
|
|
33046
|
+
const parsed = documentIdentity(document2, cache, read);
|
|
33047
|
+
const controls = rootControls(parsed.root, read);
|
|
33048
|
+
if (controls.namespace !== parsed.namespace)
|
|
33049
|
+
refuse2();
|
|
33050
|
+
return { name: parsed.name, pluginParent: parsed.pluginParent, ...controls };
|
|
33051
|
+
}
|
|
33052
|
+
function classifyCodexPluginCacheDocument(document2, cache, read) {
|
|
33053
|
+
if (!document2.startsWith(cache + sep6))
|
|
33054
|
+
return null;
|
|
33055
|
+
try {
|
|
33056
|
+
const located = documentIdentity(document2, cache, read), parsed = identity3(document2, cache, read);
|
|
33057
|
+
if (!lstatSync12(document2).isFile() || realpathSync5(document2) !== document2 || realpathSync5(located.root) !== located.root || realpathSync5(located.pluginParent) !== located.pluginParent || realpathSync5(join18(located.root, ".codex-plugin/plugin.json")) !== join18(located.root, ".codex-plugin/plugin.json"))
|
|
33058
|
+
return null;
|
|
33059
|
+
const parts = relative5(cache, parsed.pluginParent).split(sep6);
|
|
33060
|
+
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== parsed.namespace)
|
|
33061
|
+
return null;
|
|
33062
|
+
return { name: parsed.name, namespace: parsed.namespace, pluginId: `${parts[1]}@${parts[0]}`, pluginParent: parsed.pluginParent, root: located.root, manifestSha256: parsed.manifestSha256 };
|
|
33063
|
+
} catch {
|
|
33064
|
+
return null;
|
|
33065
|
+
}
|
|
33066
|
+
}
|
|
32909
33067
|
function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
32910
33068
|
try {
|
|
32911
33069
|
const parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
|
|
@@ -32969,7 +33127,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read, rules)
|
|
|
32969
33127
|
for (const parent of new Set(controls.map((control) => control.pluginParent))) {
|
|
32970
33128
|
if (absentDisabledCodexPluginParent(cache, parent, controls, rules))
|
|
32971
33129
|
continue;
|
|
32972
|
-
if (!parent.startsWith(cache + sep6) || !lstatSync12(parent, { throwIfNoEntry: false })?.isDirectory() ||
|
|
33130
|
+
if (!parent.startsWith(cache + sep6) || !lstatSync12(parent, { throwIfNoEntry: false })?.isDirectory() || realpathSync5(parent) !== parent)
|
|
32973
33131
|
return false;
|
|
32974
33132
|
for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
|
|
32975
33133
|
if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read))
|
|
@@ -32983,7 +33141,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read, rules)
|
|
|
32983
33141
|
let root = join18(parent, name), stat = lstatSync12(root);
|
|
32984
33142
|
if (stat.isSymbolicLink()) {
|
|
32985
33143
|
const target = resolve11(parent, readlinkSync3(root));
|
|
32986
|
-
if (name !== "latest" || dirname12(target) !== parent || !lstatSync12(target).isDirectory() ||
|
|
33144
|
+
if (name !== "latest" || dirname12(target) !== parent || !lstatSync12(target).isDirectory() || realpathSync5(target) !== target)
|
|
32987
33145
|
return false;
|
|
32988
33146
|
root = target;
|
|
32989
33147
|
stat = lstatSync12(root);
|
|
@@ -33003,10 +33161,10 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read, rules)
|
|
|
33003
33161
|
function sourceInputUnchanged(input2, cache, read) {
|
|
33004
33162
|
try {
|
|
33005
33163
|
const parts = relative5(cache, input2.pluginParent).split(sep6);
|
|
33006
|
-
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== input2.namespace || input2.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input2.pluginId, sourceType: "local", sourceSha256: input2.sourceSha256 }) || input2.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input2.sourceRoot })) || resolve11(input2.sourceRoot) !== input2.sourceRoot || !lstatSync12(input2.sourceRoot).isDirectory() ||
|
|
33164
|
+
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== input2.namespace || input2.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input2.pluginId, sourceType: "local", sourceSha256: input2.sourceSha256 }) || input2.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input2.sourceRoot })) || resolve11(input2.sourceRoot) !== input2.sourceRoot || !lstatSync12(input2.sourceRoot).isDirectory() || realpathSync5(input2.sourceRoot) !== input2.sourceRoot || input2.sourceRoot === cache || input2.sourceRoot.startsWith(cache + sep6) || cache.startsWith(input2.sourceRoot + sep6))
|
|
33007
33165
|
return false;
|
|
33008
33166
|
const path = join18(input2.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync12(path);
|
|
33009
|
-
if (!stat.isFile() || stat.size > 1024 * 1024 ||
|
|
33167
|
+
if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync5(path) !== path)
|
|
33010
33168
|
return false;
|
|
33011
33169
|
const text2 = read(path);
|
|
33012
33170
|
hashNativeJsonControls(text2);
|
|
@@ -33300,6 +33458,8 @@ function normalize3(config2, version2 = 2) {
|
|
|
33300
33458
|
if (isRecord3(profile))
|
|
33301
33459
|
inference(profile, version2);
|
|
33302
33460
|
}
|
|
33461
|
+
if (version2 === 5)
|
|
33462
|
+
projectStatusLine(config2);
|
|
33303
33463
|
}
|
|
33304
33464
|
function disablesOneSkill(entry) {
|
|
33305
33465
|
if (!isRecord3(entry) || entry.enabled !== false)
|
|
@@ -33437,6 +33597,14 @@ function projectModelAvailabilityNux(config2, text2) {
|
|
|
33437
33597
|
}
|
|
33438
33598
|
return { config: config2, numericSource: text2 };
|
|
33439
33599
|
}
|
|
33600
|
+
function projectStatusLine(config2) {
|
|
33601
|
+
const tui = config2.tui;
|
|
33602
|
+
if (!isRecord3(tui) || !Array.isArray(tui.status_line) || !tui.status_line.every((item) => typeof item === "string"))
|
|
33603
|
+
return;
|
|
33604
|
+
delete tui.status_line;
|
|
33605
|
+
if (!Object.keys(tui).length)
|
|
33606
|
+
delete config2.tui;
|
|
33607
|
+
}
|
|
33440
33608
|
function settingsDigest2(text2, version2 = 2) {
|
|
33441
33609
|
let config2;
|
|
33442
33610
|
try {
|
|
@@ -33446,7 +33614,7 @@ function settingsDigest2(text2, version2 = 2) {
|
|
|
33446
33614
|
}
|
|
33447
33615
|
need5(isRecord3(config2));
|
|
33448
33616
|
const mcp = version2 >= 3 ? projectRegularCodexMcp(config2, text2) : { config: config2, numericSource: text2 };
|
|
33449
|
-
const projected = version2
|
|
33617
|
+
const projected = version2 >= 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
|
|
33450
33618
|
normalize3(projected.config, version2);
|
|
33451
33619
|
return sha2(`hasna.skills.codex-settings.v${version2}\x00${JSON.stringify([canonical6(projected.config), numericTokens2(projected.numericSource)])}`);
|
|
33452
33620
|
}
|
|
@@ -33475,6 +33643,14 @@ function hashCodexSettingsReplacementV4(text2, budget2 = { remaining: 256 * 1024
|
|
|
33475
33643
|
function captureCodexSettingsV4(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
33476
33644
|
return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 4) };
|
|
33477
33645
|
}
|
|
33646
|
+
function hashCodexSettingsReplacementV5(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
33647
|
+
need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
33648
|
+
budget2.remaining -= Buffer.byteLength(text2);
|
|
33649
|
+
return settingsDigest2(text2, 5);
|
|
33650
|
+
}
|
|
33651
|
+
function captureCodexSettingsV5(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
33652
|
+
return { path, hashMode: "codex-settings-v5", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 5) };
|
|
33653
|
+
}
|
|
33478
33654
|
var sha2 = (text2) => createHash13("sha256").update(text2).digest("hex"), EFFORT2, VERBOSITY2, MODEL, CODEX_DISCOVERY_PROJECTION_FIELDS, INFERENCE;
|
|
33479
33655
|
var init_codex_settings_witness = __esm(() => {
|
|
33480
33656
|
init_codex_settings_witness_v1();
|
|
@@ -33488,9 +33664,575 @@ var init_codex_settings_witness = __esm(() => {
|
|
|
33488
33664
|
INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
|
|
33489
33665
|
});
|
|
33490
33666
|
|
|
33667
|
+
// src/lib/codex-semantic-cache-witness.ts
|
|
33668
|
+
import { createHash as createHash14 } from "crypto";
|
|
33669
|
+
import { constants as constants7, closeSync as closeSync10, fstatSync as fstatSync10, lstatSync as lstatSync13, openSync as openSync10, readSync as readSync5, readdirSync as readdirSync7, realpathSync as realpathSync6 } from "fs";
|
|
33670
|
+
import { dirname as dirname13, isAbsolute as isAbsolute11, join as join19, relative as relative6, resolve as resolve12, sep as sep7 } from "path";
|
|
33671
|
+
function defaultRead(path) {
|
|
33672
|
+
const before = lstatSync13(path, { throwIfNoEntry: false, bigint: true });
|
|
33673
|
+
if (before === undefined)
|
|
33674
|
+
return refuse3();
|
|
33675
|
+
if (!before.isFile() || before.isSymbolicLink() || before.size > BigInt(MAX_DOCUMENT_BYTES))
|
|
33676
|
+
refuse3();
|
|
33677
|
+
let fd;
|
|
33678
|
+
try {
|
|
33679
|
+
fd = openSync10(path, constants7.O_RDONLY | (constants7.O_NOFOLLOW ?? 0));
|
|
33680
|
+
} catch {
|
|
33681
|
+
return refuse3();
|
|
33682
|
+
}
|
|
33683
|
+
try {
|
|
33684
|
+
const opened = fstatSync10(fd, { bigint: true });
|
|
33685
|
+
if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size || opened.size > BigInt(MAX_DOCUMENT_BYTES))
|
|
33686
|
+
refuse3();
|
|
33687
|
+
const chunks = [];
|
|
33688
|
+
let total = 0;
|
|
33689
|
+
while (total <= MAX_DOCUMENT_BYTES) {
|
|
33690
|
+
const chunk = Buffer.alloc(Math.min(64 * 1024, MAX_DOCUMENT_BYTES + 1 - total));
|
|
33691
|
+
const count = readSync5(fd, chunk, 0, chunk.length, null);
|
|
33692
|
+
if (!count)
|
|
33693
|
+
break;
|
|
33694
|
+
total += count;
|
|
33695
|
+
if (total > MAX_DOCUMENT_BYTES)
|
|
33696
|
+
refuse3();
|
|
33697
|
+
chunks.push(chunk.subarray(0, count));
|
|
33698
|
+
}
|
|
33699
|
+
const after = fstatSync10(fd, { bigint: true });
|
|
33700
|
+
if (after.dev !== opened.dev || after.ino !== opened.ino || after.size !== opened.size || after.mtimeNs !== opened.mtimeNs || after.ctimeNs !== opened.ctimeNs || BigInt(total) !== after.size)
|
|
33701
|
+
refuse3();
|
|
33702
|
+
return Buffer.concat(chunks, total).toString("utf8");
|
|
33703
|
+
} finally {
|
|
33704
|
+
closeSync10(fd);
|
|
33705
|
+
}
|
|
33706
|
+
}
|
|
33707
|
+
function canonical7(value) {
|
|
33708
|
+
if (value === null || typeof value !== "object")
|
|
33709
|
+
return JSON.stringify(value);
|
|
33710
|
+
if (Array.isArray(value))
|
|
33711
|
+
return `[${value.map(canonical7).join(",")}]`;
|
|
33712
|
+
const object6 = value;
|
|
33713
|
+
return `{${Object.keys(object6).sort().map((key) => `${JSON.stringify(key)}:${canonical7(object6[key])}`).join(",")}}`;
|
|
33714
|
+
}
|
|
33715
|
+
function hash3(value) {
|
|
33716
|
+
return createHash14("sha256").update(canonical7(value)).digest("hex");
|
|
33717
|
+
}
|
|
33718
|
+
function plainObject(value) {
|
|
33719
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
33720
|
+
}
|
|
33721
|
+
function exactKeys(value, required2, optional2 = []) {
|
|
33722
|
+
const keys = Object.keys(value).sort();
|
|
33723
|
+
return required2.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required2.includes(key) || optional2.includes(key));
|
|
33724
|
+
}
|
|
33725
|
+
function validPluginParent(cacheRoot, pluginParent, namespace, pluginId) {
|
|
33726
|
+
if (!isAbsolute11(pluginParent) || resolve12(pluginParent) !== pluginParent)
|
|
33727
|
+
return false;
|
|
33728
|
+
const parts = relative6(cacheRoot, pluginParent).split(sep7);
|
|
33729
|
+
return parts.length === 2 && parts.every(identifier3) && parts[1] === namespace && pluginId === `${namespace}@${parts[0]}`;
|
|
33730
|
+
}
|
|
33731
|
+
function assertWitnessShape(value) {
|
|
33732
|
+
if (!plainObject(value) || !exactKeys(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute11(value.cacheRoot) || resolve12(value.cacheRoot) !== value.cacheRoot || !digest(value.sha256) || !Array.isArray(value.controls) || value.controls.length === 0 || value.controls.length > 4096 || !Array.isArray(value.appOnlyParents) || value.appOnlyParents.length > 4096 || !Array.isArray(value.skills) || value.skills.length === 0 || value.skills.length > 4096)
|
|
33733
|
+
refuse3();
|
|
33734
|
+
const witness = value;
|
|
33735
|
+
const controlParents = new Set;
|
|
33736
|
+
for (const candidate of witness.controls) {
|
|
33737
|
+
if (!plainObject(candidate))
|
|
33738
|
+
refuse3();
|
|
33739
|
+
const row = candidate;
|
|
33740
|
+
if (!exactKeys(row, ["pluginId", "namespace", "pluginParent", "manifestSha256"], ["remotePluginId", "receiptSha256", "appSha256", "mcpSha256"]) || !identifier3(row.namespace) || typeof row.pluginId !== "string" || !/^[A-Za-z0-9_-]{1,64}@[A-Za-z0-9_-]{1,64}$/.test(row.pluginId) || typeof row.pluginParent !== "string" || !validPluginParent(String(witness.cacheRoot), row.pluginParent, row.namespace, row.pluginId) || !digest(row.manifestSha256) || row.appSha256 !== undefined && !digest(row.appSha256) || row.mcpSha256 !== undefined && !digest(row.mcpSha256) || row.receiptSha256 !== undefined && !digest(row.receiptSha256) || row.remotePluginId !== undefined && (typeof row.remotePluginId !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(row.remotePluginId)) || row.remotePluginId === undefined !== (row.receiptSha256 === undefined) || controlParents.has(row.pluginParent))
|
|
33741
|
+
refuse3();
|
|
33742
|
+
controlParents.add(row.pluginParent);
|
|
33743
|
+
}
|
|
33744
|
+
const appOnlyParentPaths = new Set;
|
|
33745
|
+
for (const candidate of witness.appOnlyParents) {
|
|
33746
|
+
if (!plainObject(candidate))
|
|
33747
|
+
refuse3();
|
|
33748
|
+
const row = candidate;
|
|
33749
|
+
if (!exactKeys(row, ["role", "pluginId", "namespace", "pluginParent", "remotePluginId", "receiptSha256", "manifestSha256", "appSha256"], ["mcpSha256"]) || row.role !== "app-only" || !identifier3(row.namespace) || typeof row.pluginId !== "string" || !/^[A-Za-z0-9_-]{1,64}@[A-Za-z0-9_-]{1,64}$/.test(row.pluginId) || typeof row.pluginParent !== "string" || !validPluginParent(String(witness.cacheRoot), row.pluginParent, row.namespace, row.pluginId) || typeof row.remotePluginId !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(row.remotePluginId) || !digest(row.receiptSha256) || !digest(row.manifestSha256) || !digest(row.appSha256) || row.mcpSha256 !== undefined && !digest(row.mcpSha256) || appOnlyParentPaths.has(row.pluginParent) || controlParents.has(row.pluginParent))
|
|
33750
|
+
refuse3();
|
|
33751
|
+
appOnlyParentPaths.add(row.pluginParent);
|
|
33752
|
+
}
|
|
33753
|
+
const skillNames = new Set;
|
|
33754
|
+
const qualifiedNames = new Set;
|
|
33755
|
+
for (const candidate of witness.skills) {
|
|
33756
|
+
if (!plainObject(candidate))
|
|
33757
|
+
refuse3();
|
|
33758
|
+
const row = candidate;
|
|
33759
|
+
if (!exactKeys(row, ["name", "pluginId", "namespace", "pluginParent", "disabledRules"]) || typeof row.name !== "string" || !row.name.startsWith(`${String(row.namespace)}:`) || !identifier3(row.name.slice(String(row.namespace).length + 1)) || !identifier3(row.namespace) || typeof row.pluginId !== "string" || !/^[A-Za-z0-9_-]{1,64}@[A-Za-z0-9_-]{1,64}$/.test(row.pluginId) || typeof row.pluginParent !== "string" || !validPluginParent(String(witness.cacheRoot), row.pluginParent, String(row.namespace), row.pluginId) || !Array.isArray(row.disabledRules) || row.disabledRules.length !== 1 || row.disabledRules.some((rule) => rule !== `name:${String(row.name)}=disabled`) || !controlParents.has(row.pluginParent))
|
|
33760
|
+
refuse3();
|
|
33761
|
+
const qualified = `${row.pluginId}\x00${row.name}`;
|
|
33762
|
+
if (qualifiedNames.has(qualified) || skillNames.has(row.name))
|
|
33763
|
+
refuse3();
|
|
33764
|
+
qualifiedNames.add(qualified);
|
|
33765
|
+
skillNames.add(row.name);
|
|
33766
|
+
}
|
|
33767
|
+
const projection = {
|
|
33768
|
+
schema: witness.schema,
|
|
33769
|
+
cacheRoot: witness.cacheRoot,
|
|
33770
|
+
controls: witness.controls,
|
|
33771
|
+
appOnlyParents: witness.appOnlyParents,
|
|
33772
|
+
skills: witness.skills
|
|
33773
|
+
};
|
|
33774
|
+
if (hash3(projection) !== witness.sha256)
|
|
33775
|
+
refuse3();
|
|
33776
|
+
}
|
|
33777
|
+
function assertCanonicalDirectory(path) {
|
|
33778
|
+
if (resolve12(path) !== path || realpathSync6(path) !== path)
|
|
33779
|
+
refuse3();
|
|
33780
|
+
for (let cursor = path;; cursor = dirname13(cursor)) {
|
|
33781
|
+
const stat = lstatSync13(cursor, { throwIfNoEntry: false });
|
|
33782
|
+
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
33783
|
+
refuse3();
|
|
33784
|
+
if (dirname13(cursor) === cursor)
|
|
33785
|
+
break;
|
|
33786
|
+
}
|
|
33787
|
+
}
|
|
33788
|
+
function walkSafeTree(path, state, depth = 0, root = path) {
|
|
33789
|
+
if (depth > 64 || ++state.entries > 16384)
|
|
33790
|
+
refuse3();
|
|
33791
|
+
const names = readdirSync7(path).sort();
|
|
33792
|
+
const files = [];
|
|
33793
|
+
for (const name of names) {
|
|
33794
|
+
if (!name || name === "." || name === ".." || name.includes(sep7) || ++state.entries > 16384)
|
|
33795
|
+
refuse3();
|
|
33796
|
+
const child2 = join19(path, name), stat = lstatSync13(child2, { bigint: true });
|
|
33797
|
+
state.members.push(child2);
|
|
33798
|
+
state.signature.push(`${relative6(root, child2)}\x00${stat.mode}\x00${stat.dev}\x00${stat.ino}\x00${stat.isDirectory() ? 0 : stat.size}`);
|
|
33799
|
+
state.timestamps.set(child2, `${stat.mtimeNs}\x00${stat.ctimeNs}`);
|
|
33800
|
+
if (stat.isSymbolicLink())
|
|
33801
|
+
refuse3();
|
|
33802
|
+
if (stat.isDirectory())
|
|
33803
|
+
files.push(...walkSafeTree(child2, state, depth + 1, root));
|
|
33804
|
+
else if (stat.isFile())
|
|
33805
|
+
files.push(child2);
|
|
33806
|
+
else
|
|
33807
|
+
refuse3();
|
|
33808
|
+
}
|
|
33809
|
+
return files;
|
|
33810
|
+
}
|
|
33811
|
+
function treeSnapshot(cacheRoot) {
|
|
33812
|
+
const state = { entries: 0, members: [], signature: [], timestamps: new Map };
|
|
33813
|
+
const rootStat = lstatSync13(cacheRoot, { throwIfNoEntry: false, bigint: true });
|
|
33814
|
+
if (rootStat === undefined)
|
|
33815
|
+
return refuse3();
|
|
33816
|
+
if (!rootStat.isDirectory() || rootStat.isSymbolicLink() || realpathSync6(cacheRoot) !== cacheRoot)
|
|
33817
|
+
refuse3();
|
|
33818
|
+
state.signature.push(`.\x00${rootStat.mode}\x00${rootStat.dev}\x00${rootStat.ino}`);
|
|
33819
|
+
state.timestamps.set(cacheRoot, `${rootStat.mtimeNs}\x00${rootStat.ctimeNs}`);
|
|
33820
|
+
const files = walkSafeTree(cacheRoot, state);
|
|
33821
|
+
return { files, state };
|
|
33822
|
+
}
|
|
33823
|
+
function assertSameTree(before, after, semanticPaths) {
|
|
33824
|
+
if (canonical7([...before.members].sort()) !== canonical7([...after.members].sort()) || canonical7([...before.signature].sort()) !== canonical7([...after.signature].sort()))
|
|
33825
|
+
refuse3();
|
|
33826
|
+
for (const path of semanticPaths)
|
|
33827
|
+
if (before.timestamps.get(path) !== after.timestamps.get(path))
|
|
33828
|
+
refuse3();
|
|
33829
|
+
}
|
|
33830
|
+
function trackedBoundedRead(read) {
|
|
33831
|
+
const observed = new Map;
|
|
33832
|
+
const stableStat = (path, max) => {
|
|
33833
|
+
const stat = lstatSync13(path, { throwIfNoEntry: false, bigint: true });
|
|
33834
|
+
if (stat === undefined)
|
|
33835
|
+
return refuse3();
|
|
33836
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > BigInt(max) || realpathSync6(path) !== path)
|
|
33837
|
+
refuse3();
|
|
33838
|
+
return stat;
|
|
33839
|
+
};
|
|
33840
|
+
const guarded = (path) => {
|
|
33841
|
+
const max = path.endsWith(".codex-remote-plugin-install.json") ? MAX_RECEIPT_BYTES : MAX_DOCUMENT_BYTES;
|
|
33842
|
+
const before = stableStat(path, max);
|
|
33843
|
+
const text2 = read(path);
|
|
33844
|
+
if (Buffer.byteLength(text2, "utf8") > max)
|
|
33845
|
+
refuse3();
|
|
33846
|
+
const after = stableStat(path, max);
|
|
33847
|
+
if (after.dev !== before.dev || after.ino !== before.ino || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs)
|
|
33848
|
+
refuse3();
|
|
33849
|
+
const sha3 = createHash14("sha256").update(text2).digest("hex");
|
|
33850
|
+
const prior = observed.get(path);
|
|
33851
|
+
if (prior !== undefined && prior !== sha3)
|
|
33852
|
+
refuse3();
|
|
33853
|
+
observed.set(path, sha3);
|
|
33854
|
+
return text2;
|
|
33855
|
+
};
|
|
33856
|
+
return {
|
|
33857
|
+
read: guarded,
|
|
33858
|
+
verify: () => {
|
|
33859
|
+
for (const [path, expected] of observed) {
|
|
33860
|
+
const max = path.endsWith(".codex-remote-plugin-install.json") ? MAX_RECEIPT_BYTES : MAX_DOCUMENT_BYTES;
|
|
33861
|
+
const before = stableStat(path, max);
|
|
33862
|
+
const text2 = read(path);
|
|
33863
|
+
const after = stableStat(path, max);
|
|
33864
|
+
if (Buffer.byteLength(text2, "utf8") > max || createHash14("sha256").update(text2).digest("hex") !== expected || after.dev !== before.dev || after.ino !== before.ino || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs)
|
|
33865
|
+
refuse3();
|
|
33866
|
+
}
|
|
33867
|
+
},
|
|
33868
|
+
paths: () => [...observed.keys()]
|
|
33869
|
+
};
|
|
33870
|
+
}
|
|
33871
|
+
function controlProjection(controls, read) {
|
|
33872
|
+
const byPlugin = new Map;
|
|
33873
|
+
for (const control of controls) {
|
|
33874
|
+
if (!control || !identifier3(control.namespace) || typeof control.pluginId !== "string" || !/^[A-Za-z0-9_-]{1,64}@[A-Za-z0-9_-]{1,64}$/.test(control.pluginId) || typeof control.pluginParent !== "string" || !control.name.startsWith(`${control.namespace}:`) || !identifier3(control.name.slice(control.namespace.length + 1)))
|
|
33875
|
+
refuse3();
|
|
33876
|
+
if (!digest(control.manifestSha256) || control.appSha256 !== undefined && !digest(control.appSha256) || control.mcpSha256 !== undefined && !digest(control.mcpSha256) || control.remotePluginId !== undefined && (typeof control.remotePluginId !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId)))
|
|
33877
|
+
refuse3();
|
|
33878
|
+
const key = `${control.pluginId}\x00${control.pluginParent}`;
|
|
33879
|
+
const row = {
|
|
33880
|
+
pluginId: control.pluginId,
|
|
33881
|
+
namespace: control.namespace,
|
|
33882
|
+
pluginParent: control.pluginParent,
|
|
33883
|
+
...control.remotePluginId === undefined ? {} : { remotePluginId: control.remotePluginId },
|
|
33884
|
+
manifestSha256: control.manifestSha256,
|
|
33885
|
+
...control.appSha256 === undefined ? {} : { appSha256: control.appSha256 },
|
|
33886
|
+
...control.mcpSha256 === undefined ? {} : { mcpSha256: control.mcpSha256 }
|
|
33887
|
+
};
|
|
33888
|
+
if (row.remotePluginId !== undefined) {
|
|
33889
|
+
const receipt = join19(row.pluginParent, ".codex-remote-plugin-install.json"), stat = lstatSync13(receipt, { throwIfNoEntry: false });
|
|
33890
|
+
if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
|
|
33891
|
+
refuse3();
|
|
33892
|
+
try {
|
|
33893
|
+
const receiptText = read(receipt), value = JSON.parse(receiptText);
|
|
33894
|
+
if (!value || Array.isArray(value) || typeof value !== "object" || Object.keys(value).length !== 2 || value.schema_version !== 1 || value.remote_plugin_id !== row.remotePluginId)
|
|
33895
|
+
refuse3();
|
|
33896
|
+
row.receiptSha256 = createHash14("sha256").update(receiptText).digest("hex");
|
|
33897
|
+
} catch {
|
|
33898
|
+
refuse3();
|
|
33899
|
+
}
|
|
33900
|
+
}
|
|
33901
|
+
const previous = byPlugin.get(key);
|
|
33902
|
+
if (previous && canonical7(previous) !== canonical7(row))
|
|
33903
|
+
refuse3();
|
|
33904
|
+
byPlugin.set(key, row);
|
|
33905
|
+
}
|
|
33906
|
+
if (!byPlugin.size || byPlugin.size > 4096)
|
|
33907
|
+
refuse3();
|
|
33908
|
+
return [...byPlugin.values()].sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
|
|
33909
|
+
}
|
|
33910
|
+
function appOnlyParentProjection(cacheRoot, parents2, read) {
|
|
33911
|
+
if (!Array.isArray(parents2) || parents2.length > 4096)
|
|
33912
|
+
refuse3();
|
|
33913
|
+
const seen = new Set, rows = [];
|
|
33914
|
+
for (const parent of parents2) {
|
|
33915
|
+
if (!parent || Object.keys(parent).sort().join(",") !== "namespace,pluginId,pluginParent,remotePluginId,role" || parent.role !== "app-only" || !identifier3(parent.namespace) || !/^[A-Za-z0-9_-]{1,64}@[A-Za-z0-9_-]{1,64}$/.test(parent.pluginId) || typeof parent.pluginParent !== "string" || typeof parent.remotePluginId !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(parent.remotePluginId))
|
|
33916
|
+
refuse3();
|
|
33917
|
+
const parts = relative6(cacheRoot, parent.pluginParent).split(sep7);
|
|
33918
|
+
if (resolve12(parent.pluginParent) !== parent.pluginParent || parts.length !== 2 || parts.some((part) => !identifier3(part)) || parent.pluginId !== `${parent.namespace}@${parts[0]}` || parts[1] !== parent.namespace || seen.has(parent.pluginParent))
|
|
33919
|
+
refuse3();
|
|
33920
|
+
seen.add(parent.pluginParent);
|
|
33921
|
+
const parentStat = lstatSync13(parent.pluginParent, { throwIfNoEntry: false });
|
|
33922
|
+
if (!parentStat?.isDirectory() || parentStat.isSymbolicLink() || realpathSync6(parent.pluginParent) !== parent.pluginParent)
|
|
33923
|
+
refuse3();
|
|
33924
|
+
const receipt = join19(parent.pluginParent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync13(receipt, { throwIfNoEntry: false });
|
|
33925
|
+
if (!receiptStat?.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > 16384)
|
|
33926
|
+
refuse3();
|
|
33927
|
+
let receiptText = "";
|
|
33928
|
+
try {
|
|
33929
|
+
receiptText = read(receipt);
|
|
33930
|
+
hashNativeJsonControls(receiptText);
|
|
33931
|
+
const value = JSON.parse(receiptText);
|
|
33932
|
+
if (!value || Array.isArray(value) || typeof value !== "object" || Object.keys(value).length !== 2 || value.schema_version !== 1 || value.remote_plugin_id !== parent.remotePluginId)
|
|
33933
|
+
refuse3();
|
|
33934
|
+
} catch {
|
|
33935
|
+
refuse3();
|
|
33936
|
+
}
|
|
33937
|
+
const versions2 = readdirSync7(parent.pluginParent).filter((name) => name !== ".codex-remote-plugin-install.json");
|
|
33938
|
+
if (!versions2.length || versions2.length > 4096)
|
|
33939
|
+
refuse3();
|
|
33940
|
+
const materializations = versions2.map((version2) => {
|
|
33941
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2))
|
|
33942
|
+
refuse3();
|
|
33943
|
+
const root = join19(parent.pluginParent, version2), stat = lstatSync13(root, { throwIfNoEntry: false });
|
|
33944
|
+
if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync6(root) !== root)
|
|
33945
|
+
refuse3();
|
|
33946
|
+
const rootControls2 = readCodexPluginRootControls(root, read);
|
|
33947
|
+
if (rootControls2.namespace !== parent.namespace || !digest(rootControls2.manifestSha256) || !digest(rootControls2.appSha256) || rootControls2.mcpSha256 !== undefined && !digest(rootControls2.mcpSha256))
|
|
33948
|
+
refuse3();
|
|
33949
|
+
return {
|
|
33950
|
+
manifestSha256: rootControls2.manifestSha256,
|
|
33951
|
+
appSha256: rootControls2.appSha256,
|
|
33952
|
+
...rootControls2.mcpSha256 === undefined ? {} : { mcpSha256: rootControls2.mcpSha256 }
|
|
33953
|
+
};
|
|
33954
|
+
});
|
|
33955
|
+
const first = materializations[0];
|
|
33956
|
+
if (materializations.some((item) => canonical7(item) !== canonical7(first)))
|
|
33957
|
+
refuse3();
|
|
33958
|
+
rows.push({ ...parent, receiptSha256: createHash14("sha256").update(receiptText).digest("hex"), ...first });
|
|
33959
|
+
}
|
|
33960
|
+
return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
|
|
33961
|
+
}
|
|
33962
|
+
function assertParents(cacheRoot, controls, appOnlyParents, files, members2) {
|
|
33963
|
+
const expected = new Set;
|
|
33964
|
+
const expectedMarkets = new Set;
|
|
33965
|
+
for (const control of [...controls, ...appOnlyParents]) {
|
|
33966
|
+
const rel = relative6(cacheRoot, control.pluginParent), parts = rel.split(sep7);
|
|
33967
|
+
if (resolve12(control.pluginParent) !== control.pluginParent || parts.length !== 2 || parts.some((part) => !identifier3(part)) || control.pluginId !== `${control.namespace}@${parts[0]}` || parts[1] !== control.namespace)
|
|
33968
|
+
refuse3();
|
|
33969
|
+
expected.add(control.pluginParent);
|
|
33970
|
+
expectedMarkets.add(parts[0]);
|
|
33971
|
+
}
|
|
33972
|
+
const actual = new Set;
|
|
33973
|
+
for (const file2 of files) {
|
|
33974
|
+
const rel = relative6(cacheRoot, file2), parts = rel.split(sep7);
|
|
33975
|
+
if (parts.length < 3 || !identifier3(parts[0]) || !identifier3(parts[1]))
|
|
33976
|
+
refuse3();
|
|
33977
|
+
actual.add(join19(cacheRoot, parts[0], parts[1]));
|
|
33978
|
+
}
|
|
33979
|
+
const actualMarkets = readdirSync7(cacheRoot);
|
|
33980
|
+
if (actualMarkets.length !== expectedMarkets.size || actualMarkets.some((market) => !expectedMarkets.has(market)))
|
|
33981
|
+
refuse3();
|
|
33982
|
+
for (const market of actualMarkets) {
|
|
33983
|
+
const marketPath = join19(cacheRoot, market), marketStat = lstatSync13(marketPath);
|
|
33984
|
+
if (!identifier3(market) || !marketStat.isDirectory() || marketStat.isSymbolicLink())
|
|
33985
|
+
refuse3();
|
|
33986
|
+
for (const plugin of readdirSync7(marketPath)) {
|
|
33987
|
+
const parent = join19(marketPath, plugin), stat = lstatSync13(parent);
|
|
33988
|
+
if (!identifier3(plugin) || !stat.isDirectory() || stat.isSymbolicLink())
|
|
33989
|
+
refuse3();
|
|
33990
|
+
actual.add(parent);
|
|
33991
|
+
}
|
|
33992
|
+
}
|
|
33993
|
+
if (actual.size !== expected.size || [...actual].some((parent) => !expected.has(parent)))
|
|
33994
|
+
refuse3();
|
|
33995
|
+
for (const member of members2) {
|
|
33996
|
+
const parts = relative6(cacheRoot, member).split(sep7);
|
|
33997
|
+
if (parts.length < 3)
|
|
33998
|
+
continue;
|
|
33999
|
+
const parent = join19(cacheRoot, parts[0], parts[1]);
|
|
34000
|
+
const root = join19(parent, parts[2]);
|
|
34001
|
+
const base = parts[parts.length - 1];
|
|
34002
|
+
if (parts.includes(".codex-plugin") && (parts.length < 4 || parts[3] !== ".codex-plugin" || (parts.length === 4 ? base !== ".codex-plugin" : parts.length !== 5 || base !== "plugin.json")))
|
|
34003
|
+
refuse3();
|
|
34004
|
+
if ([".app.json", ".mcp.json"].includes(base) && (parts.length !== 4 || dirname13(member) !== root))
|
|
34005
|
+
refuse3();
|
|
34006
|
+
if (parts.includes("hooks"))
|
|
34007
|
+
refuse3();
|
|
34008
|
+
}
|
|
34009
|
+
}
|
|
34010
|
+
function assertSkillMaterializations(cacheRoot, controls, files, read) {
|
|
34011
|
+
const expectedByRoot = new Map;
|
|
34012
|
+
const byParent = new Map;
|
|
34013
|
+
for (const control of controls) {
|
|
34014
|
+
const group = byParent.get(control.pluginParent) ?? [];
|
|
34015
|
+
group.push(control);
|
|
34016
|
+
byParent.set(control.pluginParent, group);
|
|
34017
|
+
}
|
|
34018
|
+
for (const [parent, rows] of byParent) {
|
|
34019
|
+
const first = rows[0];
|
|
34020
|
+
const expectedControls = {
|
|
34021
|
+
namespace: first.namespace,
|
|
34022
|
+
manifestSha256: first.manifestSha256,
|
|
34023
|
+
...first.appSha256 === undefined ? {} : { appSha256: first.appSha256 },
|
|
34024
|
+
...first.mcpSha256 === undefined ? {} : { mcpSha256: first.mcpSha256 }
|
|
34025
|
+
};
|
|
34026
|
+
if (rows.some((row) => canonical7({
|
|
34027
|
+
namespace: row.namespace,
|
|
34028
|
+
manifestSha256: row.manifestSha256,
|
|
34029
|
+
...row.appSha256 === undefined ? {} : { appSha256: row.appSha256 },
|
|
34030
|
+
...row.mcpSha256 === undefined ? {} : { mcpSha256: row.mcpSha256 }
|
|
34031
|
+
}) !== canonical7(expectedControls)))
|
|
34032
|
+
refuse3();
|
|
34033
|
+
const expectedNames = new Set(rows.map((row) => row.name));
|
|
34034
|
+
if (expectedNames.size !== rows.length)
|
|
34035
|
+
refuse3();
|
|
34036
|
+
const entries = readdirSync7(parent);
|
|
34037
|
+
let roots = 0;
|
|
34038
|
+
for (const entry of entries) {
|
|
34039
|
+
if (entry === ".codex-remote-plugin-install.json")
|
|
34040
|
+
continue;
|
|
34041
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(entry))
|
|
34042
|
+
refuse3();
|
|
34043
|
+
const root = join19(parent, entry), stat = lstatSync13(root, { throwIfNoEntry: false });
|
|
34044
|
+
if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync6(root) !== root)
|
|
34045
|
+
refuse3();
|
|
34046
|
+
const actualControls = readCodexPluginRootControls(root, read);
|
|
34047
|
+
const actualProjection = {
|
|
34048
|
+
namespace: actualControls.namespace,
|
|
34049
|
+
manifestSha256: actualControls.manifestSha256,
|
|
34050
|
+
...actualControls.appSha256 === undefined ? {} : { appSha256: actualControls.appSha256 },
|
|
34051
|
+
...actualControls.mcpSha256 === undefined ? {} : { mcpSha256: actualControls.mcpSha256 }
|
|
34052
|
+
};
|
|
34053
|
+
if (canonical7(actualProjection) !== canonical7(expectedControls))
|
|
34054
|
+
refuse3();
|
|
34055
|
+
const key = `${first.pluginId}\x00${root}`;
|
|
34056
|
+
expectedByRoot.set(key, expectedNames);
|
|
34057
|
+
roots++;
|
|
34058
|
+
}
|
|
34059
|
+
if (!roots)
|
|
34060
|
+
refuse3();
|
|
34061
|
+
}
|
|
34062
|
+
for (const file2 of files) {
|
|
34063
|
+
if (!file2.endsWith(`${sep7}SKILL.md`))
|
|
34064
|
+
continue;
|
|
34065
|
+
const rel = relative6(cacheRoot, file2).split(sep7);
|
|
34066
|
+
if (rel.length < 4)
|
|
34067
|
+
refuse3();
|
|
34068
|
+
const parent = join19(cacheRoot, rel[0], rel[1]);
|
|
34069
|
+
if (!byParent.has(parent) || !expectedByRoot.has(`${byParent.get(parent)[0].pluginId}\x00${join19(parent, rel[2])}`))
|
|
34070
|
+
refuse3();
|
|
34071
|
+
}
|
|
34072
|
+
return expectedByRoot;
|
|
34073
|
+
}
|
|
34074
|
+
function effectiveDisabledRules(skill, rules) {
|
|
34075
|
+
if (!Array.isArray(rules))
|
|
34076
|
+
refuse3();
|
|
34077
|
+
const matching = [];
|
|
34078
|
+
for (const candidate of rules) {
|
|
34079
|
+
if (!plainObject(candidate) || typeof candidate.enabled !== "boolean")
|
|
34080
|
+
refuse3();
|
|
34081
|
+
const rule = candidate;
|
|
34082
|
+
const byName = typeof rule.name === "string" && rule.name.trim() === skill.name;
|
|
34083
|
+
let byPath = false;
|
|
34084
|
+
if (typeof rule.path === "string") {
|
|
34085
|
+
if (resolve12(rule.path) !== rule.path)
|
|
34086
|
+
refuse3();
|
|
34087
|
+
let canonicalPath2;
|
|
34088
|
+
try {
|
|
34089
|
+
canonicalPath2 = realpathSync6(rule.path);
|
|
34090
|
+
} catch {
|
|
34091
|
+
canonicalPath2 = resolve12(rule.path);
|
|
34092
|
+
}
|
|
34093
|
+
if (canonicalPath2 === skill.path && rule.path !== canonicalPath2)
|
|
34094
|
+
refuse3();
|
|
34095
|
+
byPath = canonicalPath2 === skill.path;
|
|
34096
|
+
}
|
|
34097
|
+
if ((byName || byPath) && rule.enabled !== false)
|
|
34098
|
+
refuse3();
|
|
34099
|
+
if (byName)
|
|
34100
|
+
matching.push(`name:${skill.name}=disabled`);
|
|
34101
|
+
}
|
|
34102
|
+
if (!isCodexNativeSkillDisabled(skill, rules))
|
|
34103
|
+
refuse3();
|
|
34104
|
+
if (!matching.length)
|
|
34105
|
+
refuse3();
|
|
34106
|
+
return [...new Set(matching)].sort();
|
|
34107
|
+
}
|
|
34108
|
+
function captureCodexSemanticCacheWitness(options) {
|
|
34109
|
+
const tracked = trackedBoundedRead(options.read ?? defaultRead);
|
|
34110
|
+
const read = tracked.read;
|
|
34111
|
+
try {
|
|
34112
|
+
assertCanonicalDirectory(options.cacheRoot);
|
|
34113
|
+
const initialTree = treeSnapshot(options.cacheRoot);
|
|
34114
|
+
const controls = controlProjection(options.controls, read);
|
|
34115
|
+
const appOnlyParents = appOnlyParentProjection(options.cacheRoot, options.appOnlyParents ?? [], read);
|
|
34116
|
+
const skillParents = new Set(controls.map((control) => control.pluginParent));
|
|
34117
|
+
if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)))
|
|
34118
|
+
refuse3();
|
|
34119
|
+
if (!reviewedCodexPluginCapabilitiesUnchanged(options.cacheRoot, options.controls, read, options.rules))
|
|
34120
|
+
refuse3();
|
|
34121
|
+
const files = initialTree.files;
|
|
34122
|
+
assertParents(options.cacheRoot, controls, appOnlyParents, files, initialTree.state.members);
|
|
34123
|
+
const expectedNamesByRoot = assertSkillMaterializations(options.cacheRoot, options.controls, files, read);
|
|
34124
|
+
const docs = files.filter((path) => path.endsWith(`${sep7}SKILL.md`));
|
|
34125
|
+
const skillRows = [];
|
|
34126
|
+
const rootNames = new Map;
|
|
34127
|
+
for (const document2 of docs) {
|
|
34128
|
+
const rel = relative6(options.cacheRoot, document2).split(sep7);
|
|
34129
|
+
if (appOnlyParents.some((parent) => parent.pluginParent === join19(options.cacheRoot, rel[0], rel[1])))
|
|
34130
|
+
refuse3();
|
|
34131
|
+
const documentStat = lstatSync13(document2, { throwIfNoEntry: false });
|
|
34132
|
+
if (!documentStat?.isFile() || documentStat.isSymbolicLink() || documentStat.size > MAX_DOCUMENT_BYTES)
|
|
34133
|
+
refuse3();
|
|
34134
|
+
const identity4 = classifyCodexPluginCacheDocument(document2, options.cacheRoot, read);
|
|
34135
|
+
if (identity4 === null)
|
|
34136
|
+
return refuse3();
|
|
34137
|
+
const row = {
|
|
34138
|
+
name: identity4.name,
|
|
34139
|
+
pluginId: identity4.pluginId,
|
|
34140
|
+
namespace: identity4.namespace,
|
|
34141
|
+
pluginParent: identity4.pluginParent,
|
|
34142
|
+
disabledRules: effectiveDisabledRules({ name: identity4.name, path: document2, enabled: true, pluginId: identity4.pluginId }, options.rules)
|
|
34143
|
+
};
|
|
34144
|
+
const root = identity4.root;
|
|
34145
|
+
const rootKey = `${row.pluginId}\x00${root}`;
|
|
34146
|
+
const names = rootNames.get(rootKey) ?? new Set;
|
|
34147
|
+
if (names.has(row.name))
|
|
34148
|
+
refuse3();
|
|
34149
|
+
names.add(row.name);
|
|
34150
|
+
rootNames.set(rootKey, names);
|
|
34151
|
+
skillRows.push(row);
|
|
34152
|
+
}
|
|
34153
|
+
if (!skillRows.length || skillRows.length > 4096)
|
|
34154
|
+
refuse3();
|
|
34155
|
+
const uniqueNames = new Set(skillRows.map((row) => `${row.pluginId}\x00${row.name}`));
|
|
34156
|
+
const reviewedNames = new Set(options.controls.map((control) => `${control.pluginId}\x00${control.name}`));
|
|
34157
|
+
if (reviewedNames.size !== options.controls.length || new Set(options.controls.map((control) => control.name)).size !== options.controls.length)
|
|
34158
|
+
refuse3();
|
|
34159
|
+
if (uniqueNames.size !== reviewedNames.size || [...uniqueNames].some((name) => !reviewedNames.has(name)))
|
|
34160
|
+
refuse3();
|
|
34161
|
+
if (new Set(skillRows.map((row) => row.name)).size !== reviewedNames.size)
|
|
34162
|
+
refuse3();
|
|
34163
|
+
const byPlugin = new Map;
|
|
34164
|
+
for (const row of skillRows) {
|
|
34165
|
+
const names = byPlugin.get(row.pluginId) ?? new Set;
|
|
34166
|
+
names.add(row.name);
|
|
34167
|
+
byPlugin.set(row.pluginId, names);
|
|
34168
|
+
}
|
|
34169
|
+
for (const [key, expected] of expectedNamesByRoot) {
|
|
34170
|
+
const names = rootNames.get(key);
|
|
34171
|
+
if (!names || names.size !== expected.size || [...expected].some((name) => !names.has(name)))
|
|
34172
|
+
refuse3();
|
|
34173
|
+
}
|
|
34174
|
+
if (rootNames.size !== expectedNamesByRoot.size)
|
|
34175
|
+
refuse3();
|
|
34176
|
+
for (const [key, names] of rootNames) {
|
|
34177
|
+
const pluginId = key.slice(0, key.indexOf("\x00")), expected = byPlugin.get(pluginId);
|
|
34178
|
+
if (!expected || names.size !== expected.size || [...expected].some((name) => !names.has(name)))
|
|
34179
|
+
refuse3();
|
|
34180
|
+
}
|
|
34181
|
+
const skillsByName = new Map;
|
|
34182
|
+
for (const row of skillRows) {
|
|
34183
|
+
const key = `${row.pluginId}\x00${row.name}`, prior = skillsByName.get(key);
|
|
34184
|
+
if (prior && canonical7(prior.disabledRules) !== canonical7(row.disabledRules))
|
|
34185
|
+
refuse3();
|
|
34186
|
+
if (!prior)
|
|
34187
|
+
skillsByName.set(key, row);
|
|
34188
|
+
}
|
|
34189
|
+
const uniqueSkills = [...skillsByName.values()];
|
|
34190
|
+
uniqueSkills.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.name.localeCompare(b.name));
|
|
34191
|
+
tracked.verify();
|
|
34192
|
+
const finalTree = treeSnapshot(options.cacheRoot);
|
|
34193
|
+
assertSameTree(initialTree.state, finalTree.state, tracked.paths());
|
|
34194
|
+
const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, skills: uniqueSkills };
|
|
34195
|
+
return { ...completeProjection, sha256: hash3(completeProjection) };
|
|
34196
|
+
} catch (error61) {
|
|
34197
|
+
if (error61 instanceof Error && error61.message.startsWith("CODEX_SEMANTIC_CACHE_WITNESS_UNSUPPORTED"))
|
|
34198
|
+
throw error61;
|
|
34199
|
+
return refuse3();
|
|
34200
|
+
}
|
|
34201
|
+
}
|
|
34202
|
+
function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
|
|
34203
|
+
assertWitnessShape(previous);
|
|
34204
|
+
assertWitnessShape(current);
|
|
34205
|
+
const priorProjection = {
|
|
34206
|
+
schema: previous.schema,
|
|
34207
|
+
cacheRoot: previous.cacheRoot,
|
|
34208
|
+
controls: previous.controls,
|
|
34209
|
+
appOnlyParents: previous.appOnlyParents,
|
|
34210
|
+
skills: previous.skills
|
|
34211
|
+
};
|
|
34212
|
+
const currentProjection = {
|
|
34213
|
+
schema: current.schema,
|
|
34214
|
+
cacheRoot: current.cacheRoot,
|
|
34215
|
+
controls: current.controls,
|
|
34216
|
+
appOnlyParents: current.appOnlyParents,
|
|
34217
|
+
skills: current.skills
|
|
34218
|
+
};
|
|
34219
|
+
if (hash3(priorProjection) !== previous.sha256 || hash3(currentProjection) !== current.sha256 || canonical7(priorProjection) !== canonical7(currentProjection))
|
|
34220
|
+
refuse3();
|
|
34221
|
+
}
|
|
34222
|
+
var CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA = "hasna.codex-semantic-cache-witness.v1", identifier3 = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(value), digest = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), refuse3 = () => {
|
|
34223
|
+
throw new Error("CODEX_SEMANTIC_CACHE_WITNESS_UNSUPPORTED: review Codex plugin cache identity and controls");
|
|
34224
|
+
}, MAX_DOCUMENT_BYTES, MAX_RECEIPT_BYTES;
|
|
34225
|
+
var init_codex_semantic_cache_witness = __esm(() => {
|
|
34226
|
+
init_claude_settings_witness();
|
|
34227
|
+
init_codex_plugin_skill_controls();
|
|
34228
|
+
init_codex_native_skill_catalog();
|
|
34229
|
+
MAX_DOCUMENT_BYTES = 1024 * 1024;
|
|
34230
|
+
MAX_RECEIPT_BYTES = 16 * 1024;
|
|
34231
|
+
});
|
|
34232
|
+
|
|
33491
34233
|
// src/lib/managed-policy.ts
|
|
33492
|
-
import { closeSync as
|
|
33493
|
-
import { join as
|
|
34234
|
+
import { closeSync as closeSync11, constants as constants8, fstatSync as fstatSync11, lstatSync as lstatSync14, openSync as openSync11, readSync as readSync6 } from "fs";
|
|
34235
|
+
import { join as join20 } from "path";
|
|
33494
34236
|
function refusal(reason2 = "is unreadable") {
|
|
33495
34237
|
return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason2}; refusing legacy fallback.`);
|
|
33496
34238
|
}
|
|
@@ -33515,17 +34257,17 @@ function parseManagedSkillPolicy(text2) {
|
|
|
33515
34257
|
return value;
|
|
33516
34258
|
}
|
|
33517
34259
|
function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
|
|
33518
|
-
const path =
|
|
34260
|
+
const path = join20(dataDir2, "agent-policy.json");
|
|
33519
34261
|
let descriptor, observed = false;
|
|
33520
34262
|
try {
|
|
33521
|
-
const stat =
|
|
34263
|
+
const stat = lstatSync14(path);
|
|
33522
34264
|
observed = true;
|
|
33523
34265
|
if (!stat.isFile() || stat.isSymbolicLink())
|
|
33524
34266
|
throw refusal();
|
|
33525
34267
|
if (stat.size > AGENT_POLICY_LIMITS.bytes)
|
|
33526
34268
|
throw refusal("exceeds its size limit");
|
|
33527
|
-
descriptor =
|
|
33528
|
-
const opened =
|
|
34269
|
+
descriptor = openSync11(path, constants8.O_RDONLY | (constants8.O_NOFOLLOW ?? 0) | constants8.O_NONBLOCK);
|
|
34270
|
+
const opened = fstatSync11(descriptor);
|
|
33529
34271
|
if (!opened.isFile())
|
|
33530
34272
|
throw refusal();
|
|
33531
34273
|
if (opened.size > AGENT_POLICY_LIMITS.bytes)
|
|
@@ -33533,7 +34275,7 @@ function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
|
|
|
33533
34275
|
const bytes = Buffer.allocUnsafe(AGENT_POLICY_LIMITS.bytes + 1);
|
|
33534
34276
|
let length = 0;
|
|
33535
34277
|
while (length < bytes.length) {
|
|
33536
|
-
const count =
|
|
34278
|
+
const count = readSync6(descriptor, bytes, length, bytes.length - length, null);
|
|
33537
34279
|
if (count === 0)
|
|
33538
34280
|
break;
|
|
33539
34281
|
length += count;
|
|
@@ -33544,7 +34286,7 @@ function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
|
|
|
33544
34286
|
return { text: text2, value: parseManagedSkillPolicy(text2) };
|
|
33545
34287
|
} catch (error61) {
|
|
33546
34288
|
if (error61.code === "ENOENT" && !observed) {
|
|
33547
|
-
if (
|
|
34289
|
+
if (lstatSync14(path, { throwIfNoEntry: false }) === undefined)
|
|
33548
34290
|
return null;
|
|
33549
34291
|
}
|
|
33550
34292
|
if (error61 instanceof SkillSelectionError)
|
|
@@ -33552,7 +34294,7 @@ function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
|
|
|
33552
34294
|
throw refusal();
|
|
33553
34295
|
} finally {
|
|
33554
34296
|
if (descriptor !== undefined)
|
|
33555
|
-
|
|
34297
|
+
closeSync11(descriptor);
|
|
33556
34298
|
}
|
|
33557
34299
|
}
|
|
33558
34300
|
function readManagedSkillPolicy(dataDir2 = getDataDirReadOnly()) {
|
|
@@ -33568,7 +34310,7 @@ var init_managed_policy = __esm(() => {
|
|
|
33568
34310
|
});
|
|
33569
34311
|
|
|
33570
34312
|
// src/lib/agent-bridge.ts
|
|
33571
|
-
import { createHash as
|
|
34313
|
+
import { createHash as createHash15 } from "crypto";
|
|
33572
34314
|
var CLI_BRIDGE_VERSION = 1, markdown = `---
|
|
33573
34315
|
name: skills-cli
|
|
33574
34316
|
description: Discover, load, author, and run versioned skills through the Skills CLI when a task needs reusable instructions or an executable skill.
|
|
@@ -33584,7 +34326,7 @@ Use the Skills CLI as the skill authority for this station.
|
|
|
33584
34326
|
This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
|
|
33585
34327
|
`, CLI_BRIDGE_DIGEST, CLI_BRIDGE_FILES;
|
|
33586
34328
|
var init_agent_bridge = __esm(() => {
|
|
33587
|
-
CLI_BRIDGE_DIGEST =
|
|
34329
|
+
CLI_BRIDGE_DIGEST = createHash15("sha256").update(markdown).digest("hex");
|
|
33588
34330
|
CLI_BRIDGE_FILES = Object.freeze({
|
|
33589
34331
|
"SKILL.md": markdown,
|
|
33590
34332
|
".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
|
|
@@ -33595,18 +34337,91 @@ var init_agent_bridge = __esm(() => {
|
|
|
33595
34337
|
// src/lib/agent-codex.ts
|
|
33596
34338
|
var init_agent_codex = () => {};
|
|
33597
34339
|
|
|
34340
|
+
// src/lib/hook-diagnostics.ts
|
|
34341
|
+
var CONTEXT_ERROR_CODES, HOOK_REFUSAL_REASON_CODES, OPTIONAL_CONTEXT_FAILURES;
|
|
34342
|
+
var init_hook_diagnostics = __esm(() => {
|
|
34343
|
+
CONTEXT_ERROR_CODES = new Set([
|
|
34344
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
34345
|
+
"SKILLS_API_UNAVAILABLE",
|
|
34346
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
34347
|
+
"SKILLS_API_FORBIDDEN",
|
|
34348
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
34349
|
+
"SESSION_WRITE_BUSY",
|
|
34350
|
+
"SKILLS_CONTEXT_FAILED",
|
|
34351
|
+
"SKILLS_STORAGE_FULL",
|
|
34352
|
+
"PROFILE_LOCK_MISMATCH",
|
|
34353
|
+
"PROFILE_IDENTITY_MISMATCH",
|
|
34354
|
+
"PROFILE_APPLIED_REPORT_FAILED",
|
|
34355
|
+
"CACHED_AUTHORITY_REQUIRED",
|
|
34356
|
+
"CACHED_PROFILE_EXPIRED",
|
|
34357
|
+
"CACHED_PROFILE_MISSING",
|
|
34358
|
+
"CACHED_BUNDLE_MISSING",
|
|
34359
|
+
"INVALID_CONTEXT_INPUT",
|
|
34360
|
+
"CONTEXT_INPUT_TOO_LARGE",
|
|
34361
|
+
"INVALID_CONTEXT_BUDGET",
|
|
34362
|
+
"INVALID_SELECTION",
|
|
34363
|
+
"INVALID_SELECTION_ALIASES",
|
|
34364
|
+
"INVALID_RECEIPT",
|
|
34365
|
+
"INVALID_CACHE_FILE",
|
|
34366
|
+
"UNSAFE_CACHE_PATH",
|
|
34367
|
+
"RECEIPT_TOO_LARGE",
|
|
34368
|
+
"BUNDLE_DIGEST_MISMATCH",
|
|
34369
|
+
"BUNDLE_IDENTITY_MISMATCH",
|
|
34370
|
+
"BUNDLE_UNAVAILABLE",
|
|
34371
|
+
"BUNDLE_TOO_LARGE",
|
|
34372
|
+
"SKILL_NOT_SELECTED",
|
|
34373
|
+
"INVALID_SESSION",
|
|
34374
|
+
"SESSION_PARENT_NOT_FOUND",
|
|
34375
|
+
"SESSION_PARENT_MISMATCH",
|
|
34376
|
+
"SESSION_NOT_FOUND",
|
|
34377
|
+
"SESSION_GENERATION_CHANGED",
|
|
34378
|
+
"SESSION_GENERATION_EXHAUSTED",
|
|
34379
|
+
"SESSION_PARENT_CHANGED",
|
|
34380
|
+
"SESSION_RECEIPT_CHANGED",
|
|
34381
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
34382
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
34383
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
34384
|
+
"SESSION_RECONCILIATION_INCOMPLETE",
|
|
34385
|
+
"SESSION_WRITE_LOCKED",
|
|
34386
|
+
"SESSION_WRITE_LOCK_CHANGED",
|
|
34387
|
+
"SESSION_LOCK_RECOVERY_INCOMPLETE"
|
|
34388
|
+
]);
|
|
34389
|
+
HOOK_REFUSAL_REASON_CODES = Object.freeze([
|
|
34390
|
+
...CONTEXT_ERROR_CODES,
|
|
34391
|
+
"SKILLS_HOOK_FAILED",
|
|
34392
|
+
"SKILLS_HOOK_TIMEOUT",
|
|
34393
|
+
"SKILLS_HOOK_INVALID_RESPONSE"
|
|
34394
|
+
]);
|
|
34395
|
+
OPTIONAL_CONTEXT_FAILURES = new Set([
|
|
34396
|
+
"SKILLS_API_CREDENTIAL_UNAVAILABLE",
|
|
34397
|
+
"SKILLS_API_UNAVAILABLE",
|
|
34398
|
+
"SKILLS_API_UNAUTHORIZED",
|
|
34399
|
+
"SKILLS_API_FORBIDDEN",
|
|
34400
|
+
"SKILLS_API_RESOURCE_UNAVAILABLE",
|
|
34401
|
+
"CACHED_PROFILE_EXPIRED",
|
|
34402
|
+
"CACHED_PROFILE_MISSING",
|
|
34403
|
+
"CACHED_BUNDLE_MISSING",
|
|
34404
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
34405
|
+
"SESSION_RENEWAL_TIMEOUT",
|
|
34406
|
+
"SESSION_WRITE_BUSY",
|
|
34407
|
+
"SESSION_PARENT_CHANGED",
|
|
34408
|
+
"SESSION_CONTEXT_CONFLICT",
|
|
34409
|
+
"SKILLS_HOOK_TIMEOUT"
|
|
34410
|
+
]);
|
|
34411
|
+
});
|
|
34412
|
+
|
|
33598
34413
|
// src/lib/agent-sumi.ts
|
|
33599
34414
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
33600
|
-
import { accessSync, constants as
|
|
33601
|
-
import { delimiter, isAbsolute as
|
|
34415
|
+
import { accessSync, constants as constants9, existsSync as existsSync8, lstatSync as lstatSync15, realpathSync as realpathSync7, statSync as statSync7 } from "fs";
|
|
34416
|
+
import { delimiter, isAbsolute as isAbsolute12, join as join21, resolve as resolve13 } from "path";
|
|
33602
34417
|
function recordWithKeys(value, keys) {
|
|
33603
34418
|
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
33604
34419
|
}
|
|
33605
34420
|
function absolutePath3(value) {
|
|
33606
|
-
return typeof value === "string" && value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) &&
|
|
34421
|
+
return typeof value === "string" && value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute12(value) && resolve13(value) === value;
|
|
33607
34422
|
}
|
|
33608
34423
|
function absoluteInput(value) {
|
|
33609
|
-
return value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) &&
|
|
34424
|
+
return value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute12(value);
|
|
33610
34425
|
}
|
|
33611
34426
|
function resolveSumiPathsCommand(pathValue) {
|
|
33612
34427
|
if (!pathValue)
|
|
@@ -33614,9 +34429,9 @@ function resolveSumiPathsCommand(pathValue) {
|
|
|
33614
34429
|
for (const directory of pathValue.split(delimiter)) {
|
|
33615
34430
|
if (!directory || /[\x00-\x1f\x7f]/.test(directory))
|
|
33616
34431
|
continue;
|
|
33617
|
-
const candidate =
|
|
34432
|
+
const candidate = resolve13(directory, "sumi-paths");
|
|
33618
34433
|
try {
|
|
33619
|
-
accessSync(candidate,
|
|
34434
|
+
accessSync(candidate, constants9.X_OK);
|
|
33620
34435
|
if (statSync7(candidate).isFile())
|
|
33621
34436
|
return candidate;
|
|
33622
34437
|
} catch {}
|
|
@@ -33626,7 +34441,7 @@ function resolveSumiPathsCommand(pathValue) {
|
|
|
33626
34441
|
function validPathPlan(value, home, cwd) {
|
|
33627
34442
|
if (!recordWithKeys(value, ["schemaVersion", "kind", "home", "cwd", "roots", "legacyRoots", "configFiles", "skillRoots"]))
|
|
33628
34443
|
return false;
|
|
33629
|
-
if (value.schemaVersion !== 1 || value.kind !== "sumi-paths" || value.home !==
|
|
34444
|
+
if (value.schemaVersion !== 1 || value.kind !== "sumi-paths" || value.home !== resolve13(home) || value.cwd !== resolve13(cwd))
|
|
33630
34445
|
return false;
|
|
33631
34446
|
if (!recordWithKeys(value.roots, SUMI_PATH_KEYS) || !recordWithKeys(value.legacyRoots, SUMI_PATH_KEYS))
|
|
33632
34447
|
return false;
|
|
@@ -33648,11 +34463,11 @@ function validPathPlan(value, home, cwd) {
|
|
|
33648
34463
|
const legacyConfigRoot = value.legacyRoots.config;
|
|
33649
34464
|
if (typeof configRoot !== "string" || legacyConfigRoot !== null && typeof legacyConfigRoot !== "string")
|
|
33650
34465
|
return false;
|
|
33651
|
-
if (value.configFiles.canonical !==
|
|
34466
|
+
if (value.configFiles.canonical !== join21(configRoot, "sumi.json") || value.skillRoots.canonical !== join21(configRoot, "skills"))
|
|
33652
34467
|
return false;
|
|
33653
34468
|
if (legacyConfigRoot === null)
|
|
33654
34469
|
return value.configFiles.legacy === null && value.skillRoots.legacy === null;
|
|
33655
|
-
return value.configFiles.legacy ===
|
|
34470
|
+
return value.configFiles.legacy === join21(legacyConfigRoot, "sumi.json") && value.skillRoots.legacy === join21(legacyConfigRoot, "skills");
|
|
33656
34471
|
}
|
|
33657
34472
|
function sumiPathPlan(home, cwd = process.cwd(), env = process.env) {
|
|
33658
34473
|
for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
|
|
@@ -33662,7 +34477,7 @@ function sumiPathPlan(home, cwd = process.cwd(), env = process.env) {
|
|
|
33662
34477
|
throw new SumiPathResolverError("SUMI_PATH_INPUT_REFUSED");
|
|
33663
34478
|
const runtimePath = process.env.PATH ?? "";
|
|
33664
34479
|
const executable = resolveSumiPathsCommand(runtimePath);
|
|
33665
|
-
if (!executable || !
|
|
34480
|
+
if (!executable || !isAbsolute12(executable))
|
|
33666
34481
|
throw new SumiPathResolverError("SUMI_PATH_RESOLVER_UNAVAILABLE");
|
|
33667
34482
|
const childEnv = { PATH: runtimePath };
|
|
33668
34483
|
for (const key of SUMI_PATH_SELECTORS)
|
|
@@ -33670,8 +34485,8 @@ function sumiPathPlan(home, cwd = process.cwd(), env = process.env) {
|
|
|
33670
34485
|
childEnv[key] = env[key];
|
|
33671
34486
|
let child2;
|
|
33672
34487
|
try {
|
|
33673
|
-
child2 = spawnSync4(executable, ["--json", "--home",
|
|
33674
|
-
cwd:
|
|
34488
|
+
child2 = spawnSync4(executable, ["--json", "--home", resolve13(home), "--cwd", resolve13(cwd)], {
|
|
34489
|
+
cwd: resolve13(cwd),
|
|
33675
34490
|
env: childEnv,
|
|
33676
34491
|
encoding: "buffer",
|
|
33677
34492
|
timeout: 3000,
|
|
@@ -33700,17 +34515,17 @@ function sumiPathPlan(home, cwd = process.cwd(), env = process.env) {
|
|
|
33700
34515
|
return result;
|
|
33701
34516
|
}
|
|
33702
34517
|
function selectedConfigDirectory(plan) {
|
|
33703
|
-
const
|
|
34518
|
+
const canonical8 = plan.roots.config, legacy = plan.legacyRoots.config;
|
|
33704
34519
|
if (legacy === null)
|
|
33705
|
-
return existsSync8(
|
|
33706
|
-
const canonicalStat =
|
|
33707
|
-
if (canonicalStat && legacyStat &&
|
|
34520
|
+
return existsSync8(canonical8) ? realpathSync7(canonical8) : canonical8;
|
|
34521
|
+
const canonicalStat = lstatSync15(canonical8, { throwIfNoEntry: false }), legacyStat = lstatSync15(legacy, { throwIfNoEntry: false });
|
|
34522
|
+
if (canonicalStat && legacyStat && realpathSync7(canonical8) !== realpathSync7(legacy))
|
|
33708
34523
|
throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
|
|
33709
|
-
const selected = canonicalStat ?
|
|
34524
|
+
const selected = canonicalStat ? canonical8 : legacyStat ? legacy : canonical8;
|
|
33710
34525
|
if (!existsSync8(selected))
|
|
33711
34526
|
return selected;
|
|
33712
|
-
const physical =
|
|
33713
|
-
if ((canonicalStat?.isSymbolicLink() || legacyStat?.isSymbolicLink()) && ![
|
|
34527
|
+
const physical = realpathSync7(selected);
|
|
34528
|
+
if ((canonicalStat?.isSymbolicLink() || legacyStat?.isSymbolicLink()) && ![canonical8, legacy].includes(physical))
|
|
33714
34529
|
throw new Error("Sumi config alias must resolve to its canonical or legacy root");
|
|
33715
34530
|
return physical;
|
|
33716
34531
|
}
|
|
@@ -33718,10 +34533,11 @@ function sumiConfigDirectory(home, env = process.env, cwd = process.cwd()) {
|
|
|
33718
34533
|
return selectedConfigDirectory(sumiPathPlan(home, cwd, env));
|
|
33719
34534
|
}
|
|
33720
34535
|
function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
|
|
33721
|
-
return
|
|
34536
|
+
return join21(sumiConfigDirectory(home, env, cwd), "sumi.json");
|
|
33722
34537
|
}
|
|
33723
34538
|
var SUMI_PATH_SELECTORS, SUMI_PATH_KEYS, SUMI_PATH_OUTPUT_LIMIT, SumiPathResolverError;
|
|
33724
34539
|
var init_agent_sumi = __esm(() => {
|
|
34540
|
+
init_hook_diagnostics();
|
|
33725
34541
|
SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
|
|
33726
34542
|
SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
|
|
33727
34543
|
SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
|
|
@@ -33736,9 +34552,9 @@ var init_agent_sumi = __esm(() => {
|
|
|
33736
34552
|
});
|
|
33737
34553
|
|
|
33738
34554
|
// src/lib/codex-hook-trust-files.ts
|
|
33739
|
-
import { closeSync as
|
|
33740
|
-
import { createHash as
|
|
33741
|
-
import { dirname as
|
|
34555
|
+
import { closeSync as closeSync12, constants as constants10, fstatSync as fstatSync12, fsyncSync as fsyncSync2, lstatSync as lstatSync16, openSync as openSync12, readlinkSync as readlinkSync4, readSync as readSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
34556
|
+
import { createHash as createHash16 } from "crypto";
|
|
34557
|
+
import { dirname as dirname14, isAbsolute as isAbsolute13, resolve as resolve14 } from "path";
|
|
33742
34558
|
function need6(value, code) {
|
|
33743
34559
|
if (!value)
|
|
33744
34560
|
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
@@ -33747,37 +34563,37 @@ function trustedAncestorOwner(uid) {
|
|
|
33747
34563
|
return BigInt(uid) === 0n || BigInt(uid) === BigInt(process.getuid());
|
|
33748
34564
|
}
|
|
33749
34565
|
function safeParents(file2) {
|
|
33750
|
-
for (let p =
|
|
33751
|
-
const s =
|
|
34566
|
+
for (let p = dirname14(file2);; p = dirname14(p)) {
|
|
34567
|
+
const s = lstatSync16(p);
|
|
33752
34568
|
need6(s.isDirectory() && !s.isSymbolicLink() && trustedAncestorOwner(s.uid), "UNSAFE_PARENT");
|
|
33753
34569
|
need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
33754
|
-
if (
|
|
34570
|
+
if (dirname14(p) === p)
|
|
33755
34571
|
break;
|
|
33756
34572
|
}
|
|
33757
34573
|
}
|
|
33758
34574
|
function snapshot(file2, privateFile = false, options = {}) {
|
|
33759
34575
|
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
33760
34576
|
safeParents(file2);
|
|
33761
|
-
const initial =
|
|
34577
|
+
const initial = lstatSync16(file2, { bigint: true });
|
|
33762
34578
|
need6(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
|
|
33763
34579
|
need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
|
|
33764
|
-
const fd =
|
|
34580
|
+
const fd = openSync12(file2, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
|
|
33765
34581
|
try {
|
|
33766
|
-
const before =
|
|
34582
|
+
const before = fstatSync12(fd, { bigint: true });
|
|
33767
34583
|
need6(sameStat(initial, before), "INPUT_CHANGED");
|
|
33768
34584
|
const bytes = Buffer.alloc(Number(before.size) + 1);
|
|
33769
34585
|
let length = 0;
|
|
33770
34586
|
while (length < bytes.length) {
|
|
33771
|
-
const n2 =
|
|
34587
|
+
const n2 = readSync7(fd, bytes, length, bytes.length - length, null);
|
|
33772
34588
|
if (!n2)
|
|
33773
34589
|
break;
|
|
33774
34590
|
length += n2;
|
|
33775
34591
|
}
|
|
33776
|
-
need6(length === Number(before.size) && sameStat(before,
|
|
34592
|
+
need6(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync16(file2, { bigint: true })), "INPUT_CHANGED");
|
|
33777
34593
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
33778
|
-
return { file: file2, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256:
|
|
34594
|
+
return { file: file2, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
|
|
33779
34595
|
} finally {
|
|
33780
|
-
|
|
34596
|
+
closeSync12(fd);
|
|
33781
34597
|
}
|
|
33782
34598
|
}
|
|
33783
34599
|
function unchanged2(before) {
|
|
@@ -33839,47 +34655,47 @@ var init_runtime_launcher = __esm(() => {
|
|
|
33839
34655
|
});
|
|
33840
34656
|
|
|
33841
34657
|
// src/lib/retained-review-artifacts.ts
|
|
33842
|
-
import { createHash as
|
|
33843
|
-
import { constants as
|
|
33844
|
-
import { dirname as
|
|
34658
|
+
import { createHash as createHash17 } from "crypto";
|
|
34659
|
+
import { constants as constants11, closeSync as closeSync13, fstatSync as fstatSync13, fsyncSync as fsyncSync3, lstatSync as lstatSync17, mkdirSync as mkdirSync5, openSync as openSync13, readSync as readSync8, writeSync as writeSync2 } from "fs";
|
|
34660
|
+
import { dirname as dirname15, isAbsolute as isAbsolute14, join as join22, resolve as resolve15, sep as sep8 } from "path";
|
|
33845
34661
|
function keys(value, names) {
|
|
33846
34662
|
return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === names.length && names.every((name) => Object.hasOwn(value, name)));
|
|
33847
34663
|
}
|
|
33848
34664
|
function safe3(path) {
|
|
33849
|
-
need7(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters &&
|
|
33850
|
-
for (let cursor = path;; cursor =
|
|
33851
|
-
const stat =
|
|
34665
|
+
need7(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute14(path) && resolve15(path) === path && !/[\x00-\x1f\x7f]/.test(path));
|
|
34666
|
+
for (let cursor = path;; cursor = dirname15(cursor)) {
|
|
34667
|
+
const stat = lstatSync17(cursor, { throwIfNoEntry: false });
|
|
33852
34668
|
need7(!stat?.isSymbolicLink());
|
|
33853
34669
|
if (cursor !== path && stat)
|
|
33854
34670
|
need7(stat.isDirectory());
|
|
33855
|
-
if (
|
|
34671
|
+
if (dirname15(cursor) === cursor)
|
|
33856
34672
|
break;
|
|
33857
34673
|
}
|
|
33858
34674
|
}
|
|
33859
34675
|
function readExact(path) {
|
|
33860
34676
|
safe3(path);
|
|
33861
|
-
const before =
|
|
34677
|
+
const before = lstatSync17(path, { throwIfNoEntry: false });
|
|
33862
34678
|
need7(before?.isFile() && before.nlink === 1 && before.size <= AGENT_POLICY_LIMITS.discoveryRawSourceBytes && (before.mode & 63) === 0 && (process.getuid === undefined || before.uid === process.getuid()));
|
|
33863
|
-
const fd =
|
|
34679
|
+
const fd = openSync13(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
|
|
33864
34680
|
try {
|
|
33865
|
-
const opened =
|
|
34681
|
+
const opened = fstatSync13(fd);
|
|
33866
34682
|
need7(opened.dev === before.dev && opened.ino === before.ino && opened.size === before.size && opened.mode === before.mode && opened.uid === before.uid && opened.nlink === 1);
|
|
33867
34683
|
const bytes = Buffer.alloc(opened.size);
|
|
33868
34684
|
let offset = 0;
|
|
33869
34685
|
while (offset < bytes.length) {
|
|
33870
|
-
const count =
|
|
34686
|
+
const count = readSync8(fd, bytes, offset, bytes.length - offset, null);
|
|
33871
34687
|
need7(count > 0);
|
|
33872
34688
|
offset += count;
|
|
33873
34689
|
}
|
|
33874
|
-
need7(
|
|
33875
|
-
const after =
|
|
34690
|
+
need7(readSync8(fd, Buffer.alloc(1), 0, 1, null) === 0);
|
|
34691
|
+
const after = fstatSync13(fd), current = lstatSync17(path, { throwIfNoEntry: false });
|
|
33876
34692
|
safe3(path);
|
|
33877
34693
|
need7(current && opened.dev === current.dev && opened.ino === current.ino && opened.mode === current.mode && opened.uid === current.uid && opened.size === current.size && opened.mtimeMs === current.mtimeMs && opened.ctimeMs === current.ctimeMs && current.nlink === 1 && opened.mode === after.mode && opened.uid === after.uid && after.nlink === 1 && opened.size === after.size && opened.mtimeMs === after.mtimeMs && opened.ctimeMs === after.ctimeMs);
|
|
33878
34694
|
const text2 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
33879
34695
|
need7(Buffer.from(text2).equals(bytes));
|
|
33880
34696
|
return text2;
|
|
33881
34697
|
} finally {
|
|
33882
|
-
|
|
34698
|
+
closeSync13(fd);
|
|
33883
34699
|
}
|
|
33884
34700
|
}
|
|
33885
34701
|
function catalogDigest(text2) {
|
|
@@ -33887,7 +34703,7 @@ function catalogDigest(text2) {
|
|
|
33887
34703
|
try {
|
|
33888
34704
|
catalog = JSON.parse(text2);
|
|
33889
34705
|
} catch {
|
|
33890
|
-
return
|
|
34706
|
+
return refuse4();
|
|
33891
34707
|
}
|
|
33892
34708
|
need7(catalog && typeof catalog === "object" && !Array.isArray(catalog) && Object.keys(catalog).every((key) => ["version", "cwd", "skills", "plugins"].includes(key)) && typeof catalog.version === "string" && supportsCodexNativeCapability(catalog.version, "installed-plugin-review"));
|
|
33893
34709
|
const skills = projectCodexNativeSkillCatalog({ data: [{ cwd: catalog.cwd, errors: [], skills: catalog.skills }] }, catalog.cwd);
|
|
@@ -33895,32 +34711,32 @@ function catalogDigest(text2) {
|
|
|
33895
34711
|
return sha3(JSON.stringify({ version: catalog.version, cwd: catalog.cwd, skills, ...plugins === undefined ? {} : { plugins } }));
|
|
33896
34712
|
}
|
|
33897
34713
|
function retainedArtifactPath(metadata) {
|
|
33898
|
-
return
|
|
34714
|
+
return join22(metadata.storeRoot, "agent-discovery", "artifacts", metadata.sha256, "catalog.json");
|
|
33899
34715
|
}
|
|
33900
34716
|
function validRetainedArtifact(value) {
|
|
33901
|
-
return keys(value, ["version", "kind", "storeRoot", "originalPath", "sha256", "catalogSha256"]) && value.version === 1 && value.kind === "codex-native-catalog" && hex3(value.sha256) && hex3(value.catalogSha256) && [value.storeRoot, value.originalPath].every((path) => typeof path === "string" &&
|
|
34717
|
+
return keys(value, ["version", "kind", "storeRoot", "originalPath", "sha256", "catalogSha256"]) && value.version === 1 && value.kind === "codex-native-catalog" && hex3(value.sha256) && hex3(value.catalogSha256) && [value.storeRoot, value.originalPath].every((path) => typeof path === "string" && isAbsolute14(path) && resolve15(path) === path && path.length <= AGENT_POLICY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(path));
|
|
33902
34718
|
}
|
|
33903
34719
|
function privateStore(storeRoot) {
|
|
33904
34720
|
safe3(storeRoot);
|
|
33905
|
-
const stat =
|
|
34721
|
+
const stat = lstatSync17(storeRoot, { throwIfNoEntry: false });
|
|
33906
34722
|
need7(!stat || stat.isDirectory() && (stat.mode & 18) === 0 && (process.getuid === undefined || stat.uid === process.getuid()));
|
|
33907
34723
|
}
|
|
33908
34724
|
function verifyRetainedReviewArtifact(source, storeRoot) {
|
|
33909
34725
|
const artifact = source.reviewArtifact;
|
|
33910
|
-
need7(validRetainedArtifact(artifact) && source.hashMode === "bytes" && source.sha256 === artifact.sha256 && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.path === retainedArtifactPath(artifact) && (storeRoot === undefined || artifact.storeRoot ===
|
|
34726
|
+
need7(validRetainedArtifact(artifact) && source.hashMode === "bytes" && source.sha256 === artifact.sha256 && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.path === retainedArtifactPath(artifact) && (storeRoot === undefined || artifact.storeRoot === resolve15(storeRoot)));
|
|
33911
34727
|
privateStore(artifact.storeRoot);
|
|
33912
34728
|
const text2 = readExact(source.path);
|
|
33913
34729
|
need7(sha3(text2) === artifact.sha256 && catalogDigest(text2) === artifact.catalogSha256);
|
|
33914
|
-
for (let parent =
|
|
33915
|
-
const stat =
|
|
34730
|
+
for (let parent = dirname15(source.path);parent !== artifact.storeRoot; parent = dirname15(parent)) {
|
|
34731
|
+
const stat = lstatSync17(parent);
|
|
33916
34732
|
need7(stat.isDirectory() && (stat.mode & 63) === 0 && (process.getuid === undefined || stat.uid === process.getuid()));
|
|
33917
34733
|
}
|
|
33918
34734
|
}
|
|
33919
|
-
var sha3 = (bytes) =>
|
|
34735
|
+
var sha3 = (bytes) => createHash17("sha256").update(bytes).digest("hex"), hex3 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), refuse4 = () => {
|
|
33920
34736
|
throw new Error("REVIEW_ARTIFACT_INVALID");
|
|
33921
34737
|
}, need7 = (condition) => {
|
|
33922
34738
|
if (!condition)
|
|
33923
|
-
|
|
34739
|
+
refuse4();
|
|
33924
34740
|
};
|
|
33925
34741
|
var init_retained_review_artifacts = __esm(() => {
|
|
33926
34742
|
init_agent_policy_limits();
|
|
@@ -33937,6 +34753,8 @@ var init_agent_integration = __esm(() => {
|
|
|
33937
34753
|
init_codex_native_skill_catalog();
|
|
33938
34754
|
init_codex_settings_witness();
|
|
33939
34755
|
init_claude_settings_witness();
|
|
34756
|
+
init_codex_native_compatibility();
|
|
34757
|
+
init_codex_semantic_cache_witness();
|
|
33940
34758
|
init_native_discovery_roots();
|
|
33941
34759
|
init_config();
|
|
33942
34760
|
init_managed_policy();
|
|
@@ -33987,12 +34805,12 @@ var init_skill_aliases = __esm(() => {
|
|
|
33987
34805
|
|
|
33988
34806
|
// src/lib/project-state.ts
|
|
33989
34807
|
import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync5 } from "fs";
|
|
33990
|
-
import { join as
|
|
34808
|
+
import { join as join23 } from "path";
|
|
33991
34809
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
33992
|
-
return
|
|
34810
|
+
return join23(targetDir, SKILLS_PROJECT_DIR);
|
|
33993
34811
|
}
|
|
33994
34812
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
33995
|
-
return
|
|
34813
|
+
return join23(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
33996
34814
|
}
|
|
33997
34815
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
33998
34816
|
const path = getProjectConfigPath(targetDir);
|
|
@@ -34108,7 +34926,7 @@ var init_project_state = __esm(() => {
|
|
|
34108
34926
|
|
|
34109
34927
|
// src/lib/installer.ts
|
|
34110
34928
|
import { existsSync as existsSync10, readFileSync as readFileSync14, rmSync } from "fs";
|
|
34111
|
-
import { join as
|
|
34929
|
+
import { join as join24 } from "path";
|
|
34112
34930
|
import { homedir as homedir2 } from "os";
|
|
34113
34931
|
function getSkillPath(name) {
|
|
34114
34932
|
const skillName = normalizeSkillName(getCanonicalSkillName(name));
|
|
@@ -34118,7 +34936,7 @@ function getSkillPath(name) {
|
|
|
34118
34936
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
34119
34937
|
if (extensionPath)
|
|
34120
34938
|
return extensionPath;
|
|
34121
|
-
return
|
|
34939
|
+
return join24(getPortableSkillsRoot(), skillName);
|
|
34122
34940
|
}
|
|
34123
34941
|
function getCanonicalSkillName(name) {
|
|
34124
34942
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
@@ -34172,11 +34990,11 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
34172
34990
|
const base = projectDir || process.cwd();
|
|
34173
34991
|
switch (agent) {
|
|
34174
34992
|
case "pi":
|
|
34175
|
-
return scope === "project" ?
|
|
34993
|
+
return scope === "project" ? join24(base, ".pi", "skills") : join24(homedir2(), ".pi", "agent", "skills");
|
|
34176
34994
|
case "opencode":
|
|
34177
|
-
return scope === "project" ?
|
|
34995
|
+
return scope === "project" ? join24(base, ".opencode", "skills") : join24(homedir2(), ".config", "opencode", "skills");
|
|
34178
34996
|
default:
|
|
34179
|
-
return scope === "project" ?
|
|
34997
|
+
return scope === "project" ? join24(base, `.${agent}`, "skills") : join24(homedir2(), `.${agent}`, "skills");
|
|
34180
34998
|
}
|
|
34181
34999
|
}
|
|
34182
35000
|
function warnMissingDependencies(name, targetDir) {
|
|
@@ -34191,7 +35009,7 @@ function warnMissingDependencies(name, targetDir) {
|
|
|
34191
35009
|
}
|
|
34192
35010
|
}
|
|
34193
35011
|
function readBundledSkillVersion(name) {
|
|
34194
|
-
const pkgPath =
|
|
35012
|
+
const pkgPath = join24(getSkillPath(name), "package.json");
|
|
34195
35013
|
if (!existsSync10(pkgPath))
|
|
34196
35014
|
return "unknown";
|
|
34197
35015
|
try {
|
|
@@ -34261,11 +35079,11 @@ var init_skill_doc_file = __esm(() => {
|
|
|
34261
35079
|
|
|
34262
35080
|
// src/lib/skillinfo.ts
|
|
34263
35081
|
import { existsSync as existsSync11, readFileSync as readFileSync15 } from "fs";
|
|
34264
|
-
import { join as
|
|
35082
|
+
import { join as join25 } from "path";
|
|
34265
35083
|
function isInstructionSkillDir(skillPath, meta3) {
|
|
34266
35084
|
if (meta3?.kind === "instruction")
|
|
34267
35085
|
return true;
|
|
34268
|
-
const skillMdPath =
|
|
35086
|
+
const skillMdPath = join25(skillPath, "SKILL.md");
|
|
34269
35087
|
if (!existsSync11(skillMdPath))
|
|
34270
35088
|
return false;
|
|
34271
35089
|
try {
|
|
@@ -34279,9 +35097,9 @@ function getSkillDocs(name) {
|
|
|
34279
35097
|
if (!existsSync11(skillPath))
|
|
34280
35098
|
return null;
|
|
34281
35099
|
return {
|
|
34282
|
-
skillMd: readIfExists(
|
|
34283
|
-
readme: readIfExists(
|
|
34284
|
-
claudeMd: readIfExists(
|
|
35100
|
+
skillMd: readIfExists(join25(skillPath, "SKILL.md")),
|
|
35101
|
+
readme: readIfExists(join25(skillPath, "README.md")),
|
|
35102
|
+
claudeMd: readIfExists(join25(skillPath, "CLAUDE.md"))
|
|
34285
35103
|
};
|
|
34286
35104
|
}
|
|
34287
35105
|
function getSkillBestDoc(name, file2) {
|
|
@@ -34297,14 +35115,14 @@ function getSkillRequirements(name) {
|
|
|
34297
35115
|
return null;
|
|
34298
35116
|
const texts = [];
|
|
34299
35117
|
for (const file2 of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
34300
|
-
const content = readIfExists(
|
|
35118
|
+
const content = readIfExists(join25(skillPath, file2));
|
|
34301
35119
|
if (content)
|
|
34302
35120
|
texts.push(content);
|
|
34303
35121
|
}
|
|
34304
35122
|
const meta3 = getSkill(name);
|
|
34305
35123
|
let dependencies = {};
|
|
34306
35124
|
try {
|
|
34307
|
-
dependencies = JSON.parse(readFileSync15(
|
|
35125
|
+
dependencies = JSON.parse(readFileSync15(join25(skillPath, "package.json"), "utf8")).dependencies || {};
|
|
34308
35126
|
} catch {}
|
|
34309
35127
|
return getSkillRequirementsFromContent(name, texts, dependencies, meta3);
|
|
34310
35128
|
}
|
|
@@ -34366,7 +35184,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
34366
35184
|
error: `Skill '${name}' is an instruction skill (kind: instruction) and is not runnable. Instruction skills are consumed by coding agents via SKILL.md, not executed with 'skills run'.`
|
|
34367
35185
|
};
|
|
34368
35186
|
}
|
|
34369
|
-
const pkgPath =
|
|
35187
|
+
const pkgPath = join25(skillPath, "package.json");
|
|
34370
35188
|
if (!existsSync11(pkgPath)) {
|
|
34371
35189
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
34372
35190
|
}
|
|
@@ -34386,12 +35204,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
34386
35204
|
} catch {
|
|
34387
35205
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
34388
35206
|
}
|
|
34389
|
-
const entryPath =
|
|
35207
|
+
const entryPath = join25(skillPath, entryPoint2);
|
|
34390
35208
|
if (!existsSync11(entryPath)) {
|
|
34391
35209
|
return { exitCode: 1, error: `Entry point '${entryPoint2}' not found in skill '${name}'` };
|
|
34392
35210
|
}
|
|
34393
35211
|
const env = { ...process.env, ...options.env };
|
|
34394
|
-
if (!existsSync11(
|
|
35212
|
+
if (!existsSync11(join25(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
|
|
34395
35213
|
const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
|
|
34396
35214
|
if (failure)
|
|
34397
35215
|
return failure;
|
|
@@ -34417,7 +35235,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
34417
35235
|
function detectProjectSkills(cwd = process.cwd()) {
|
|
34418
35236
|
let pkg;
|
|
34419
35237
|
try {
|
|
34420
|
-
pkg = JSON.parse(readFileSync15(
|
|
35238
|
+
pkg = JSON.parse(readFileSync15(join25(cwd, "package.json"), "utf8"));
|
|
34421
35239
|
} catch {
|
|
34422
35240
|
return { detected: [], recommended: [] };
|
|
34423
35241
|
}
|
|
@@ -34622,7 +35440,7 @@ var init_selection_resolver = __esm(() => {
|
|
|
34622
35440
|
});
|
|
34623
35441
|
|
|
34624
35442
|
// src/lib/plugin-projection.ts
|
|
34625
|
-
import { createHash as
|
|
35443
|
+
import { createHash as createHash18 } from "crypto";
|
|
34626
35444
|
function pluginRefusal(message) {
|
|
34627
35445
|
throw new SkillSelectionError("PLUGIN_ADMISSION_REFUSED", message);
|
|
34628
35446
|
}
|
|
@@ -34700,26 +35518,26 @@ function pluginFileWitnesses(entries) {
|
|
|
34700
35518
|
return { path: item.path, mode: item.mode, size: item.bytes.byteLength, sha256: pluginHash(item.bytes) };
|
|
34701
35519
|
});
|
|
34702
35520
|
}
|
|
34703
|
-
var PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) =>
|
|
35521
|
+
var PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) => createHash18("sha256").update(bytes).digest("hex");
|
|
34704
35522
|
var init_plugin_projection = __esm(() => {
|
|
34705
35523
|
init_selection_cache();
|
|
34706
35524
|
PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
34707
35525
|
});
|
|
34708
35526
|
|
|
34709
35527
|
// src/lib/plugin-projection-store.ts
|
|
34710
|
-
import { constants as
|
|
34711
|
-
import { dirname as
|
|
35528
|
+
import { constants as constants12, closeSync as closeSync14, fchmodSync, fstatSync as fstatSync14, fsyncSync as fsyncSync4, linkSync as linkSync2, lstatSync as lstatSync18, mkdirSync as mkdirSync7, openSync as openSync14, opendirSync as opendirSync2, readSync as readSync9, renameSync as renameSync2, rmdirSync as rmdirSync2, rmSync as rmSync2, unlinkSync as unlinkSync3, writeSync as writeSync3 } from "fs";
|
|
35529
|
+
import { dirname as dirname16, isAbsolute as isAbsolute15, join as join26, parse as parse10, resolve as resolve16 } from "path";
|
|
34712
35530
|
function stat(path) {
|
|
34713
35531
|
try {
|
|
34714
|
-
return
|
|
35532
|
+
return lstatSync18(path, { bigint: true });
|
|
34715
35533
|
} catch (error61) {
|
|
34716
35534
|
if (missing(error61))
|
|
34717
35535
|
return null;
|
|
34718
35536
|
return pluginRefusal("Cannot inspect plugin filesystem entry");
|
|
34719
35537
|
}
|
|
34720
35538
|
}
|
|
34721
|
-
function
|
|
34722
|
-
pluginNeed(typeof path === "string" && path.length <= 8192 &&
|
|
35539
|
+
function canonical8(path) {
|
|
35540
|
+
pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute15(path) && resolve16(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
|
|
34723
35541
|
}
|
|
34724
35542
|
function owner(stats, system = false) {
|
|
34725
35543
|
const current = uid();
|
|
@@ -34729,12 +35547,12 @@ function same3(left, right, identityOnly = false) {
|
|
|
34729
35547
|
pluginNeed(right && left.dev === right.dev && left.ino === right.ino && left.mode === right.mode && left.uid === right.uid && left.gid === right.gid && (identityOnly || left.size === right.size && left.nlink === right.nlink && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs), "Plugin filesystem entry changed during access");
|
|
34730
35548
|
}
|
|
34731
35549
|
function ancestors(path, create = false) {
|
|
34732
|
-
|
|
34733
|
-
const result = [], parent =
|
|
35550
|
+
canonical8(path);
|
|
35551
|
+
const result = [], parent = dirname16(path), filesystemRoot = parse10(parent).root;
|
|
34734
35552
|
let current = filesystemRoot;
|
|
34735
35553
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
34736
35554
|
if (segment)
|
|
34737
|
-
current =
|
|
35555
|
+
current = join26(current, segment);
|
|
34738
35556
|
let currentStat = stat(current);
|
|
34739
35557
|
if (!currentStat && create) {
|
|
34740
35558
|
try {
|
|
@@ -34780,19 +35598,19 @@ function readBytes(path, maximum, kind) {
|
|
|
34780
35598
|
fileShape(before, maximum, kind);
|
|
34781
35599
|
let fd;
|
|
34782
35600
|
try {
|
|
34783
|
-
fd =
|
|
34784
|
-
const opened =
|
|
35601
|
+
fd = openSync14(path, READ_FLAGS);
|
|
35602
|
+
const opened = fstatSync14(fd, { bigint: true });
|
|
34785
35603
|
same3(before, opened);
|
|
34786
35604
|
fileShape(opened, maximum, kind);
|
|
34787
35605
|
const bytes = new Uint8Array(Number(opened.size));
|
|
34788
35606
|
let offset = 0;
|
|
34789
35607
|
while (offset < bytes.byteLength) {
|
|
34790
|
-
const count =
|
|
35608
|
+
const count = readSync9(fd, bytes, offset, bytes.byteLength - offset, null);
|
|
34791
35609
|
pluginNeed(count > 0, "Plugin file changed while being read");
|
|
34792
35610
|
offset += count;
|
|
34793
35611
|
}
|
|
34794
|
-
pluginNeed(
|
|
34795
|
-
same3(opened,
|
|
35612
|
+
pluginNeed(readSync9(fd, new Uint8Array(1), 0, 1, null) === 0, "Plugin file grew while being read");
|
|
35613
|
+
same3(opened, fstatSync14(fd, { bigint: true }));
|
|
34796
35614
|
same3(opened, stat(path));
|
|
34797
35615
|
verifyAncestors(chain);
|
|
34798
35616
|
return bytes;
|
|
@@ -34802,7 +35620,7 @@ function readBytes(path, maximum, kind) {
|
|
|
34802
35620
|
return pluginRefusal("Cannot safely read plugin file");
|
|
34803
35621
|
} finally {
|
|
34804
35622
|
if (fd !== undefined)
|
|
34805
|
-
|
|
35623
|
+
closeSync14(fd);
|
|
34806
35624
|
}
|
|
34807
35625
|
}
|
|
34808
35626
|
function exactPaths(entries) {
|
|
@@ -34832,7 +35650,7 @@ function directoryNames(path) {
|
|
|
34832
35650
|
function snapshotPluginTree(root, options = {}) {
|
|
34833
35651
|
const chain = ancestors(root), entries = [], files = [], directories = [];
|
|
34834
35652
|
let bytes = 0, visited = 0;
|
|
34835
|
-
const visit3 = (directory,
|
|
35653
|
+
const visit3 = (directory, relative7) => {
|
|
34836
35654
|
const before = stat(directory);
|
|
34837
35655
|
pluginNeed(before && before.isDirectory() && !before.isSymbolicLink(), "Plugin tree must contain real directories");
|
|
34838
35656
|
const names = directoryNames(directory);
|
|
@@ -34840,18 +35658,18 @@ function snapshotPluginTree(root, options = {}) {
|
|
|
34840
35658
|
directories.push({ path: directory, stat: before, names });
|
|
34841
35659
|
for (const name of names) {
|
|
34842
35660
|
pluginNeed(++visited <= PLUGIN_PROJECTION_LIMITS.files * 50, "Plugin tree membership exceeds its limit");
|
|
34843
|
-
const path =
|
|
35661
|
+
const path = relative7 ? `${relative7}/${name}` : name;
|
|
34844
35662
|
pluginNeed(new TextEncoder().encode(path).byteLength <= 100 && !/[\\:\x00-\x1f\x7f]/u.test(path), "Plugin entry path exceeds its limit or is unsafe");
|
|
34845
|
-
const absolute =
|
|
35663
|
+
const absolute = join26(directory, name), entryStat = stat(absolute);
|
|
34846
35664
|
pluginNeed(entryStat, "Plugin tree member disappeared");
|
|
34847
|
-
if (
|
|
35665
|
+
if (relative7 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
|
|
34848
35666
|
const bytes2 = readBytes(absolute, 32, "native-pin");
|
|
34849
35667
|
pluginNeed(/^[1-9][0-9]{12}$/.test(new TextDecoder().decode(bytes2)), "Malformed native orphan timestamp");
|
|
34850
35668
|
same3(entryStat, stat(absolute));
|
|
34851
35669
|
files.push({ path: absolute, stat: entryStat });
|
|
34852
35670
|
continue;
|
|
34853
35671
|
}
|
|
34854
|
-
if (
|
|
35672
|
+
if (relative7 === "" && name === ".in_use" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
|
|
34855
35673
|
pluginNeed(entryStat.isDirectory() && !entryStat.isSymbolicLink(), "Native plugin pins require a real directory");
|
|
34856
35674
|
owner(entryStat);
|
|
34857
35675
|
const pins = directoryNames(absolute);
|
|
@@ -34859,7 +35677,7 @@ function snapshotPluginTree(root, options = {}) {
|
|
|
34859
35677
|
directories.push({ path: absolute, stat: entryStat, names: pins });
|
|
34860
35678
|
for (const pin of pins) {
|
|
34861
35679
|
pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin) && Number(pin) <= 2147483647, "Unrecognized native plugin pin name");
|
|
34862
|
-
const pinPath =
|
|
35680
|
+
const pinPath = join26(absolute, pin), pinStat = stat(pinPath);
|
|
34863
35681
|
pluginNeed(pinStat, "Native plugin pin disappeared");
|
|
34864
35682
|
const bytes2 = readBytes(pinPath, 4096, "native-pin");
|
|
34865
35683
|
let value;
|
|
@@ -34925,8 +35743,8 @@ function readPluginJson(path) {
|
|
|
34925
35743
|
var READ_FLAGS, WRITE_FLAGS, uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined, missing = (error61) => Boolean(error61 && typeof error61 === "object" && "code" in error61 && error61.code === "ENOENT");
|
|
34926
35744
|
var init_plugin_projection_store = __esm(() => {
|
|
34927
35745
|
init_plugin_projection();
|
|
34928
|
-
READ_FLAGS =
|
|
34929
|
-
WRITE_FLAGS =
|
|
35746
|
+
READ_FLAGS = constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
|
|
35747
|
+
WRITE_FLAGS = constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
|
|
34930
35748
|
});
|
|
34931
35749
|
|
|
34932
35750
|
// src/lib/selected-manifest.ts
|
|
@@ -34956,7 +35774,7 @@ var init_selected_manifest = __esm(() => {
|
|
|
34956
35774
|
});
|
|
34957
35775
|
|
|
34958
35776
|
// src/lib/plugin-admission.ts
|
|
34959
|
-
import { isAbsolute as
|
|
35777
|
+
import { isAbsolute as isAbsolute16, join as join27, resolve as resolve17 } from "path";
|
|
34960
35778
|
function validatePluginTarget(value) {
|
|
34961
35779
|
pluginKeys(value, ["schemaVersion", "pluginId", "registrations", "native", "resolver"]);
|
|
34962
35780
|
pluginNeed(value.schemaVersion === 1, "Invalid plugin admission target");
|
|
@@ -34966,7 +35784,7 @@ function validatePluginTarget(value) {
|
|
|
34966
35784
|
for (const registration of value.registrations) {
|
|
34967
35785
|
pluginKeys(registration, ["scope", "projectPath"]);
|
|
34968
35786
|
pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
|
|
34969
|
-
pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" &&
|
|
35787
|
+
pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute16(registration.projectPath) && resolve17(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
|
|
34970
35788
|
const key = JSON.stringify([registration.scope, registration.projectPath]);
|
|
34971
35789
|
pluginNeed(!scopes.has(key), "Duplicate plugin registration scope");
|
|
34972
35790
|
scopes.add(key);
|
|
@@ -34974,7 +35792,7 @@ function validatePluginTarget(value) {
|
|
|
34974
35792
|
for (const key of ["native", "resolver"]) {
|
|
34975
35793
|
pluginKeys(value[key], key === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
|
|
34976
35794
|
pluginText(value[key].executable);
|
|
34977
|
-
pluginNeed(
|
|
35795
|
+
pluginNeed(isAbsolute16(value[key].executable) && resolve17(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
|
|
34978
35796
|
pluginDigest(value[key].digest);
|
|
34979
35797
|
}
|
|
34980
35798
|
pluginNeed(["2.1.274", "2.1.276"].includes(value.native.version), "Plugin admission requires a certified Claude command-source runtime (2.1.274 or 2.1.276)");
|
|
@@ -35117,12 +35935,12 @@ function pluginResolverCommand(binding) {
|
|
|
35117
35935
|
}
|
|
35118
35936
|
function pluginBindingPath(storeRoot, id) {
|
|
35119
35937
|
pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
|
|
35120
|
-
return
|
|
35938
|
+
return join27(storeRoot, "bindings", `${id}.json`);
|
|
35121
35939
|
}
|
|
35122
|
-
function pluginReceiptPath(storeRoot, id,
|
|
35940
|
+
function pluginReceiptPath(storeRoot, id, digest2) {
|
|
35123
35941
|
pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
|
|
35124
|
-
pluginDigest(
|
|
35125
|
-
return
|
|
35942
|
+
pluginDigest(digest2);
|
|
35943
|
+
return join27(storeRoot, "receipts", id, `${digest2.slice(7)}.json`);
|
|
35126
35944
|
}
|
|
35127
35945
|
function readPluginBinding(storeRoot, id) {
|
|
35128
35946
|
const value = readPluginJson(pluginBindingPath(storeRoot, id));
|
|
@@ -35131,18 +35949,18 @@ function readPluginBinding(storeRoot, id) {
|
|
|
35131
35949
|
pluginNeed(bindingId(binding) === id, "Plugin admission binding changed");
|
|
35132
35950
|
return binding;
|
|
35133
35951
|
}
|
|
35134
|
-
function readPluginAdmissionReceipt(storeRoot, id,
|
|
35135
|
-
const value = readPluginJson(pluginReceiptPath(storeRoot, id,
|
|
35952
|
+
function readPluginAdmissionReceipt(storeRoot, id, digest2) {
|
|
35953
|
+
const value = readPluginJson(pluginReceiptPath(storeRoot, id, digest2));
|
|
35136
35954
|
pluginKeys(value, ["schemaVersion", "plan", "materializedPath", "admittedAt"]);
|
|
35137
35955
|
pluginNeed(value.schemaVersion === 3, "Unsupported plugin admission receipt; schema version 3 is required");
|
|
35138
35956
|
validatePlan(value.plan);
|
|
35139
35957
|
const receipt = value;
|
|
35140
35958
|
receipt.plan.files = normalizedWitnesses(receipt.plan.files);
|
|
35141
35959
|
receipt.plan.removed = normalizedWitnesses(receipt.plan.removed);
|
|
35142
|
-
pluginNeed(receipt.plan.bindingId === id && receipt.plan.planDigest ===
|
|
35960
|
+
pluginNeed(receipt.plan.bindingId === id && receipt.plan.planDigest === digest2, "Plugin receipt identity differs from its filename");
|
|
35143
35961
|
const binding = readPluginBinding(storeRoot, id);
|
|
35144
35962
|
pluginNeed(canonicalJson(binding) === canonicalJson(receipt.plan.binding) && receipt.plan.sourceCommand === pluginResolverCommand(binding), "Plugin receipt binding changed");
|
|
35145
|
-
pluginNeed(receipt.materializedPath ===
|
|
35963
|
+
pluginNeed(receipt.materializedPath === join27(storeRoot, "objects", id, digest2.slice(7)), "Plugin receipt points outside its immutable materialization");
|
|
35146
35964
|
pluginText(receipt.admittedAt, 64);
|
|
35147
35965
|
pluginNeed(Number.isFinite(Date.parse(receipt.admittedAt)), "Invalid plugin admission time");
|
|
35148
35966
|
return receipt;
|
|
@@ -35159,8 +35977,8 @@ var init_plugin_admission = __esm(() => {
|
|
|
35159
35977
|
});
|
|
35160
35978
|
|
|
35161
35979
|
// src/lib/plugin-discovery.ts
|
|
35162
|
-
import { closeSync as
|
|
35163
|
-
import { basename as basename7, dirname as
|
|
35980
|
+
import { closeSync as closeSync15, constants as constants13, fstatSync as fstatSync15, lstatSync as lstatSync19, openSync as openSync15, opendirSync as opendirSync3, readSync as readSync10 } from "fs";
|
|
35981
|
+
import { basename as basename7, dirname as dirname17, isAbsolute as isAbsolute17, join as join28, resolve as resolve18 } from "path";
|
|
35164
35982
|
function validateManagedPluginWitnesses(value) {
|
|
35165
35983
|
pluginNeed(Array.isArray(value) && value.length > 0 && value.length <= PLUGIN_PROJECTION_LIMITS.registrations, "Invalid managed plugin witness collection");
|
|
35166
35984
|
const seen = new Set;
|
|
@@ -35169,35 +35987,35 @@ function validateManagedPluginWitnesses(value) {
|
|
|
35169
35987
|
pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
|
|
35170
35988
|
seen.add(item.bindingId);
|
|
35171
35989
|
pluginText(item.storeRoot);
|
|
35172
|
-
pluginNeed(
|
|
35990
|
+
pluginNeed(isAbsolute17(item.storeRoot) && resolve18(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
|
|
35173
35991
|
}
|
|
35174
35992
|
}
|
|
35175
35993
|
function safe4(path) {
|
|
35176
35994
|
pluginText(path);
|
|
35177
|
-
pluginNeed(
|
|
35178
|
-
for (let at = path;; at =
|
|
35179
|
-
pluginNeed(!
|
|
35180
|
-
if (at ===
|
|
35995
|
+
pluginNeed(isAbsolute17(path) && resolve18(path) === path, "Invalid plugin registry path");
|
|
35996
|
+
for (let at = path;; at = dirname17(at)) {
|
|
35997
|
+
pluginNeed(!lstatSync19(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
|
|
35998
|
+
if (at === dirname17(at))
|
|
35181
35999
|
break;
|
|
35182
36000
|
}
|
|
35183
36001
|
}
|
|
35184
36002
|
function readRegistry(path) {
|
|
35185
36003
|
safe4(path);
|
|
35186
|
-
const initial =
|
|
36004
|
+
const initial = lstatSync19(path);
|
|
35187
36005
|
pluginNeed(initial.isFile() && initial.size <= 1024 * 1024, "Plugin registry is not a bounded regular file");
|
|
35188
|
-
const fd =
|
|
36006
|
+
const fd = openSync15(path, constants13.O_RDONLY | constants13.O_NOFOLLOW | constants13.O_NONBLOCK);
|
|
35189
36007
|
try {
|
|
35190
|
-
const opened =
|
|
36008
|
+
const opened = fstatSync15(fd);
|
|
35191
36009
|
pluginNeed(opened.isFile() && opened.ino === initial.ino && opened.dev === initial.dev, "Plugin registry changed during open");
|
|
35192
36010
|
const bytes = Buffer.alloc(1024 * 1024 + 1);
|
|
35193
36011
|
let length = 0;
|
|
35194
36012
|
while (length < bytes.length) {
|
|
35195
|
-
const count =
|
|
36013
|
+
const count = readSync10(fd, bytes, length, bytes.length - length, null);
|
|
35196
36014
|
if (!count)
|
|
35197
36015
|
break;
|
|
35198
36016
|
length += count;
|
|
35199
36017
|
}
|
|
35200
|
-
const after =
|
|
36018
|
+
const after = fstatSync15(fd), current = lstatSync19(path);
|
|
35201
36019
|
safe4(path);
|
|
35202
36020
|
pluginNeed(length <= 1024 * 1024 && length === opened.size && after.size === opened.size && after.mtimeMs === opened.mtimeMs && after.ctimeMs === opened.ctimeMs && current.dev === opened.dev && current.ino === opened.ino && current.mtimeMs === opened.mtimeMs && current.ctimeMs === opened.ctimeMs, "Plugin registry changed during read");
|
|
35203
36021
|
let value;
|
|
@@ -35209,7 +36027,7 @@ function readRegistry(path) {
|
|
|
35209
36027
|
pluginNeed(pluginObject(value) && value.version === 2 && pluginObject(value.plugins), "Unsupported native plugin registry schema");
|
|
35210
36028
|
return { value, rawDigest: pluginHash(bytes.subarray(0, length)) };
|
|
35211
36029
|
} finally {
|
|
35212
|
-
|
|
36030
|
+
closeSync15(fd);
|
|
35213
36031
|
}
|
|
35214
36032
|
}
|
|
35215
36033
|
function nativeVersionMatches(version2, receipt) {
|
|
@@ -35227,7 +36045,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
35227
36045
|
seen.add(target.pluginId);
|
|
35228
36046
|
const rows = plugins[target.pluginId];
|
|
35229
36047
|
pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
|
|
35230
|
-
const [name, market] = target.pluginId.split("@"), cacheRoot =
|
|
36048
|
+
const [name, market] = target.pluginId.split("@"), cacheRoot = join28(dirname17(path), "cache", market, name);
|
|
35231
36049
|
const scopes = new Set, approvals = new Map, currentFiles = new Map;
|
|
35232
36050
|
plugins[target.pluginId] = rows.map((row) => {
|
|
35233
36051
|
pluginNeed(pluginObject(row), "Invalid managed plugin registration");
|
|
@@ -35240,11 +36058,11 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
35240
36058
|
pluginText(row.sourceProducerPath);
|
|
35241
36059
|
pluginText(row.lastUpdated, 64);
|
|
35242
36060
|
pluginNeed(Number.isFinite(Date.parse(row.lastUpdated)), "Invalid managed plugin update timestamp");
|
|
35243
|
-
pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath ===
|
|
36061
|
+
pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join28(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
|
|
35244
36062
|
const approval = (producer) => {
|
|
35245
36063
|
pluginText(producer);
|
|
35246
|
-
const
|
|
35247
|
-
const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId,
|
|
36064
|
+
const digest2 = `sha256:${basename7(producer)}`;
|
|
36065
|
+
const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest2);
|
|
35248
36066
|
pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
|
|
35249
36067
|
if (!verified.has(producer)) {
|
|
35250
36068
|
verifiedBytes += receipt2.plan.files.reduce((total, file2) => total + file2.size, 0);
|
|
@@ -35270,7 +36088,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
35270
36088
|
return Object.fromEntries(Object.entries(row).filter(([key]) => !["version", "installPath", "sourceProducerPath", "previousProducerPaths", "lastUpdated"].includes(key)));
|
|
35271
36089
|
});
|
|
35272
36090
|
safe4(cacheRoot);
|
|
35273
|
-
const before =
|
|
36091
|
+
const before = lstatSync19(cacheRoot);
|
|
35274
36092
|
pluginNeed(before.isDirectory(), "Invalid managed plugin cache root");
|
|
35275
36093
|
const directory = opendirSync3(cacheRoot), names = [];
|
|
35276
36094
|
try {
|
|
@@ -35283,17 +36101,17 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
35283
36101
|
directory.closeSync();
|
|
35284
36102
|
}
|
|
35285
36103
|
for (const version2 of names.sort()) {
|
|
35286
|
-
const files = pluginFileWitnesses(snapshotPluginTree(
|
|
36104
|
+
const files = pluginFileWitnesses(snapshotPluginTree(join28(cacheRoot, version2), { nativeRuntime: `claude-${target.native.version}` }));
|
|
35287
36105
|
verifiedBytes += files.reduce((total, file2) => total + file2.size, 0);
|
|
35288
36106
|
pluginNeed(verifiedBytes <= 256 * 1024 * 1024, "Managed plugin verification exceeds its aggregate byte budget");
|
|
35289
36107
|
const serialized = JSON.stringify(files);
|
|
35290
|
-
const expected = currentFiles.get(
|
|
36108
|
+
const expected = currentFiles.get(join28(cacheRoot, version2));
|
|
35291
36109
|
pluginNeed(expected === undefined || serialized === expected, "Current native cache differs from its exact registered producer receipt");
|
|
35292
36110
|
pluginNeed([...approvals.values()].some((receipt) => nativeVersionMatches(version2, receipt) && JSON.stringify(receipt.plan.files) === serialized), "A retained native plugin cache version differs from every approved projection");
|
|
35293
36111
|
}
|
|
35294
36112
|
for (const row of rows)
|
|
35295
36113
|
pluginNeed(names.includes(row.version), "Managed native plugin installation is missing");
|
|
35296
|
-
const after =
|
|
36114
|
+
const after = lstatSync19(cacheRoot);
|
|
35297
36115
|
safe4(cacheRoot);
|
|
35298
36116
|
pluginNeed(before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs, "Managed cache version membership changed during verification");
|
|
35299
36117
|
}
|
|
@@ -35307,23 +36125,23 @@ var init_plugin_discovery = __esm(() => {
|
|
|
35307
36125
|
});
|
|
35308
36126
|
|
|
35309
36127
|
// src/lib/claude-marketplace-registry.ts
|
|
35310
|
-
import { createHash as
|
|
35311
|
-
import { closeSync as
|
|
35312
|
-
import { basename as basename8, dirname as
|
|
36128
|
+
import { createHash as createHash19 } from "crypto";
|
|
36129
|
+
import { closeSync as closeSync16, constants as constants14, fstatSync as fstatSync16, lstatSync as lstatSync20, openSync as openSync16, readSync as readSync11 } from "fs";
|
|
36130
|
+
import { basename as basename8, dirname as dirname18, isAbsolute as isAbsolute18, resolve as resolve19 } from "path";
|
|
35313
36131
|
function need8(value, reason2) {
|
|
35314
36132
|
if (!value)
|
|
35315
36133
|
throw new Error(`Claude marketplace registry ${reason2}`);
|
|
35316
36134
|
}
|
|
35317
36135
|
function absolutePath4(value) {
|
|
35318
|
-
need8(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) &&
|
|
36136
|
+
need8(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute18(value) && resolve19(value) === value, "requires a normalized absolute path");
|
|
35319
36137
|
}
|
|
35320
36138
|
function parents2(path) {
|
|
35321
36139
|
const result = [];
|
|
35322
|
-
for (let at =
|
|
35323
|
-
const stat2 =
|
|
36140
|
+
for (let at = dirname18(path);; at = dirname18(at)) {
|
|
36141
|
+
const stat2 = lstatSync20(at, { bigint: true });
|
|
35324
36142
|
need8(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
35325
36143
|
result.push([at, stat2]);
|
|
35326
|
-
if (at ===
|
|
36144
|
+
if (at === dirname18(at))
|
|
35327
36145
|
return result;
|
|
35328
36146
|
}
|
|
35329
36147
|
}
|
|
@@ -35331,25 +36149,25 @@ function readRegistry2(path, budget2) {
|
|
|
35331
36149
|
absolutePath4(path);
|
|
35332
36150
|
need8(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
|
|
35333
36151
|
need8(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
|
|
35334
|
-
const ancestors2 = parents2(path), initial =
|
|
36152
|
+
const ancestors2 = parents2(path), initial = lstatSync20(path, { bigint: true });
|
|
35335
36153
|
need8(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
|
|
35336
36154
|
need8(initial.size <= BigInt(budget2.remaining), "exceeds its aggregate byte limit");
|
|
35337
|
-
const fd =
|
|
36155
|
+
const fd = openSync16(path, constants14.O_RDONLY | constants14.O_NOFOLLOW | constants14.O_NONBLOCK);
|
|
35338
36156
|
try {
|
|
35339
|
-
const opened =
|
|
36157
|
+
const opened = fstatSync16(fd, { bigint: true });
|
|
35340
36158
|
need8(opened.isFile() && sameFile2(initial, opened), "changed during open");
|
|
35341
36159
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
35342
36160
|
let length = 0;
|
|
35343
36161
|
while (length < bytes.length) {
|
|
35344
|
-
const count =
|
|
36162
|
+
const count = readSync11(fd, bytes, length, bytes.length - length, null);
|
|
35345
36163
|
if (!count)
|
|
35346
36164
|
break;
|
|
35347
36165
|
length += count;
|
|
35348
36166
|
}
|
|
35349
|
-
const after =
|
|
36167
|
+
const after = fstatSync16(fd, { bigint: true }), current = lstatSync20(path, { bigint: true });
|
|
35350
36168
|
need8(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
35351
36169
|
for (const [ancestor, before] of ancestors2) {
|
|
35352
|
-
const now =
|
|
36170
|
+
const now = lstatSync20(ancestor, { bigint: true });
|
|
35353
36171
|
need8(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
35354
36172
|
}
|
|
35355
36173
|
budget2.remaining -= length;
|
|
@@ -35359,7 +36177,7 @@ function readRegistry2(path, budget2) {
|
|
|
35359
36177
|
throw new Error("Claude marketplace registry is not strict UTF-8");
|
|
35360
36178
|
}
|
|
35361
36179
|
} finally {
|
|
35362
|
-
|
|
36180
|
+
closeSync16(fd);
|
|
35363
36181
|
}
|
|
35364
36182
|
}
|
|
35365
36183
|
function parse11(text2) {
|
|
@@ -35436,7 +36254,7 @@ function parse11(text2) {
|
|
|
35436
36254
|
need8(root.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
|
|
35437
36255
|
return root;
|
|
35438
36256
|
}
|
|
35439
|
-
function
|
|
36257
|
+
function exactKeys2(value, keys2) {
|
|
35440
36258
|
return value?.kind === "object" && value.entries.length === keys2.length && value.entries.every(([key]) => keys2.includes(key));
|
|
35441
36259
|
}
|
|
35442
36260
|
function textField(value) {
|
|
@@ -35444,8 +36262,8 @@ function textField(value) {
|
|
|
35444
36262
|
return value.value;
|
|
35445
36263
|
}
|
|
35446
36264
|
function normalizeRow(row, version2 = 1) {
|
|
35447
|
-
if (!
|
|
35448
|
-
if (version2 !== 2 || !
|
|
36265
|
+
if (!exactKeys2(row, ["source", "installLocation", "lastUpdated"])) {
|
|
36266
|
+
if (version2 !== 2 || !exactKeys2(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
35449
36267
|
return;
|
|
35450
36268
|
const autoUpdate = field2(row, "autoUpdate");
|
|
35451
36269
|
need8(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
@@ -35457,7 +36275,7 @@ function normalizeRow(row, version2 = 1) {
|
|
|
35457
36275
|
if (type?.kind !== "string" || !["github", "directory"].includes(type.value))
|
|
35458
36276
|
return;
|
|
35459
36277
|
const location = type.value === "github" ? "repo" : "path";
|
|
35460
|
-
if (!
|
|
36278
|
+
if (!exactKeys2(source, ["source", location]))
|
|
35461
36279
|
return;
|
|
35462
36280
|
const sourceLocation = textField(field2(source, location));
|
|
35463
36281
|
if (type.value === "directory")
|
|
@@ -35473,14 +36291,14 @@ function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 102
|
|
|
35473
36291
|
const value = parse11(readRegistry2(path, budget2));
|
|
35474
36292
|
for (const [, row] of value.entries)
|
|
35475
36293
|
normalizeRow(row);
|
|
35476
|
-
const sha2562 =
|
|
36294
|
+
const sha2562 = createHash19("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
35477
36295
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
35478
36296
|
}
|
|
35479
36297
|
function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
35480
36298
|
const value = parse11(readRegistry2(path, budget2));
|
|
35481
36299
|
for (const [, row] of value.entries)
|
|
35482
36300
|
normalizeRow(row, 2);
|
|
35483
|
-
const sha2562 =
|
|
36301
|
+
const sha2562 = createHash19("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
35484
36302
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
35485
36303
|
}
|
|
35486
36304
|
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS, sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs, field2 = (object6, key) => object6.entries.find(([name]) => name === key)?.[1];
|
|
@@ -35502,7 +36320,7 @@ var init_codex_hook_additions_review = __esm(() => {
|
|
|
35502
36320
|
});
|
|
35503
36321
|
|
|
35504
36322
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
35505
|
-
import { createHash as
|
|
36323
|
+
import { createHash as createHash20 } from "crypto";
|
|
35506
36324
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
35507
36325
|
const proof = proofs2.get(witness);
|
|
35508
36326
|
need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === discoveryDigest(binding), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
@@ -35517,7 +36335,7 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
35517
36335
|
need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
35518
36336
|
return true;
|
|
35519
36337
|
}
|
|
35520
|
-
var sha4 = (text2) =>
|
|
36338
|
+
var sha4 = (text2) => createHash20("sha256").update(text2).digest("hex"), fields, discoveryDigest = (binding) => {
|
|
35521
36339
|
const controls = binding.codexDisabledPluginSkills;
|
|
35522
36340
|
if (!Object.hasOwn(binding, "codexDisabledPluginSkills") || Array.isArray(controls) && controls.length === 0) {
|
|
35523
36341
|
const { codexDisabledPluginSkills: _controls, ...withoutControls } = binding;
|
|
@@ -35535,16 +36353,16 @@ var init_codex_hook_discovery_recovery = __esm(() => {
|
|
|
35535
36353
|
});
|
|
35536
36354
|
|
|
35537
36355
|
// src/lib/agent-discovery.ts
|
|
35538
|
-
import { createHash as
|
|
35539
|
-
import { lstatSync as
|
|
35540
|
-
import { basename as basename9, dirname as
|
|
36356
|
+
import { createHash as createHash21 } from "crypto";
|
|
36357
|
+
import { lstatSync as lstatSync21, readFileSync as readFileSync16, readdirSync as readdirSync8, realpathSync as realpathSync8 } from "fs";
|
|
36358
|
+
import { basename as basename9, dirname as dirname19, isAbsolute as isAbsolute19, join as join29, relative as relative7, resolve as resolve20, sep as sep9 } from "path";
|
|
35541
36359
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
35542
36360
|
function discoveryExecutable(agent) {
|
|
35543
36361
|
const command = DISCOVERY_RUNTIME_COMMANDS[agent];
|
|
35544
36362
|
const found = command ? Bun.which(command) : null;
|
|
35545
36363
|
if (!command || !found)
|
|
35546
36364
|
return null;
|
|
35547
|
-
const path =
|
|
36365
|
+
const path = resolve20(found), target = realpathOrNull(path);
|
|
35548
36366
|
return target === null ? null : { command, path, target };
|
|
35549
36367
|
}
|
|
35550
36368
|
function parseConfig(text2, path, toml = false) {
|
|
@@ -35558,12 +36376,12 @@ function parseConfig(text2, path, toml = false) {
|
|
|
35558
36376
|
}
|
|
35559
36377
|
}
|
|
35560
36378
|
function safe5(path) {
|
|
35561
|
-
if (!
|
|
36379
|
+
if (!isAbsolute19(path) || path.includes("\x00"))
|
|
35562
36380
|
throw new Error("Expected an absolute native discovery path");
|
|
35563
|
-
for (let cursor = path;; cursor =
|
|
35564
|
-
if (
|
|
36381
|
+
for (let cursor = path;; cursor = dirname19(cursor)) {
|
|
36382
|
+
if (lstatSync21(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
35565
36383
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
35566
|
-
if (
|
|
36384
|
+
if (dirname19(cursor) === cursor)
|
|
35567
36385
|
break;
|
|
35568
36386
|
}
|
|
35569
36387
|
}
|
|
@@ -35571,27 +36389,27 @@ function read(path, changes) {
|
|
|
35571
36389
|
safe5(path);
|
|
35572
36390
|
if (changes?.has(path))
|
|
35573
36391
|
return changes.get(path);
|
|
35574
|
-
const stat2 =
|
|
36392
|
+
const stat2 = lstatSync21(path, { throwIfNoEntry: false });
|
|
35575
36393
|
if (!stat2)
|
|
35576
36394
|
return null;
|
|
35577
36395
|
if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
|
|
35578
36396
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
35579
36397
|
return readFileSync16(path, "utf8");
|
|
35580
36398
|
}
|
|
35581
|
-
function assertReviewedClaudeCacheClosures(home, config2, review,
|
|
36399
|
+
function assertReviewedClaudeCacheClosures(home, config2, review, canonical9) {
|
|
35582
36400
|
const enabledIds = Object.entries(config2.enabledPlugins ?? {}).filter(([, enabled]) => enabled === true).map(([id]) => id);
|
|
35583
36401
|
if (enabledIds.length === 0)
|
|
35584
36402
|
return;
|
|
35585
|
-
const registryPath =
|
|
35586
|
-
const cacheAliasRoot =
|
|
35587
|
-
const cacheRoot =
|
|
36403
|
+
const registryPath = canonical9(join29(home, ".claude/plugins/installed_plugins.json"));
|
|
36404
|
+
const cacheAliasRoot = join29(home, ".claude/plugins/cache");
|
|
36405
|
+
const cacheRoot = canonical9(cacheAliasRoot);
|
|
35588
36406
|
const registryText = read(registryPath);
|
|
35589
36407
|
if (registryText === null)
|
|
35590
36408
|
return;
|
|
35591
36409
|
const registry2 = parseConfig(registryText, registryPath);
|
|
35592
36410
|
if (registry2.version !== 2 || !registry2.plugins || typeof registry2.plugins !== "object" || Array.isArray(registry2.plugins))
|
|
35593
36411
|
throw new Error("Reviewed Claude installed plugin registrations are invalid");
|
|
35594
|
-
const cachePath = (path) => typeof path === "string" &&
|
|
36412
|
+
const cachePath = (path) => typeof path === "string" && isAbsolute19(path) && resolve20(path) === path && [cacheAliasRoot, cacheRoot].some((root) => path === root || path.startsWith(root + sep9));
|
|
35595
36413
|
const activeRoots = new Set;
|
|
35596
36414
|
for (const id of enabledIds) {
|
|
35597
36415
|
const rows = registry2.plugins[id];
|
|
@@ -35603,13 +36421,13 @@ function assertReviewedClaudeCacheClosures(home, config2, review, canonical8) {
|
|
|
35603
36421
|
if (!row || typeof row !== "object" || Array.isArray(row) || typeof row.installPath !== "string") {
|
|
35604
36422
|
throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
|
|
35605
36423
|
}
|
|
35606
|
-
if ([cacheAliasRoot, cacheRoot].some((root) => row.installPath === root || row.installPath.startsWith(root +
|
|
36424
|
+
if ([cacheAliasRoot, cacheRoot].some((root) => row.installPath === root || row.installPath.startsWith(root + sep9))) {
|
|
35607
36425
|
if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename9(row.installPath) !== row.version)) {
|
|
35608
36426
|
throw new Error("Reviewed Claude discovery has a malformed active cache registration");
|
|
35609
36427
|
}
|
|
35610
|
-
if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" &&
|
|
36428
|
+
if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute19(row.projectPath) && resolve20(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
|
|
35611
36429
|
throw new Error("Reviewed Claude discovery has an unsupported active cache registration scope");
|
|
35612
|
-
activeRoots.add(
|
|
36430
|
+
activeRoots.add(canonical9(row.installPath));
|
|
35613
36431
|
}
|
|
35614
36432
|
}
|
|
35615
36433
|
}
|
|
@@ -35625,11 +36443,11 @@ function assertReviewedClaudeCacheClosures(home, config2, review, canonical8) {
|
|
|
35625
36443
|
};
|
|
35626
36444
|
for (const root of activeRoots) {
|
|
35627
36445
|
safe5(root);
|
|
35628
|
-
const rootStat =
|
|
35629
|
-
if (!rootStat?.isDirectory() ||
|
|
36446
|
+
const rootStat = lstatSync21(root, { throwIfNoEntry: false });
|
|
36447
|
+
if (!rootStat?.isDirectory() || realpathSync8(root) !== root)
|
|
35630
36448
|
throw new Error("Reviewed Claude active cache plugin root is missing or unsafe");
|
|
35631
|
-
const manifestPath =
|
|
35632
|
-
const manifestStat =
|
|
36449
|
+
const manifestPath = join29(root, ".claude-plugin/plugin.json");
|
|
36450
|
+
const manifestStat = lstatSync21(manifestPath, { throwIfNoEntry: false });
|
|
35633
36451
|
let manifest;
|
|
35634
36452
|
if (manifestStat) {
|
|
35635
36453
|
if (!manifestStat.isFile())
|
|
@@ -35644,37 +36462,37 @@ function assertReviewedClaudeCacheClosures(home, config2, review, canonical8) {
|
|
|
35644
36462
|
} else if (!review.sources.some((source) => source.path === manifestPath && source.hashMode === "path-bytes" && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined)) {
|
|
35645
36463
|
throw new Error("Reviewed Claude active cache plugin manifest absence requires a path-bytes witness");
|
|
35646
36464
|
}
|
|
35647
|
-
const roots = new Set([
|
|
35648
|
-
if (
|
|
36465
|
+
const roots = new Set([join29(root, "skills")]);
|
|
36466
|
+
if (lstatSync21(join29(root, "SKILL.md"), { throwIfNoEntry: false })?.isFile())
|
|
35649
36467
|
roots.add(root);
|
|
35650
36468
|
const declared = manifest?.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
35651
36469
|
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
35652
36470
|
throw new Error("Reviewed Claude active cache plugin has unsupported skill paths");
|
|
35653
36471
|
for (const value of declared) {
|
|
35654
|
-
const path =
|
|
35655
|
-
if (path !== root && !path.startsWith(root +
|
|
36472
|
+
const path = resolve20(root, value);
|
|
36473
|
+
if (path !== root && !path.startsWith(root + sep9))
|
|
35656
36474
|
throw new Error("Reviewed Claude active cache plugin skill path escapes its root");
|
|
35657
|
-
roots.add(path.endsWith(`${
|
|
36475
|
+
roots.add(path.endsWith(`${sep9}SKILL.md`) ? dirname19(path) : path);
|
|
35658
36476
|
}
|
|
35659
36477
|
for (const skillRoot of roots)
|
|
35660
36478
|
if (!review.roots.includes(skillRoot))
|
|
35661
36479
|
throw new Error("Reviewed Claude active cache plugin skill root is not covered");
|
|
35662
|
-
const hooksPath =
|
|
35663
|
-
const hooksStat =
|
|
36480
|
+
const hooksPath = join29(root, "hooks/hooks.json");
|
|
36481
|
+
const hooksStat = lstatSync21(hooksPath, { throwIfNoEntry: false });
|
|
35664
36482
|
if (hooksStat) {
|
|
35665
36483
|
if (!hooksStat.isFile())
|
|
35666
36484
|
throw new Error("Reviewed Claude plugin hook manifest is unsupported");
|
|
35667
36485
|
requireSource(hooksPath, "hook manifest");
|
|
35668
36486
|
}
|
|
35669
36487
|
if (typeof manifest?.hooks === "string")
|
|
35670
|
-
requireSource(
|
|
36488
|
+
requireSource(resolve20(root, manifest.hooks), "manifest-declared hook source");
|
|
35671
36489
|
}
|
|
35672
36490
|
}
|
|
35673
36491
|
function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
35674
36492
|
const object6 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
|
|
35675
36493
|
if (!object6 || typeof object6 !== "object" || Array.isArray(object6))
|
|
35676
36494
|
throw new Error("Expected native discovery configuration object");
|
|
35677
|
-
return
|
|
36495
|
+
return digest2(JSON.stringify(Object.fromEntries(fields2.map((field3) => [field3, object6[field3] ?? null]))));
|
|
35678
36496
|
}
|
|
35679
36497
|
function isSupersededSettingsProjection(agent, configPath, source) {
|
|
35680
36498
|
if (agent === "sumi")
|
|
@@ -35684,7 +36502,7 @@ function isSupersededSettingsProjection(agent, configPath, source) {
|
|
|
35684
36502
|
function supersedesSettingsProjection(agent, sources, configPath) {
|
|
35685
36503
|
if (agent === "sumi")
|
|
35686
36504
|
return sources.some((source) => source.path === configPath && source.hashMode === "sumi-settings-v1");
|
|
35687
|
-
return agent === "codex" && sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")));
|
|
36505
|
+
return agent === "codex" && sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")));
|
|
35688
36506
|
}
|
|
35689
36507
|
function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
35690
36508
|
if (source.reviewArtifact !== undefined)
|
|
@@ -35697,13 +36515,13 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
|
35697
36515
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
35698
36516
|
return changes?.has(source.path) ? hashSumiSettingsReplacement(changes.get(source.path), budget2) : current;
|
|
35699
36517
|
}
|
|
35700
|
-
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) {
|
|
36518
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")) {
|
|
35701
36519
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
35702
36520
|
throw new Error("Codex settings witnesses require exact typed metadata");
|
|
35703
|
-
const current = (source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget2).sha256;
|
|
36521
|
+
const current = (source.hashMode === "codex-settings-v5" ? captureCodexSettingsV5 : source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget2).sha256;
|
|
35704
36522
|
if (current !== source.sha256)
|
|
35705
36523
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
35706
|
-
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
36524
|
+
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v5" ? hashCodexSettingsReplacementV5 : source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
35707
36525
|
}
|
|
35708
36526
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) {
|
|
35709
36527
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
@@ -35755,7 +36573,7 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
|
35755
36573
|
if (text2 === null)
|
|
35756
36574
|
return null;
|
|
35757
36575
|
if (!source.format)
|
|
35758
|
-
return
|
|
36576
|
+
return digest2(text2);
|
|
35759
36577
|
return projectNativeDiscoveryFields(text2, source.format, source.fields ?? [], source.path);
|
|
35760
36578
|
}
|
|
35761
36579
|
function absentCodexSkillRoots(binding) {
|
|
@@ -35764,18 +36582,18 @@ function absentCodexSkillRoots(binding) {
|
|
|
35764
36582
|
return [];
|
|
35765
36583
|
if (binding.agent !== "codex" || !Array.isArray(controls) || controls.length > 4096)
|
|
35766
36584
|
throw new Error("Invalid disabled Codex discovery proof");
|
|
35767
|
-
if (binding.method !== "reviewed" || controls.every((control) =>
|
|
36585
|
+
if (binding.method !== "reviewed" || controls.every((control) => lstatSync21(control.pluginParent, { throwIfNoEntry: false })))
|
|
35768
36586
|
return [];
|
|
35769
|
-
const cache =
|
|
36587
|
+
const cache = dirname19(dirname19(controls[0].pluginParent)), config2 = join29(dirname19(dirname19(cache)), "config.toml");
|
|
35770
36588
|
const source = binding.sources.find((source2) => source2.path === config2 && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
35771
36589
|
if (!source || projected(source) !== source.sha256)
|
|
35772
36590
|
throw new Error("Missing current disabled Codex configuration witness");
|
|
35773
36591
|
const rules = Bun.TOML.parse(read(config2)).skills?.config ?? [];
|
|
35774
36592
|
return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
|
|
35775
36593
|
}
|
|
35776
|
-
function codexDirectHookSelects(cache, root) {
|
|
35777
|
-
const codex =
|
|
35778
|
-
const hooks = read(
|
|
36594
|
+
function codexDirectHookSelects(cache, root, configPath = join29(dirname19(dirname19(cache)), "config.toml")) {
|
|
36595
|
+
const codex = dirname19(configPath), config2 = Bun.TOML.parse(read(configPath) ?? "");
|
|
36596
|
+
const hooks = read(join29(codex, "hooks.json"));
|
|
35779
36597
|
if (hooks !== null)
|
|
35780
36598
|
hashNativeJsonControls(hooks);
|
|
35781
36599
|
return JSON.stringify(config2.hooks ?? {}).includes(root) || hooks !== null && JSON.stringify(JSON.parse(hooks)).includes(root);
|
|
@@ -35787,7 +36605,7 @@ function retiredCodexRoots(binding) {
|
|
|
35787
36605
|
const controls = binding.codexDisabledPluginSkills;
|
|
35788
36606
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !Array.isArray(controls) || !controls.length || !Array.isArray(proof.roots) || proof.roots.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.roots).size !== proof.roots.length || !Array.isArray(proof.directories) || proof.parents !== undefined && (!Array.isArray(proof.parents) || proof.parents.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.parents).size !== proof.parents.length) || !proof.roots.length && !proof.parents?.length)
|
|
35789
36607
|
throw new Error("Invalid retired Codex materialization proof");
|
|
35790
|
-
const cache =
|
|
36608
|
+
const cache = dirname19(dirname19(controls[0].pluginParent)), config2 = join29(dirname19(dirname19(cache)), "config.toml");
|
|
35791
36609
|
const source = binding.sources.find((source2) => source2.path === config2 && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
35792
36610
|
if (!source || projected(source) !== source.sha256)
|
|
35793
36611
|
throw new Error("Missing current retired Codex configuration witness");
|
|
@@ -35795,7 +36613,7 @@ function retiredCodexRoots(binding) {
|
|
|
35795
36613
|
if ((proof.parents ?? []).some((parent) => codexDirectHookSelects(cache, parent) || !reviewedDisabledCodexPluginParent(cache, parent, controls, rules)) || !reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read(path), rules))
|
|
35796
36614
|
throw new Error("Retired Codex parent capabilities changed");
|
|
35797
36615
|
for (const root of proof.roots) {
|
|
35798
|
-
const manifest = binding.sources.find((source2) => source2.path ===
|
|
36616
|
+
const manifest = binding.sources.find((source2) => source2.path === join29(root, ".codex-plugin/plugin.json") && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && [undefined, "bytes"].includes(source2.hashMode));
|
|
35799
36617
|
if (!manifest || codexDirectHookSelects(cache, root) || !reviewedRetiredCodexRoot(cache, root, controls, rules, (path) => read(path), true))
|
|
35800
36618
|
throw new Error("Retired Codex materialization changed");
|
|
35801
36619
|
}
|
|
@@ -35804,7 +36622,7 @@ function retiredCodexRoots(binding) {
|
|
|
35804
36622
|
function retainableAbsentSource(source) {
|
|
35805
36623
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined)
|
|
35806
36624
|
return false;
|
|
35807
|
-
return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(
|
|
36625
|
+
return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json"));
|
|
35808
36626
|
}
|
|
35809
36627
|
function absentRetiredClaudeRoots(binding) {
|
|
35810
36628
|
if (binding.agent !== "claude" || binding.method !== "reviewed")
|
|
@@ -35813,17 +36631,17 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
35813
36631
|
for (const source of binding.sources) {
|
|
35814
36632
|
if (!retainableAbsentSource(source) || source.sha256 === null)
|
|
35815
36633
|
continue;
|
|
35816
|
-
const marker =
|
|
36634
|
+
const marker = sep9 + ".claude" + sep9 + "plugins" + sep9 + "cache" + sep9, at = source.path.indexOf(marker);
|
|
35817
36635
|
if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
|
|
35818
36636
|
continue;
|
|
35819
|
-
const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(
|
|
36637
|
+
const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(sep9);
|
|
35820
36638
|
if (parts.length < 4 || parts.slice(0, 3).some((part) => !part || part === "." || part === ".."))
|
|
35821
36639
|
continue;
|
|
35822
|
-
const root =
|
|
36640
|
+
const root = join29(cache, ...parts.slice(0, 3));
|
|
35823
36641
|
safe5(root);
|
|
35824
|
-
if (
|
|
36642
|
+
if (lstatSync21(root, { throwIfNoEntry: false }))
|
|
35825
36643
|
continue;
|
|
35826
|
-
const claude =
|
|
36644
|
+
const claude = dirname19(dirname19(cache)), settings = join29(claude, "settings.json"), registry2 = join29(claude, "plugins/installed_plugins.json");
|
|
35827
36645
|
const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
|
|
35828
36646
|
const configSource = full(settings), registrySource = full(registry2);
|
|
35829
36647
|
if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
|
|
@@ -35840,15 +36658,15 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
35840
36658
|
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
35841
36659
|
return null;
|
|
35842
36660
|
const value = row;
|
|
35843
|
-
if (typeof value.installPath !== "string" || /[\x00-\x1f\x7f]/.test(value.installPath) || !
|
|
36661
|
+
if (typeof value.installPath !== "string" || /[\x00-\x1f\x7f]/.test(value.installPath) || !isAbsolute19(value.installPath) || resolve20(value.installPath) !== value.installPath || value.installPath.length > AGENT_POLICY_LIMITS.pathCharacters)
|
|
35844
36662
|
return null;
|
|
35845
36663
|
if (value.scope === "user")
|
|
35846
36664
|
return value.projectPath === undefined || value.projectPath === null ? "user\x00" : null;
|
|
35847
36665
|
if (value.scope === "project")
|
|
35848
|
-
return typeof value.projectPath === "string" && !/[\x00-\x1f\x7f]/.test(value.projectPath) && value.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters &&
|
|
36666
|
+
return typeof value.projectPath === "string" && !/[\x00-\x1f\x7f]/.test(value.projectPath) && value.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute19(value.projectPath) && resolve20(value.projectPath) === value.projectPath ? `project\x00${value.projectPath}` : null;
|
|
35849
36667
|
return null;
|
|
35850
36668
|
};
|
|
35851
|
-
if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root || row.installPath.startsWith(root +
|
|
36669
|
+
if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root || row.installPath.startsWith(root + sep9) || root.startsWith(row.installPath + sep9))))
|
|
35852
36670
|
continue;
|
|
35853
36671
|
const rows = installed.plugins[id];
|
|
35854
36672
|
if (!Array.isArray(rows) || rows.length < 1)
|
|
@@ -35866,36 +36684,94 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
35866
36684
|
if (!targetValid || !targetScopes.has("user\x00"))
|
|
35867
36685
|
continue;
|
|
35868
36686
|
const active = rows[0]?.installPath;
|
|
35869
|
-
if (typeof active !== "string" || rows[0]?.version !== basename9(active) ||
|
|
36687
|
+
if (typeof active !== "string" || rows[0]?.version !== basename9(active) || dirname19(active) !== dirname19(root) || active === root || !lstatSync21(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync8(active) !== active)
|
|
35870
36688
|
continue;
|
|
35871
|
-
const activeManifest = full(
|
|
36689
|
+
const activeManifest = full(join29(active, ".claude-plugin/plugin.json"));
|
|
35872
36690
|
if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
|
|
35873
36691
|
continue;
|
|
35874
36692
|
result.add(root);
|
|
35875
36693
|
}
|
|
35876
36694
|
return [...result];
|
|
35877
36695
|
}
|
|
35878
|
-
function
|
|
36696
|
+
function codexSemanticCacheSourceRole(source, cacheRoot, parents3) {
|
|
36697
|
+
if (!isAbsolute19(source.path) || resolve20(source.path) !== source.path || /[\x00-\x1f\x7f]/.test(source.path) || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.reviewArtifact !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes")
|
|
36698
|
+
return false;
|
|
36699
|
+
const parts = relative7(cacheRoot, source.path).split(sep9);
|
|
36700
|
+
if (parts.length < 3 || !parents3.includes(join29(cacheRoot, parts[0], parts[1])))
|
|
36701
|
+
return false;
|
|
36702
|
+
const tail = parts.slice(3).join("/");
|
|
36703
|
+
return parts.length === 5 && tail === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail === "SKILL.md" || parts.length === 5 && tail === "hooks/hooks.json");
|
|
36704
|
+
}
|
|
36705
|
+
function codexSemanticCacheDirectoryRole(path, cacheRoot) {
|
|
36706
|
+
return isAbsolute19(path) && resolve20(path) === path && !/[\x00-\x1f\x7f]/.test(path) && (path === cacheRoot || path.startsWith(cacheRoot + sep9));
|
|
36707
|
+
}
|
|
36708
|
+
function assertCodexSemanticCacheHookSafety(cacheRoot, parents3, configPath, hookRootAlias = null) {
|
|
36709
|
+
const selectionRoots = [cacheRoot, ...parents3];
|
|
36710
|
+
if (hookRootAlias !== null) {
|
|
36711
|
+
if (!hookRootAlias || Object.keys(hookRootAlias).length !== 2 || [hookRootAlias.alias, hookRootAlias.target].some((path) => typeof path !== "string" || !isAbsolute19(path) || resolve20(path) !== path || /[\x00-\x1f\x7f]/.test(path)) || hookRootAlias.target !== dirname19(configPath) || cacheRoot !== join29(hookRootAlias.target, "plugins/cache") || !lstatSync21(hookRootAlias.alias, { throwIfNoEntry: false })?.isSymbolicLink() || realpathSync8(hookRootAlias.alias) !== hookRootAlias.target || realpathSync8(hookRootAlias.target) !== hookRootAlias.target)
|
|
36712
|
+
throw new Error("Reviewed semantic cache hook alias changed");
|
|
36713
|
+
selectionRoots.push(...selectionRoots.map((root) => join29(hookRootAlias.alias, relative7(hookRootAlias.target, root))));
|
|
36714
|
+
}
|
|
36715
|
+
selectionRoots.push("plugins/cache");
|
|
36716
|
+
if (selectionRoots.some((parent) => codexDirectHookSelects(cacheRoot, parent, configPath)))
|
|
36717
|
+
throw new Error("Direct native hooks select a projected Codex cache input");
|
|
36718
|
+
}
|
|
36719
|
+
function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases) {
|
|
36720
|
+
const proof = binding.codexSemanticCache;
|
|
36721
|
+
if (!proof)
|
|
36722
|
+
return;
|
|
36723
|
+
if (reviewedRootAliases !== undefined) {
|
|
36724
|
+
const alias = reviewedRootAliases.find((alias2) => alias2.agent === "codex");
|
|
36725
|
+
const expected = alias ? { alias: alias.alias, target: alias.target } : null;
|
|
36726
|
+
if (!isDeepStrictEqual4(proof.hookRootAlias, expected))
|
|
36727
|
+
throw new Error("Semantic cache hook alias differs from reviewed root binding");
|
|
36728
|
+
}
|
|
36729
|
+
const controls = binding.codexDisabledPluginSkills;
|
|
36730
|
+
if (binding.agent !== "codex" || binding.method !== "reviewed" || !controls?.length || proof.schema !== "hasna.codex-semantic-cache-discovery.v1" || !/^[a-f0-9]{64}$/.test(proof.previousPolicySha256) || !Array.isArray(proof.appOnlyParents) || !Array.isArray(proof.supersededSources) || !Array.isArray(proof.supersededDirectories))
|
|
36731
|
+
throw new Error("Invalid Codex semantic cache discovery proof");
|
|
36732
|
+
const cacheRoot = proof.witness.cacheRoot;
|
|
36733
|
+
if (controls.some((control) => dirname19(dirname19(control.pluginParent)) !== cacheRoot))
|
|
36734
|
+
throw new Error("Codex semantic cache names another enrolled cache");
|
|
36735
|
+
const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent)])];
|
|
36736
|
+
const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3));
|
|
36737
|
+
const directories = (binding.directories ?? []).filter((directory) => codexSemanticCacheDirectoryRole(directory.path, cacheRoot));
|
|
36738
|
+
if (!directories.length || !isDeepStrictEqual4(sources, proof.supersededSources) || !isDeepStrictEqual4(directories, proof.supersededDirectories))
|
|
36739
|
+
throw new Error("Codex semantic cache provenance changed");
|
|
36740
|
+
const configPath = proof.configPath;
|
|
36741
|
+
if (!isAbsolute19(configPath) || resolve20(configPath) !== configPath || basename9(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
|
|
36742
|
+
throw new Error("Invalid semantic cache configuration source");
|
|
36743
|
+
const configSource = binding.sources.find((source) => source.path === configPath && source.sha256 !== null && source.format === undefined && source.fields === undefined);
|
|
36744
|
+
if (!configSource || projected(configSource) !== configSource.sha256)
|
|
36745
|
+
throw new Error("Codex semantic cache requires unchanged reviewed configuration");
|
|
36746
|
+
assertCodexSemanticCacheHookSafety(cacheRoot, parents3, configPath, proof.hookRootAlias);
|
|
36747
|
+
const rules = parseConfig(read(configPath), configPath, true).skills?.config ?? [];
|
|
36748
|
+
const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, rules });
|
|
36749
|
+
assertCodexSemanticCacheWitnessUnchanged(proof.witness, current);
|
|
36750
|
+
return proof;
|
|
36751
|
+
}
|
|
36752
|
+
function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases) {
|
|
35879
36753
|
if (codexRecovery)
|
|
35880
36754
|
assertCodexHookDiscoveryRecovery(binding, codexRecovery);
|
|
35881
36755
|
if (!binding || !Array.isArray(binding.sources) || !Array.isArray(binding.roots) || binding.sources.length > AGENT_POLICY_LIMITS.discoverySources || binding.roots.length > AGENT_POLICY_LIMITS.discoveryRoots)
|
|
35882
36756
|
throw new Error("Invalid native discovery binding");
|
|
35883
36757
|
if (binding.agent === "hermes" && !binding.directories?.length)
|
|
35884
36758
|
throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
|
|
36759
|
+
const semanticCache = verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases);
|
|
36760
|
+
const semanticDirectory = (path) => Boolean(semanticCache && codexSemanticCacheDirectoryRole(path, semanticCache.witness.cacheRoot));
|
|
35885
36761
|
const installationRoots = codexInstallationRoots(binding);
|
|
35886
36762
|
const retiredRoots = retiredCodexRoots(binding);
|
|
35887
|
-
const installationInput = (path) => installationRoots.some((root) => path === root || path.startsWith(root +
|
|
36763
|
+
const installationInput = (path) => installationRoots.some((root) => path === root || path.startsWith(root + sep9));
|
|
35888
36764
|
if (binding.directories !== undefined) {
|
|
35889
36765
|
const projected2 = binding.codexInstallationInputs?.directories ?? [];
|
|
35890
|
-
const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root) => root.startsWith(directory.path +
|
|
36766
|
+
const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root) => root.startsWith(directory.path + sep9)));
|
|
35891
36767
|
if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
|
|
35892
36768
|
throw new Error("Missing installation input directory projection");
|
|
35893
|
-
const ordinary = binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory));
|
|
36769
|
+
const ordinary = binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory) && !semanticDirectory(directory.path));
|
|
35894
36770
|
if (binding.codexRetiredMaterializations) {
|
|
35895
|
-
const projection = binding.codexRetiredMaterializations.directories;
|
|
36771
|
+
const projection = binding.codexRetiredMaterializations.directories.filter((directory) => !semanticDirectory(directory.path));
|
|
35896
36772
|
if (projection.length !== ordinary.length || projection.some((item, index) => item.path !== ordinary[index]?.path))
|
|
35897
36773
|
throw new Error("Missing retired Codex directory projection");
|
|
35898
|
-
const omitted = [...retiredRoots, ...(binding.codexRetiredMaterializations.parents ?? []).filter((parent) => !
|
|
36774
|
+
const omitted = [...retiredRoots, ...(binding.codexRetiredMaterializations.parents ?? []).filter((parent) => !lstatSync21(parent, { throwIfNoEntry: false }))];
|
|
35899
36775
|
if (projection.every((item) => Array.isArray(item.entries)))
|
|
35900
36776
|
verifyDiscoveryDirectoryProjection(projection, omitted);
|
|
35901
36777
|
else {
|
|
@@ -35909,7 +36785,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
35909
36785
|
verifyDiscoveryDirectories(ordinary);
|
|
35910
36786
|
verifyDiscoveryDirectories(projected2, installationRoots);
|
|
35911
36787
|
}
|
|
35912
|
-
const absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root) => !
|
|
36788
|
+
const absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root) => !lstatSync21(root, { throwIfNoEntry: false }))];
|
|
35913
36789
|
const budget2 = discoveryByteBudget();
|
|
35914
36790
|
for (const source of binding.sources) {
|
|
35915
36791
|
assertMarketplaceBinding(binding, source);
|
|
@@ -35919,11 +36795,15 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
35919
36795
|
throw new Error("Invalid native discovery projection");
|
|
35920
36796
|
if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
35921
36797
|
throw new Error("Invalid native discovery digest");
|
|
36798
|
+
if (semanticCache?.supersededSources.some((previous) => isDeepStrictEqual4(previous, source))) {
|
|
36799
|
+
safe5(source.path);
|
|
36800
|
+
continue;
|
|
36801
|
+
}
|
|
35922
36802
|
if (installationInput(source.path)) {
|
|
35923
36803
|
safe5(source.path);
|
|
35924
36804
|
continue;
|
|
35925
36805
|
}
|
|
35926
|
-
if (absentRoots.some((root) => source.path.startsWith(root +
|
|
36806
|
+
if (absentRoots.some((root) => source.path.startsWith(root + sep9)) && retainableAbsentSource(source)) {
|
|
35927
36807
|
safe5(source.path);
|
|
35928
36808
|
continue;
|
|
35929
36809
|
}
|
|
@@ -35935,7 +36815,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
35935
36815
|
safe5(root);
|
|
35936
36816
|
for (const root of absentRoots) {
|
|
35937
36817
|
safe5(root);
|
|
35938
|
-
if (
|
|
36818
|
+
if (lstatSync21(root, { throwIfNoEntry: false }))
|
|
35939
36819
|
throw new Error("Retired native materialization changed during discovery verification");
|
|
35940
36820
|
}
|
|
35941
36821
|
}
|
|
@@ -35945,13 +36825,13 @@ function codexInstallationRoots(binding) {
|
|
|
35945
36825
|
return [];
|
|
35946
36826
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !supportsCodexNativeCapability(proof.version, "installed-plugin-review") || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
|
|
35947
36827
|
throw new Error("Invalid native installation input proof");
|
|
35948
|
-
const cache =
|
|
35949
|
-
const config2 =
|
|
36828
|
+
const cache = dirname19(dirname19(proof.plugins[0].pluginParent));
|
|
36829
|
+
const config2 = join29(dirname19(dirname19(cache)), "config.toml"), home = dirname19(dirname19(dirname19(cache)));
|
|
35950
36830
|
const configSource = binding.sources.find((source) => source.path === config2 && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
|
|
35951
|
-
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) =>
|
|
35952
|
-
if (!configSource || proof.plugins.some((input2) => nativeRoots.some((root) => root === input2.sourceRoot || root.startsWith(input2.sourceRoot +
|
|
36831
|
+
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join29(home, path));
|
|
36832
|
+
if (!configSource || proof.plugins.some((input2) => nativeRoots.some((root) => root === input2.sourceRoot || root.startsWith(input2.sourceRoot + sep9) || input2.sourceRoot.startsWith(root + sep9)) || binding.sources.some((source) => basename9(source.path) === "config.toml" && (source.path === input2.sourceRoot || source.path.startsWith(input2.sourceRoot + sep9)))))
|
|
35953
36833
|
throw new Error("Native installation input overlaps configuration or loading roots");
|
|
35954
|
-
if (proof.plugins.some((input2) =>
|
|
36834
|
+
if (proof.plugins.some((input2) => dirname19(dirname19(input2.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join29(input2.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
|
|
35955
36835
|
throw new Error("Missing native installation input source witness");
|
|
35956
36836
|
return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
|
|
35957
36837
|
const text2 = read(path);
|
|
@@ -35961,11 +36841,11 @@ function codexInstallationRoots(binding) {
|
|
|
35961
36841
|
});
|
|
35962
36842
|
}
|
|
35963
36843
|
function assertMarketplaceBinding(binding, source) {
|
|
35964
|
-
if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(
|
|
36844
|
+
if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json"))))
|
|
35965
36845
|
throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
|
|
35966
36846
|
if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename9(source.path) !== "sumi.json"))
|
|
35967
36847
|
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
35968
|
-
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
36848
|
+
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
35969
36849
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
35970
36850
|
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename9(source.path) !== "settings.json"))
|
|
35971
36851
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
@@ -35977,13 +36857,13 @@ function assertMarketplaceBinding(binding, source) {
|
|
|
35977
36857
|
function agentDiscoveryConfigPath(home, agent) {
|
|
35978
36858
|
if (agent === "sumi")
|
|
35979
36859
|
return sumiConfigPath(home);
|
|
35980
|
-
return
|
|
36860
|
+
return join29(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
35981
36861
|
}
|
|
35982
36862
|
function resolveAgentDiscovery(options) {
|
|
35983
|
-
const
|
|
36863
|
+
const canonical9 = options.canonical ?? resolve20, home = resolve20(options.home), agent = options.agent;
|
|
35984
36864
|
const sources = [], roots = new Set, builtinNames = [];
|
|
35985
36865
|
const witness = (path, format, fields2, hashMode) => {
|
|
35986
|
-
const source = { path:
|
|
36866
|
+
const source = { path: canonical9(path), sha256: null, ...format ? { format, fields: fields2 } : {}, ...hashMode ? { hashMode } : {} };
|
|
35987
36867
|
source.sha256 = projected(source);
|
|
35988
36868
|
sources.push(source);
|
|
35989
36869
|
return read(source.path);
|
|
@@ -36001,42 +36881,42 @@ function resolveAgentDiscovery(options) {
|
|
|
36001
36881
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
36002
36882
|
if (agent === "sumi") {
|
|
36003
36883
|
const directory = sumiConfigDirectory(home);
|
|
36004
|
-
if (witness(
|
|
36884
|
+
if (witness(join29(directory, "sumi.jsonc")) !== null)
|
|
36005
36885
|
unresolved("JSONC configuration requires a supported format adapter");
|
|
36006
|
-
for (const root of [directory,
|
|
36886
|
+
for (const root of [directory, join29(home, ".claude"), join29(home, ".agents")])
|
|
36007
36887
|
for (const name of ["skill", "skills"])
|
|
36008
|
-
roots.add(
|
|
36888
|
+
roots.add(canonical9(join29(root, name)));
|
|
36009
36889
|
if (config2.skills !== undefined && (!Array.isArray(config2.skills) || config2.skills.some((path) => typeof path !== "string")))
|
|
36010
36890
|
unresolved("skills must be a string array");
|
|
36011
36891
|
for (const value of config2.skills ?? []) {
|
|
36012
|
-
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(
|
|
36892
|
+
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute19(value) || value.startsWith("~/")))
|
|
36013
36893
|
unresolved("remote or relative skill sources need a dedicated discovery adapter");
|
|
36014
|
-
roots.add(
|
|
36894
|
+
roots.add(canonical9(value.startsWith("~/") ? join29(home, value.slice(2)) : value));
|
|
36015
36895
|
}
|
|
36016
36896
|
for (const key of ["plugin", "plugins"])
|
|
36017
36897
|
if (config2[key] !== undefined && !Array.isArray(config2[key]))
|
|
36018
36898
|
unresolved(`${key} must be an array`);
|
|
36019
|
-
const plugins = [
|
|
36899
|
+
const plugins = [join29(directory, "plugin"), join29(directory, "plugins")];
|
|
36020
36900
|
for (const path of plugins) {
|
|
36021
36901
|
safe5(path);
|
|
36022
|
-
if (
|
|
36023
|
-
for (const name of
|
|
36902
|
+
if (lstatSync21(path, { throwIfNoEntry: false })?.isDirectory())
|
|
36903
|
+
for (const name of readdirSync8(path).sort()) {
|
|
36024
36904
|
if (name === "skills-cli.js" && path === plugins[1])
|
|
36025
36905
|
continue;
|
|
36026
|
-
const target =
|
|
36027
|
-
if (!
|
|
36906
|
+
const target = join29(path, name);
|
|
36907
|
+
if (!lstatSync21(target).isFile())
|
|
36028
36908
|
unresolved("local plugin packages need a dedicated discovery adapter");
|
|
36029
36909
|
witness(target);
|
|
36030
36910
|
}
|
|
36031
36911
|
}
|
|
36032
36912
|
for (const root of roots) {
|
|
36033
36913
|
safe5(root);
|
|
36034
|
-
if (
|
|
36914
|
+
if (lstatSync21(root, { throwIfNoEntry: false })?.isDirectory() && readdirSync8(root).some((name) => name.endsWith(".md")))
|
|
36035
36915
|
unresolved("flat markdown skills require a dedicated native migration adapter");
|
|
36036
36916
|
}
|
|
36037
36917
|
}
|
|
36038
36918
|
if (agent === "hermes") {
|
|
36039
|
-
const profile = witness(
|
|
36919
|
+
const profile = witness(join29(home, ".hermes/active_profile"));
|
|
36040
36920
|
if (profile?.trim() && profile.trim() !== "default")
|
|
36041
36921
|
unresolved("a non-default profile is active");
|
|
36042
36922
|
const declared = config2.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
|
|
@@ -36050,7 +36930,7 @@ function resolveAgentDiscovery(options) {
|
|
|
36050
36930
|
continue;
|
|
36051
36931
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
36052
36932
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
36053
|
-
const path = normalized === "~" ? home : normalized.startsWith("~/") ?
|
|
36933
|
+
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join29(home, normalized.slice(2)) : resolve20(home, ".hermes", normalized);
|
|
36054
36934
|
safe5(path);
|
|
36055
36935
|
roots.add(path);
|
|
36056
36936
|
}
|
|
@@ -36059,24 +36939,24 @@ function resolveAgentDiscovery(options) {
|
|
|
36059
36939
|
const executable = options.executable === undefined ? discoveryExecutable(agent) : options.executable;
|
|
36060
36940
|
if (executable) {
|
|
36061
36941
|
let packageRoot;
|
|
36062
|
-
for (let directory =
|
|
36063
|
-
const path =
|
|
36942
|
+
for (let directory = dirname19(executable.target), depth = 0;depth < 8; directory = dirname19(directory), depth++) {
|
|
36943
|
+
const path = join29(directory, "package.json"), raw = read(path);
|
|
36064
36944
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
36065
36945
|
packageRoot = directory;
|
|
36066
36946
|
witness(path);
|
|
36067
36947
|
break;
|
|
36068
36948
|
}
|
|
36069
|
-
if (
|
|
36949
|
+
if (dirname19(directory) === directory)
|
|
36070
36950
|
break;
|
|
36071
36951
|
}
|
|
36072
36952
|
if (!packageRoot)
|
|
36073
36953
|
unresolved("installed Gemini package layout is not recognized");
|
|
36074
|
-
const builtinRoot =
|
|
36954
|
+
const builtinRoot = join29(packageRoot, "bundle/builtin");
|
|
36075
36955
|
safe5(builtinRoot);
|
|
36076
|
-
if (!
|
|
36956
|
+
if (!lstatSync21(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
36077
36957
|
unresolved("installed Gemini builtin root is missing");
|
|
36078
|
-
for (const name of
|
|
36079
|
-
const text2 = witness(
|
|
36958
|
+
for (const name of readdirSync8(builtinRoot).sort()) {
|
|
36959
|
+
const text2 = witness(join29(builtinRoot, name, "SKILL.md"));
|
|
36080
36960
|
const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
|
|
36081
36961
|
if (!match?.[1] || builtinNames.includes(match[1]))
|
|
36082
36962
|
unresolved("installed Gemini builtin name is missing or ambiguous");
|
|
@@ -36089,11 +36969,16 @@ function resolveAgentDiscovery(options) {
|
|
|
36089
36969
|
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
36090
36970
|
unresolved("invalid retained review");
|
|
36091
36971
|
verifyAgentDiscovery(retained);
|
|
36092
|
-
if (!retained.sources.some((source) => source.path ===
|
|
36972
|
+
if (!retained.sources.some((source) => source.path === canonical9(configPath) && source.format === undefined && source.fields === undefined))
|
|
36093
36973
|
unresolved("retained review is missing the full agent configuration source");
|
|
36094
|
-
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !==
|
|
36974
|
+
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")))) && source.path !== canonical9(configPath)))
|
|
36095
36975
|
unresolved("retained settings witness names another configuration source");
|
|
36096
|
-
|
|
36976
|
+
let covered = supersedesSettingsProjection(agent, retained.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
|
|
36977
|
+
if (retained.codexSemanticCache) {
|
|
36978
|
+
const proof = retained.codexSemanticCache;
|
|
36979
|
+
const parents3 = [...new Set([...(retained.codexDisabledPluginSkills ?? []).map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent)])];
|
|
36980
|
+
covered = covered.filter((source) => !codexSemanticCacheSourceRole(source, proof.witness.cacheRoot, parents3));
|
|
36981
|
+
}
|
|
36097
36982
|
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual4(saved, source))) || [...roots].some((root) => !retained.roots.includes(root)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
36098
36983
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
36099
36984
|
return retained;
|
|
@@ -36105,16 +36990,16 @@ function resolveAgentDiscovery(options) {
|
|
|
36105
36990
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
36106
36991
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
36107
36992
|
verifyAgentDiscovery(supplied);
|
|
36108
|
-
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !==
|
|
36993
|
+
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")))) && source.path !== canonical9(configPath)))
|
|
36109
36994
|
throw new Error("Semantic settings witnesses must name the configured agent configuration source");
|
|
36110
|
-
if (!review.sources.some((source) => source.path ===
|
|
36995
|
+
if (!review.sources.some((source) => source.path === canonical9(configPath)))
|
|
36111
36996
|
throw new Error("Discovery review must include the agent configuration source");
|
|
36112
|
-
const reviewedSources = supersedesSettingsProjection(agent, review.sources,
|
|
36997
|
+
const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
|
|
36113
36998
|
const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
|
|
36114
|
-
if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(
|
|
36999
|
+
if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json")))
|
|
36115
37000
|
return source;
|
|
36116
|
-
const pluginRoot =
|
|
36117
|
-
const hookStat =
|
|
37001
|
+
const pluginRoot = dirname19(dirname19(source.path)), hooksPath = join29(pluginRoot, "hooks/hooks.json");
|
|
37002
|
+
const hookStat = lstatSync21(hooksPath, { throwIfNoEntry: false });
|
|
36118
37003
|
const text2 = read(source.path);
|
|
36119
37004
|
if (text2 === null)
|
|
36120
37005
|
throw new Error("Reviewed Claude plugin manifest is missing");
|
|
@@ -36128,58 +37013,58 @@ function resolveAgentDiscovery(options) {
|
|
|
36128
37013
|
if (manifest.hooks !== undefined) {
|
|
36129
37014
|
if (typeof manifest.hooks !== "string")
|
|
36130
37015
|
throw new Error("Reviewed Claude plugin manifest has an unsupported hooks target");
|
|
36131
|
-
const declaredHookPath =
|
|
36132
|
-
if (!declaredHookPath.startsWith(pluginRoot +
|
|
37016
|
+
const declaredHookPath = resolve20(pluginRoot, manifest.hooks);
|
|
37017
|
+
if (!declaredHookPath.startsWith(pluginRoot + sep9))
|
|
36133
37018
|
throw new Error("Reviewed Claude plugin hooks target escapes its root");
|
|
36134
37019
|
reviewedHookPaths.add(declaredHookPath);
|
|
36135
37020
|
}
|
|
36136
37021
|
for (const hooksFile of reviewedHookPaths)
|
|
36137
|
-
if (!review.sources.some((hook) => hook.path ===
|
|
37022
|
+
if (!review.sources.some((hook) => hook.path === canonical9(hooksFile) && hook.sha256 !== null && hook.format === undefined && hook.fields === undefined && hook.managedPlugins === undefined && (hook.hashMode === undefined || hook.hashMode === "bytes")))
|
|
36138
37023
|
throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
|
|
36139
37024
|
return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
|
|
36140
37025
|
});
|
|
36141
37026
|
if (agent === "claude")
|
|
36142
|
-
assertReviewedClaudeCacheClosures(home, config2, review,
|
|
37027
|
+
assertReviewedClaudeCacheClosures(home, config2, review, canonical9);
|
|
36143
37028
|
const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
|
|
36144
37029
|
const unique = combined.filter((source, index) => combined.findIndex((candidate) => isDeepStrictEqual4(candidate, source)) === index);
|
|
36145
37030
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
|
36146
37031
|
}
|
|
36147
37032
|
function plugin(root) {
|
|
36148
|
-
root =
|
|
37033
|
+
root = canonical9(root);
|
|
36149
37034
|
safe5(root);
|
|
36150
|
-
const manifestPath =
|
|
37035
|
+
const manifestPath = join29(root, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
36151
37036
|
const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
|
|
36152
37037
|
if (raw === null)
|
|
36153
37038
|
unresolved("plugin manifest missing");
|
|
36154
37039
|
const manifest = parseConfig(raw, manifestPath);
|
|
36155
|
-
const hooks = witness(
|
|
37040
|
+
const hooks = witness(join29(root, "hooks/hooks.json"));
|
|
36156
37041
|
if (manifest.hooks !== undefined || hooks !== null)
|
|
36157
37042
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
36158
|
-
const commands =
|
|
37043
|
+
const commands = join29(root, "commands");
|
|
36159
37044
|
safe5(commands);
|
|
36160
|
-
if (agent === "claude" && (manifest.commands !== undefined ||
|
|
37045
|
+
if (agent === "claude" && (manifest.commands !== undefined || lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync8(commands).length))
|
|
36161
37046
|
unresolved("legacy command discovery requires retirement or a dedicated format adapter");
|
|
36162
|
-
const rootSkill = witness(
|
|
37047
|
+
const rootSkill = witness(join29(root, "SKILL.md"));
|
|
36163
37048
|
if (rootSkill !== null)
|
|
36164
37049
|
roots.add(root);
|
|
36165
37050
|
const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
36166
37051
|
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
36167
37052
|
unresolved("unsupported plugin skill paths");
|
|
36168
37053
|
if (agent === "claude")
|
|
36169
|
-
roots.add(
|
|
37054
|
+
roots.add(join29(root, "skills"));
|
|
36170
37055
|
for (const value of declared) {
|
|
36171
|
-
const path =
|
|
36172
|
-
if (path !== root && !path.startsWith(root +
|
|
37056
|
+
const path = resolve20(root, value);
|
|
37057
|
+
if (path !== root && !path.startsWith(root + sep9))
|
|
36173
37058
|
unresolved("plugin skill path escapes its root");
|
|
36174
|
-
roots.add(path.endsWith(`${
|
|
37059
|
+
roots.add(path.endsWith(`${sep9}SKILL.md`) ? dirname19(path) : path);
|
|
36175
37060
|
}
|
|
36176
37061
|
}
|
|
36177
37062
|
if (agent === "claude") {
|
|
36178
|
-
const registrations = witness(
|
|
36179
|
-
const installed = registrations === null ? {} : parseConfig(registrations,
|
|
36180
|
-
const commands =
|
|
37063
|
+
const registrations = witness(join29(home, ".claude/plugins/installed_plugins.json"));
|
|
37064
|
+
const installed = registrations === null ? {} : parseConfig(registrations, join29(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
37065
|
+
const commands = canonical9(join29(home, ".claude/commands"));
|
|
36181
37066
|
safe5(commands);
|
|
36182
|
-
if (
|
|
37067
|
+
if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync8(commands).length)
|
|
36183
37068
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
36184
37069
|
for (const [id, enabled] of Object.entries(config2.enabledPlugins ?? {})) {
|
|
36185
37070
|
if (enabled === false)
|
|
@@ -36199,37 +37084,37 @@ function resolveAgentDiscovery(options) {
|
|
|
36199
37084
|
unresolved("unsupported plugin enablement");
|
|
36200
37085
|
const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
|
|
36201
37086
|
const market = config2.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
36202
|
-
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !
|
|
37087
|
+
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute19(market.source))
|
|
36203
37088
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
36204
|
-
const catalogText = witness(
|
|
37089
|
+
const catalogText = witness(join29(market.source, ".agents/plugins/marketplace.json"));
|
|
36205
37090
|
if (catalogText === null)
|
|
36206
37091
|
unresolved("marketplace catalog missing");
|
|
36207
|
-
const catalog = parseConfig(catalogText,
|
|
37092
|
+
const catalog = parseConfig(catalogText, join29(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
36208
37093
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
36209
37094
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
36210
|
-
plugin(
|
|
37095
|
+
plugin(resolve20(market.source, matches[0].source.path));
|
|
36211
37096
|
}
|
|
36212
37097
|
} else if (agent === "opencode") {
|
|
36213
|
-
if (witness(
|
|
37098
|
+
if (witness(join29(home, ".config/opencode/opencode.jsonc")) !== null || witness(join29(home, ".config/opencode/config.json")) !== null)
|
|
36214
37099
|
unresolved("additional config layers require review");
|
|
36215
37100
|
if ((config2.plugin?.length ?? 0) > 0 || config2.skills?.urls?.length)
|
|
36216
37101
|
unresolved("external plugin or remote skill source");
|
|
36217
37102
|
for (const path of config2.skills?.paths ?? []) {
|
|
36218
|
-
if (typeof path !== "string" || !
|
|
37103
|
+
if (typeof path !== "string" || !isAbsolute19(path))
|
|
36219
37104
|
unresolved("relative or malformed added skill root");
|
|
36220
37105
|
roots.add(path);
|
|
36221
37106
|
}
|
|
36222
|
-
const localPlugins =
|
|
37107
|
+
const localPlugins = join29(home, ".config/opencode/plugins");
|
|
36223
37108
|
safe5(localPlugins);
|
|
36224
|
-
if (
|
|
37109
|
+
if (lstatSync21(localPlugins, { throwIfNoEntry: false }) && readdirSync8(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
36225
37110
|
unresolved("additional local plugin hooks require review");
|
|
36226
37111
|
} else if (agent === "sumi") {
|
|
36227
|
-
if ((config2.plugin?.length ?? 0) > 0 || (config2.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !==
|
|
37112
|
+
if ((config2.plugin?.length ?? 0) > 0 || (config2.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical9(configPath) && source.path !== canonical9(join29(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
|
|
36228
37113
|
unresolved("external or foreign local plugins require review");
|
|
36229
37114
|
} else if (agent === "hermes") {
|
|
36230
|
-
for (const directory of [
|
|
37115
|
+
for (const directory of [join29(home, ".hermes/plugins"), join29(home, ".hermes/hermes-agent")]) {
|
|
36231
37116
|
safe5(directory);
|
|
36232
|
-
if (
|
|
37117
|
+
if (lstatSync21(directory, { throwIfNoEntry: false }))
|
|
36233
37118
|
unresolved("installed runtime/plugin discovery requires reviewed source bindings");
|
|
36234
37119
|
}
|
|
36235
37120
|
if (Bun.which("hermes") || Object.keys(config2.plugins ?? {}).length)
|
|
@@ -36237,32 +37122,32 @@ function resolveAgentDiscovery(options) {
|
|
|
36237
37122
|
} else if (agent === "gemini") {
|
|
36238
37123
|
if (Object.keys(config2.extensions ?? {}).length || config2.skills?.paths?.length)
|
|
36239
37124
|
unresolved("custom extension or skill paths");
|
|
36240
|
-
const directory =
|
|
37125
|
+
const directory = join29(home, ".gemini/extensions");
|
|
36241
37126
|
safe5(directory);
|
|
36242
|
-
if (
|
|
36243
|
-
for (const name of
|
|
36244
|
-
const root =
|
|
37127
|
+
if (lstatSync21(directory, { throwIfNoEntry: false }))
|
|
37128
|
+
for (const name of readdirSync8(directory)) {
|
|
37129
|
+
const root = join29(directory, name);
|
|
36245
37130
|
safe5(root);
|
|
36246
|
-
if (!
|
|
37131
|
+
if (!lstatSync21(root)?.isDirectory())
|
|
36247
37132
|
continue;
|
|
36248
|
-
const raw = witness(
|
|
37133
|
+
const raw = witness(join29(root, "gemini-extension.json"));
|
|
36249
37134
|
if (raw === null)
|
|
36250
37135
|
unresolved("extension registration missing");
|
|
36251
|
-
const manifest = parseConfig(raw,
|
|
36252
|
-
if (manifest.hooks !== undefined || witness(
|
|
37136
|
+
const manifest = parseConfig(raw, join29(root, "gemini-extension.json"));
|
|
37137
|
+
if (manifest.hooks !== undefined || witness(join29(root, "hooks/hooks.json")) !== null || witness(join29(root, ".gemini-extension-install.json")) !== null)
|
|
36253
37138
|
unresolved("extension hooks or linked registration require review");
|
|
36254
|
-
roots.add(
|
|
37139
|
+
roots.add(join29(root, "skills"));
|
|
36255
37140
|
}
|
|
36256
37141
|
}
|
|
36257
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([
|
|
37142
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join29(home, ".hermes/plugins"), join29(home, ".hermes/hermes-agent")].map((path) => canonical9(path))) } : {} };
|
|
36258
37143
|
}
|
|
36259
37144
|
var DISCOVERY_RUNTIME_COMMANDS, realpathOrNull = (path) => {
|
|
36260
37145
|
try {
|
|
36261
|
-
return
|
|
37146
|
+
return realpathSync8(path);
|
|
36262
37147
|
} catch {
|
|
36263
37148
|
return null;
|
|
36264
37149
|
}
|
|
36265
|
-
},
|
|
37150
|
+
}, digest2 = (text2) => createHash21("sha256").update(text2).digest("hex");
|
|
36266
37151
|
var init_agent_discovery = __esm(() => {
|
|
36267
37152
|
init_sumi_settings_witness();
|
|
36268
37153
|
init_agent_policy_limits();
|
|
@@ -36283,16 +37168,17 @@ var init_agent_discovery = __esm(() => {
|
|
|
36283
37168
|
init_native_discovery_roots();
|
|
36284
37169
|
init_codex_native_compatibility();
|
|
36285
37170
|
init_retained_review_artifacts();
|
|
37171
|
+
init_codex_semantic_cache_witness();
|
|
36286
37172
|
init_agent_discovery_directories();
|
|
36287
37173
|
DISCOVERY_RUNTIME_COMMANDS = Object.freeze({ gemini: "gemini" });
|
|
36288
37174
|
});
|
|
36289
37175
|
|
|
36290
37176
|
// src/lib/authoring-path.ts
|
|
36291
|
-
import { lstatSync as
|
|
37177
|
+
import { lstatSync as lstatSync22, realpathSync as realpathSync9 } from "fs";
|
|
36292
37178
|
import { homedir as homedir3 } from "os";
|
|
36293
|
-
import { basename as basename10, dirname as
|
|
37179
|
+
import { basename as basename10, dirname as dirname20, join as join30, resolve as resolve21, sep as sep10 } from "path";
|
|
36294
37180
|
function pathParts(path) {
|
|
36295
|
-
return
|
|
37181
|
+
return resolve21(path).split(sep10).filter(Boolean);
|
|
36296
37182
|
}
|
|
36297
37183
|
function nativeRootMatch(path) {
|
|
36298
37184
|
const parts = pathParts(path);
|
|
@@ -36306,24 +37192,24 @@ function nativeRootMatch(path) {
|
|
|
36306
37192
|
return;
|
|
36307
37193
|
}
|
|
36308
37194
|
function physicalPath(path) {
|
|
36309
|
-
let cursor =
|
|
37195
|
+
let cursor = resolve21(path);
|
|
36310
37196
|
const suffix = [];
|
|
36311
|
-
while (!
|
|
37197
|
+
while (!lstatSync22(cursor, { throwIfNoEntry: false })) {
|
|
36312
37198
|
suffix.unshift(basename10(cursor));
|
|
36313
|
-
const parent =
|
|
37199
|
+
const parent = dirname20(cursor);
|
|
36314
37200
|
if (parent === cursor)
|
|
36315
|
-
return
|
|
37201
|
+
return resolve21(path);
|
|
36316
37202
|
cursor = parent;
|
|
36317
37203
|
}
|
|
36318
|
-
return
|
|
37204
|
+
return resolve21(realpathSync9(cursor), ...suffix);
|
|
36319
37205
|
}
|
|
36320
37206
|
function contains(root, path) {
|
|
36321
|
-
return path === root || path.startsWith(root +
|
|
37207
|
+
return path === root || path.startsWith(root + sep10);
|
|
36322
37208
|
}
|
|
36323
37209
|
function configuredRoots(home, explicitHome) {
|
|
36324
|
-
const homeData =
|
|
37210
|
+
const homeData = join30(home, ".hasna", "skills");
|
|
36325
37211
|
const homePolicy = readManagedSkillPolicySnapshot(homeData);
|
|
36326
|
-
const configuredPolicy = !explicitHome &&
|
|
37212
|
+
const configuredPolicy = !explicitHome && resolve21(getDataDirReadOnly()) !== resolve21(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
|
|
36327
37213
|
const policy = homePolicy ?? configuredPolicy;
|
|
36328
37214
|
const bridge = policy?.value.bridge;
|
|
36329
37215
|
if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
|
|
@@ -36352,8 +37238,8 @@ function configuredRoots(home, explicitHome) {
|
|
|
36352
37238
|
admitBinding(binding);
|
|
36353
37239
|
continue;
|
|
36354
37240
|
}
|
|
36355
|
-
const nativeHome =
|
|
36356
|
-
if (!
|
|
37241
|
+
const nativeHome = join30(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
|
|
37242
|
+
if (!lstatSync22(nativeHome, { throwIfNoEntry: false }))
|
|
36357
37243
|
continue;
|
|
36358
37244
|
roots.push(...resolveAgentDiscovery({ home, agent, canonical: physicalPath }).roots);
|
|
36359
37245
|
}
|
|
@@ -36362,22 +37248,22 @@ function configuredRoots(home, explicitHome) {
|
|
|
36362
37248
|
function assertPortableAuthoringPath(path, options = {}) {
|
|
36363
37249
|
if (typeof path !== "string" || !path || path.includes("\x00"))
|
|
36364
37250
|
throw new Error("Invalid portable authoring path");
|
|
36365
|
-
const lexical =
|
|
37251
|
+
const lexical = resolve21(path);
|
|
36366
37252
|
const physical = physicalPath(lexical);
|
|
36367
37253
|
const candidates = [lexical, physical];
|
|
36368
37254
|
let native = candidates.map(nativeRootMatch).find(Boolean);
|
|
36369
|
-
const home =
|
|
36370
|
-
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) =>
|
|
36371
|
-
defaults.push(
|
|
37255
|
+
const home = resolve21(options.homeDir ?? homedir3());
|
|
37256
|
+
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join30(home, ...parts));
|
|
37257
|
+
defaults.push(join30(home, ".codex/plugins/cache"), join30(home, ".claude/plugins/cache"), join30(home, ".claude/plugins/synced"), join30(home, ".gemini/extensions"));
|
|
36372
37258
|
for (const root of defaults) {
|
|
36373
|
-
if (candidates.some((candidate) => contains(
|
|
37259
|
+
if (candidates.some((candidate) => contains(resolve21(root), candidate) || contains(physicalPath(root), candidate)))
|
|
36374
37260
|
native ??= root;
|
|
36375
37261
|
}
|
|
36376
37262
|
if (native) {
|
|
36377
37263
|
throw new Error(`Refusing portable Skills authoring below native discovery root '${native}': ${lexical}. Set HASNA_SKILLS_DIR to a private authoring corpus outside native roots.`);
|
|
36378
37264
|
}
|
|
36379
37265
|
for (const root of configuredRoots(home, options.homeDir !== undefined)) {
|
|
36380
|
-
if (candidates.some((candidate) => contains(
|
|
37266
|
+
if (candidates.some((candidate) => contains(resolve21(root), candidate) || contains(physicalPath(root), candidate))) {
|
|
36381
37267
|
throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
|
|
36382
37268
|
}
|
|
36383
37269
|
}
|
|
@@ -36397,29 +37283,29 @@ import {
|
|
|
36397
37283
|
existsSync as existsSync12,
|
|
36398
37284
|
mkdirSync as mkdirSync8,
|
|
36399
37285
|
mkdtempSync,
|
|
36400
|
-
readdirSync as
|
|
36401
|
-
realpathSync as
|
|
37286
|
+
readdirSync as readdirSync9,
|
|
37287
|
+
realpathSync as realpathSync10,
|
|
36402
37288
|
renameSync as renameSync3,
|
|
36403
37289
|
rmSync as rmSync3,
|
|
36404
37290
|
statSync as statSync8,
|
|
36405
37291
|
writeFileSync as writeFileSync6
|
|
36406
37292
|
} from "fs";
|
|
36407
|
-
import { basename as basename11, dirname as
|
|
37293
|
+
import { basename as basename11, dirname as dirname21, isAbsolute as isAbsolute20, join as join31, normalize as normalize4, relative as relative8, resolve as resolve22, sep as sep11 } from "path";
|
|
36408
37294
|
function getPortableSkillsRoot(options = {}) {
|
|
36409
37295
|
return resolvePortableSkillsRoot(options, true);
|
|
36410
37296
|
}
|
|
36411
37297
|
function resolvePortableSkillsRoot(options, migrate) {
|
|
36412
37298
|
if (options.rootDir)
|
|
36413
37299
|
return options.rootDir;
|
|
36414
|
-
const appDir = options.homeDir ?
|
|
36415
|
-
const cache =
|
|
37300
|
+
const appDir = options.homeDir ? join31(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
|
|
37301
|
+
const cache = join31(appDir, SKILLS_CACHE_DIRNAME);
|
|
36416
37302
|
if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
|
|
36417
37303
|
return cache;
|
|
36418
|
-
const installed =
|
|
37304
|
+
const installed = join31(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
36419
37305
|
return installed;
|
|
36420
37306
|
}
|
|
36421
37307
|
function getPortableSkillPath(name, options = {}) {
|
|
36422
|
-
return
|
|
37308
|
+
return join31(getPortableSkillsRoot(options), normalizePortableSkillName(name));
|
|
36423
37309
|
}
|
|
36424
37310
|
function findPortableSkill(name, options = {}) {
|
|
36425
37311
|
let normalized;
|
|
@@ -36442,10 +37328,10 @@ function listPortableSkills(options = {}) {
|
|
|
36442
37328
|
if (!safeIsDirectory(root))
|
|
36443
37329
|
return [];
|
|
36444
37330
|
const skills = [];
|
|
36445
|
-
for (const entry of
|
|
37331
|
+
for (const entry of readdirSync9(root).sort()) {
|
|
36446
37332
|
if (entry.startsWith("."))
|
|
36447
37333
|
continue;
|
|
36448
|
-
const path =
|
|
37334
|
+
const path = join31(root, entry);
|
|
36449
37335
|
if (!safeIsDirectory(path))
|
|
36450
37336
|
continue;
|
|
36451
37337
|
try {
|
|
@@ -36478,9 +37364,9 @@ function isOfficialSkillName(name) {
|
|
|
36478
37364
|
function scaffoldPortableSkill(name, options = {}) {
|
|
36479
37365
|
const skillName = normalizeNewPortableSkillName(name);
|
|
36480
37366
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
36481
|
-
assertPortableAuthoringPath(
|
|
37367
|
+
assertPortableAuthoringPath(join31(readOnlyRoot, skillName), options);
|
|
36482
37368
|
const root = getPortableSkillsRoot(options);
|
|
36483
|
-
const skillPath =
|
|
37369
|
+
const skillPath = join31(root, skillName);
|
|
36484
37370
|
assertPortableAuthoringPath(skillPath, options);
|
|
36485
37371
|
if (existsSync12(skillPath)) {
|
|
36486
37372
|
if (!options.overwrite)
|
|
@@ -36499,20 +37385,20 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
36499
37385
|
return { name: skillName, path: skillPath, manifest, created: true };
|
|
36500
37386
|
}
|
|
36501
37387
|
function physicalImportPath(path) {
|
|
36502
|
-
let ancestor =
|
|
37388
|
+
let ancestor = resolve22(path);
|
|
36503
37389
|
const suffix = [];
|
|
36504
37390
|
while (!existsSync12(ancestor)) {
|
|
36505
37391
|
suffix.unshift(basename11(ancestor));
|
|
36506
|
-
ancestor =
|
|
37392
|
+
ancestor = dirname21(ancestor);
|
|
36507
37393
|
}
|
|
36508
|
-
return
|
|
37394
|
+
return join31(realpathSync10(ancestor), ...suffix);
|
|
36509
37395
|
}
|
|
36510
37396
|
function assertDisjointImportPaths(source, destination) {
|
|
36511
|
-
const sourcePath =
|
|
37397
|
+
const sourcePath = realpathSync10(source);
|
|
36512
37398
|
const destinationPath = physicalImportPath(destination);
|
|
36513
37399
|
const contains2 = (parent, child2) => {
|
|
36514
|
-
const path =
|
|
36515
|
-
return path === "" || !
|
|
37400
|
+
const path = relative8(parent, child2);
|
|
37401
|
+
return path === "" || !isAbsolute20(path) && path !== ".." && !path.startsWith(`..${sep11}`);
|
|
36516
37402
|
};
|
|
36517
37403
|
if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
|
|
36518
37404
|
throw new Error("Source and destination skill directories must not overlap");
|
|
@@ -36531,11 +37417,11 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
36531
37417
|
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename11(absoluteSource)}')` : ""} matches a bundled official skill.`;
|
|
36532
37418
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
36533
37419
|
}
|
|
36534
|
-
const readOnlyDestination =
|
|
37420
|
+
const readOnlyDestination = join31(resolvePortableSkillsRoot(options, false), skillName);
|
|
36535
37421
|
assertPortableAuthoringPath(readOnlyDestination, options);
|
|
36536
37422
|
assertDisjointImportPaths(absoluteSource, readOnlyDestination);
|
|
36537
37423
|
const root = getPortableSkillsRoot(options);
|
|
36538
|
-
const destination =
|
|
37424
|
+
const destination = join31(root, skillName);
|
|
36539
37425
|
assertPortableAuthoringPath(destination, options);
|
|
36540
37426
|
assertDisjointImportPaths(absoluteSource, destination);
|
|
36541
37427
|
if (existsSync12(destination)) {
|
|
@@ -36543,7 +37429,7 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
36543
37429
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
36544
37430
|
rmSync3(destination, { recursive: true, force: true });
|
|
36545
37431
|
}
|
|
36546
|
-
mkdirSync8(
|
|
37432
|
+
mkdirSync8(dirname21(destination), { recursive: true });
|
|
36547
37433
|
copySkillDirectory(absoluteSource, destination);
|
|
36548
37434
|
const base = {
|
|
36549
37435
|
...inferred,
|
|
@@ -36567,8 +37453,8 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
36567
37453
|
const warnings = [...base.warnings];
|
|
36568
37454
|
let manifest;
|
|
36569
37455
|
if (existsSync12(skillPath)) {
|
|
36570
|
-
const skillJsonPath =
|
|
36571
|
-
const skillMdPath =
|
|
37456
|
+
const skillJsonPath = join31(skillPath, "skill.json");
|
|
37457
|
+
const skillMdPath = join31(skillPath, "SKILL.md");
|
|
36572
37458
|
if (!existsSync12(skillJsonPath) && !existsSync12(skillMdPath)) {
|
|
36573
37459
|
add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
|
|
36574
37460
|
}
|
|
@@ -36588,7 +37474,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
36588
37474
|
add3(issues, "portable.version_missing", "Portable manifest missing version");
|
|
36589
37475
|
}
|
|
36590
37476
|
const contractIssues = validatePortableManifestContract(manifest, {
|
|
36591
|
-
strict: existsSync12(
|
|
37477
|
+
strict: existsSync12(join31(skillPath, "skill.json")),
|
|
36592
37478
|
skillPath
|
|
36593
37479
|
});
|
|
36594
37480
|
for (const issue2 of contractIssues)
|
|
@@ -36628,7 +37514,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
36628
37514
|
add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
|
|
36629
37515
|
continue;
|
|
36630
37516
|
}
|
|
36631
|
-
const entryPath =
|
|
37517
|
+
const entryPath = join31(skillPath, command.entry);
|
|
36632
37518
|
if (!existsSync12(entryPath))
|
|
36633
37519
|
add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
|
|
36634
37520
|
else if (statSync8(entryPath).isDirectory())
|
|
@@ -36639,7 +37525,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
36639
37525
|
} catch (error61) {
|
|
36640
37526
|
add3(issues, "portable.manifest_invalid", error61.message);
|
|
36641
37527
|
}
|
|
36642
|
-
if (manifest?.kind !== "instruction" && !existsSync12(
|
|
37528
|
+
if (manifest?.kind !== "instruction" && !existsSync12(join31(skillPath, "AGENTS.md"))) {
|
|
36643
37529
|
add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
|
|
36644
37530
|
}
|
|
36645
37531
|
}
|
|
@@ -36677,7 +37563,7 @@ function safeIsDirectory(path) {
|
|
|
36677
37563
|
}
|
|
36678
37564
|
}
|
|
36679
37565
|
function isSafeRelativePath2(value) {
|
|
36680
|
-
if (!value.trim() ||
|
|
37566
|
+
if (!value.trim() || isAbsolute20(value))
|
|
36681
37567
|
return false;
|
|
36682
37568
|
const normalized = normalize4(value).replace(/\\/g, "/");
|
|
36683
37569
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
@@ -36866,8 +37752,8 @@ var init_search = __esm(() => {
|
|
|
36866
37752
|
});
|
|
36867
37753
|
|
|
36868
37754
|
// src/lib/registry.ts
|
|
36869
|
-
import { existsSync as existsSync13, readFileSync as readFileSync17, readdirSync as
|
|
36870
|
-
import { join as
|
|
37755
|
+
import { existsSync as existsSync13, readFileSync as readFileSync17, readdirSync as readdirSync10 } from "fs";
|
|
37756
|
+
import { join as join32 } from "path";
|
|
36871
37757
|
function parseSkillMdFrontmatter(content) {
|
|
36872
37758
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
36873
37759
|
if (!match)
|
|
@@ -36904,11 +37790,11 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
36904
37790
|
return [];
|
|
36905
37791
|
const result = [];
|
|
36906
37792
|
try {
|
|
36907
|
-
const entries =
|
|
37793
|
+
const entries = readdirSync10(dir, { withFileTypes: true });
|
|
36908
37794
|
for (const entry of entries) {
|
|
36909
37795
|
if (!entry.isDirectory())
|
|
36910
37796
|
continue;
|
|
36911
|
-
const skillMdPath =
|
|
37797
|
+
const skillMdPath = join32(dir, entry.name, "SKILL.md");
|
|
36912
37798
|
if (!existsSync13(skillMdPath))
|
|
36913
37799
|
continue;
|
|
36914
37800
|
let content;
|
|
@@ -36928,7 +37814,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
36928
37814
|
category: fm.category || "Development Tools",
|
|
36929
37815
|
tags: fm.tags || [],
|
|
36930
37816
|
...fm.kind ? { kind: fm.kind } : {},
|
|
36931
|
-
...isHostedMetadataSkillDir(
|
|
37817
|
+
...isHostedMetadataSkillDir(join32(dir, entry.name)) ? { serverOwned: true } : {},
|
|
36932
37818
|
source
|
|
36933
37819
|
});
|
|
36934
37820
|
}
|
|
@@ -36940,12 +37826,12 @@ function findExtensionSkillPath(name) {
|
|
|
36940
37826
|
if (!config2.extensionsDir || !existsSync13(config2.extensionsDir))
|
|
36941
37827
|
return null;
|
|
36942
37828
|
try {
|
|
36943
|
-
const entries =
|
|
37829
|
+
const entries = readdirSync10(config2.extensionsDir, { withFileTypes: true });
|
|
36944
37830
|
for (const entry of entries) {
|
|
36945
37831
|
if (!entry.isDirectory())
|
|
36946
37832
|
continue;
|
|
36947
|
-
const skillDir =
|
|
36948
|
-
const skillMdPath =
|
|
37833
|
+
const skillDir = join32(config2.extensionsDir, entry.name);
|
|
37834
|
+
const skillMdPath = join32(skillDir, "SKILL.md");
|
|
36949
37835
|
if (!existsSync13(skillMdPath))
|
|
36950
37836
|
continue;
|
|
36951
37837
|
let content;
|
|
@@ -37362,7 +38248,7 @@ var init_v4 = __esm(() => {
|
|
|
37362
38248
|
});
|
|
37363
38249
|
|
|
37364
38250
|
// src/lib/canonical-json.ts
|
|
37365
|
-
import { createHash as
|
|
38251
|
+
import { createHash as createHash22 } from "crypto";
|
|
37366
38252
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
37367
38253
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
37368
38254
|
}
|
|
@@ -37406,7 +38292,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
37406
38292
|
return visit3(value, 0);
|
|
37407
38293
|
}
|
|
37408
38294
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
37409
|
-
return
|
|
38295
|
+
return createHash22("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
37410
38296
|
}
|
|
37411
38297
|
var init_canonical_json = () => {};
|
|
37412
38298
|
|
|
@@ -37516,14 +38402,14 @@ function requireResult(value) {
|
|
|
37516
38402
|
if (!value)
|
|
37517
38403
|
throw new RemoteRecurringReadError;
|
|
37518
38404
|
}
|
|
37519
|
-
function termsIntegrity(terms,
|
|
37520
|
-
requireResult(terms.organizationId ===
|
|
37521
|
-
requireResult(canonicalJsonSha256(terms) ===
|
|
38405
|
+
function termsIntegrity(terms, hash4, identity4, own2) {
|
|
38406
|
+
requireResult(terms.organizationId === identity4.organization.id && (!own2 || terms.approvedByUserId === identity4.user.id && terms.approvedMembershipId === identity4.user.membershipId));
|
|
38407
|
+
requireResult(canonicalJsonSha256(terms) === hash4);
|
|
37522
38408
|
requireResult(canonicalJsonSha256({ skill: terms.skill, input: terms.input, args: terms.args, runtime: terms.runtime }) === terms.payloadSha256);
|
|
37523
38409
|
requireResult(Date.parse(terms.expiresAt) > Date.parse(terms.startsAt));
|
|
37524
38410
|
}
|
|
37525
|
-
function previewIntegrity(value,
|
|
37526
|
-
termsIntegrity(value.terms, value.termsSha256,
|
|
38411
|
+
function previewIntegrity(value, identity4) {
|
|
38412
|
+
termsIntegrity(value.terms, value.termsSha256, identity4, true);
|
|
37527
38413
|
requireResult(Date.parse(value.approvalDeadline) > Date.parse(value.quote.quotedAt));
|
|
37528
38414
|
const anchor2 = Date.parse(value.terms.startsAt), end = Date.parse(value.terms.expiresAt), cadence = value.terms.everyMinutes * 60000;
|
|
37529
38415
|
requireResult(value.firstDueInstants.every((at, i) => Date.parse(at) === anchor2 + i * cadence && Date.parse(at) < end));
|
|
@@ -37531,7 +38417,7 @@ function previewIntegrity(value, identity3) {
|
|
|
37531
38417
|
const day = Date.parse(value.firstPeriod.startsAt);
|
|
37532
38418
|
requireResult(day % 86400000 === 0 && day <= anchor2 && anchor2 < Date.parse(value.firstPeriod.endsAt) && Date.parse(value.firstPeriod.endsAt) === day + 86400000);
|
|
37533
38419
|
}
|
|
37534
|
-
function parseRecurringResult(action, value, input2,
|
|
38420
|
+
function parseRecurringResult(action, value, input2, identity4) {
|
|
37535
38421
|
const wrappers = { preview: 1, draft: 1, activate: 2, list: 3, get: 1, occurrences: 0, revoke: 0 };
|
|
37536
38422
|
canonicalJsonAtDepth(value, 64 * 1024 * 1024, 64 + wrappers[action]);
|
|
37537
38423
|
const result = results[action].parse(value);
|
|
@@ -37540,7 +38426,7 @@ function parseRecurringResult(action, value, input2, identity3) {
|
|
|
37540
38426
|
const row = input2;
|
|
37541
38427
|
if (action === "preview" || action === "draft") {
|
|
37542
38428
|
const p = result;
|
|
37543
|
-
previewIntegrity(p,
|
|
38429
|
+
previewIntegrity(p, identity4);
|
|
37544
38430
|
if (action === "draft")
|
|
37545
38431
|
requireResult(p.draftId === row.draftId);
|
|
37546
38432
|
else {
|
|
@@ -37559,7 +38445,7 @@ function parseRecurringResult(action, value, input2, identity3) {
|
|
|
37559
38445
|
}
|
|
37560
38446
|
const views = action === "list" ? result.items : action === "get" ? [result] : action === "activate" ? [result.consent] : [];
|
|
37561
38447
|
for (const view of views) {
|
|
37562
|
-
termsIntegrity(view.terms, view.termsSha256,
|
|
38448
|
+
termsIntegrity(view.terms, view.termsSha256, identity4, action === "activate");
|
|
37563
38449
|
requireResult(view.state === "revoked" === (view.revokedAt !== null));
|
|
37564
38450
|
if (action === "get")
|
|
37565
38451
|
requireResult(view.consentId === row.consentId);
|
|
@@ -37585,7 +38471,7 @@ function parseRecurringResult(action, value, input2, identity3) {
|
|
|
37585
38471
|
}
|
|
37586
38472
|
return result;
|
|
37587
38473
|
}
|
|
37588
|
-
var RecurringInputError, RemoteRecurringUnavailableError, RemoteRecurringReadError, RemoteRecurringUnconfirmedError, failures, RemoteRecurringError, uuid3, instant2, integer2,
|
|
38474
|
+
var RecurringInputError, RemoteRecurringUnavailableError, RemoteRecurringReadError, RemoteRecurringUnconfirmedError, failures, RemoteRecurringError, uuid3, instant2, integer2, digest3, list, inputs, capabilityNames, budget2, period, preview, consent, refusal2, occurrence, revocation, page = (item) => exports_external.object({ contractVersion: exports_external.literal(1), items: exports_external.array(item).max(100), nextCursor: exports_external.string().min(1).max(2048).nullable() }), results;
|
|
37589
38475
|
var init_remote_recurring = __esm(() => {
|
|
37590
38476
|
init_v4();
|
|
37591
38477
|
init_canonical_json();
|
|
@@ -37651,7 +38537,7 @@ var init_remote_recurring = __esm(() => {
|
|
|
37651
38537
|
uuid3 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
37652
38538
|
instant2 = exports_external.string().max(32).datetime();
|
|
37653
38539
|
integer2 = exports_external.number().int().min(0).max(MAX_RECURRING_CREDITS);
|
|
37654
|
-
|
|
38540
|
+
digest3 = exports_external.string().regex(/^[a-f0-9]{64}$/);
|
|
37655
38541
|
list = exports_external.object({ limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() }).strict();
|
|
37656
38542
|
inputs = {
|
|
37657
38543
|
preview: recurringRequestSchema,
|
|
@@ -37677,7 +38563,7 @@ var init_remote_recurring = __esm(() => {
|
|
|
37677
38563
|
contractVersion: exports_external.literal(1),
|
|
37678
38564
|
draftId: uuid3,
|
|
37679
38565
|
terms: recurringTermsSchema,
|
|
37680
|
-
termsSha256:
|
|
38566
|
+
termsSha256: digest3,
|
|
37681
38567
|
quote: exports_external.object({ costCredits: integer2, quotedAt: instant2, admissionReprices: exports_external.literal(true) }),
|
|
37682
38568
|
approvalDeadline: instant2,
|
|
37683
38569
|
firstDueInstants: exports_external.array(instant2).min(1).max(5),
|
|
@@ -37689,7 +38575,7 @@ var init_remote_recurring = __esm(() => {
|
|
|
37689
38575
|
consentId: uuid3,
|
|
37690
38576
|
scheduleId: uuid3,
|
|
37691
38577
|
terms: recurringTermsSchema,
|
|
37692
|
-
termsSha256:
|
|
38578
|
+
termsSha256: digest3,
|
|
37693
38579
|
state: exports_external.enum(["active", "revoked", "expired"]),
|
|
37694
38580
|
activatedAt: instant2,
|
|
37695
38581
|
revokedAt: instant2.nullable(),
|
|
@@ -37943,10 +38829,10 @@ function sessionToken(value) {
|
|
|
37943
38829
|
return value;
|
|
37944
38830
|
}
|
|
37945
38831
|
function parseWorkspaceSession(value, expected) {
|
|
37946
|
-
const
|
|
37947
|
-
if (
|
|
38832
|
+
const identity4 = parseWorkspaceIdentity(value, expected.userId);
|
|
38833
|
+
if (identity4.user.membershipId !== expected.membershipId)
|
|
37948
38834
|
throw new WorkspaceIdentityMismatchError;
|
|
37949
|
-
return { token: sessionToken(value.token), ...
|
|
38835
|
+
return { token: sessionToken(value.token), ...identity4 };
|
|
37950
38836
|
}
|
|
37951
38837
|
function parseWorkspaceLogin(value, expectedUserId) {
|
|
37952
38838
|
const user = record5(value) && value.user;
|
|
@@ -38157,13 +39043,13 @@ var init_remote_workspace_leave = __esm(() => {
|
|
|
38157
39043
|
|
|
38158
39044
|
// src/lib/auth-store.ts
|
|
38159
39045
|
import { chmodSync, existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync18, renameSync as renameSync4, statSync as statSync9, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
38160
|
-
import { basename as basename12, dirname as
|
|
39046
|
+
import { basename as basename12, dirname as dirname22, join as join33 } from "path";
|
|
38161
39047
|
function getAuthFilePath(env = process.env) {
|
|
38162
39048
|
return skillsCredentialFilePath(env);
|
|
38163
39049
|
}
|
|
38164
39050
|
function getIdentityFilePath(env = process.env) {
|
|
38165
39051
|
const file2 = skillsCredentialFilePath(env);
|
|
38166
|
-
return
|
|
39052
|
+
return join33(dirname22(file2), basename12(file2).replace(/^credentials/, "identity") + ".json");
|
|
38167
39053
|
}
|
|
38168
39054
|
function getApiUrl(action, env = process.env, options = {}) {
|
|
38169
39055
|
return requireSkillsApiOrigin(action, env, options);
|
|
@@ -38391,10 +39277,10 @@ async function readQuoteUnavailableCode(response) {
|
|
|
38391
39277
|
let timer;
|
|
38392
39278
|
let deadlineExceeded = false;
|
|
38393
39279
|
const expired = Symbol("quote body deadline");
|
|
38394
|
-
const deadline = new Promise((
|
|
39280
|
+
const deadline = new Promise((resolve23) => {
|
|
38395
39281
|
timer = setTimeout(() => {
|
|
38396
39282
|
deadlineExceeded = true;
|
|
38397
|
-
|
|
39283
|
+
resolve23(expired);
|
|
38398
39284
|
reader.cancel().catch(() => {});
|
|
38399
39285
|
}, 1500);
|
|
38400
39286
|
});
|
|
@@ -38697,16 +39583,16 @@ class RemoteSkillsClient {
|
|
|
38697
39583
|
async listAccountWorkspaces(expectedUserId) {
|
|
38698
39584
|
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
38699
39585
|
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
38700
|
-
let
|
|
39586
|
+
let identity4;
|
|
38701
39587
|
if (expected !== undefined) {
|
|
38702
39588
|
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
38703
39589
|
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
38704
39590
|
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces", "INTERACTIVE_SESSION_REQUIRED");
|
|
38705
|
-
|
|
39591
|
+
identity4 = parseWorkspaceIdentity(value, expected);
|
|
38706
39592
|
}
|
|
38707
39593
|
const result = parseAccountWorkspaces(await connection.requestWorkspaceSelection("/api/v1/account/workspaces"));
|
|
38708
39594
|
const current = result.workspaces.find((workspace) => workspace.current);
|
|
38709
|
-
if (
|
|
39595
|
+
if (identity4 && (current.membershipId !== identity4.user.membershipId || current.organization.id !== identity4.organization.id))
|
|
38710
39596
|
throw new WorkspaceIdentityMismatchError;
|
|
38711
39597
|
return result;
|
|
38712
39598
|
}
|
|
@@ -38724,10 +39610,10 @@ class RemoteSkillsClient {
|
|
|
38724
39610
|
const verified = await new RemoteSkillsClient(selected.token, connection.apiUrl).requestWorkspaceSelection("/api/auth/whoami");
|
|
38725
39611
|
if (!verified || typeof verified !== "object" || verified.authMethod !== "jwt")
|
|
38726
39612
|
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
38727
|
-
const
|
|
38728
|
-
if (
|
|
39613
|
+
const identity4 = parseWorkspaceIdentity(verified, target.userId);
|
|
39614
|
+
if (identity4.user.membershipId !== target.membershipId || identity4.organization.id !== selected.organization.id)
|
|
38729
39615
|
throw new WorkspaceIdentityMismatchError;
|
|
38730
|
-
return { token: selected.token, ...
|
|
39616
|
+
return { token: selected.token, ...identity4 };
|
|
38731
39617
|
}
|
|
38732
39618
|
async requestWorkspaceSelection(path, options) {
|
|
38733
39619
|
let response;
|
|
@@ -38811,8 +39697,8 @@ class RemoteSkillsClient {
|
|
|
38811
39697
|
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
38812
39698
|
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
38813
39699
|
throw new RemoteWorkspaceLeaveError("INTERACTIVE_SESSION_REQUIRED");
|
|
38814
|
-
const
|
|
38815
|
-
if (
|
|
39700
|
+
const identity4 = parseWorkspaceIdentity(value, captured.context.userId);
|
|
39701
|
+
if (identity4.user.membershipId !== captured.context.membershipId)
|
|
38816
39702
|
throw new WorkspaceIdentityMismatchError;
|
|
38817
39703
|
let response, body;
|
|
38818
39704
|
try {
|
|
@@ -38827,7 +39713,7 @@ class RemoteSkillsClient {
|
|
|
38827
39713
|
throw new RemoteWorkspaceLeaveError(code);
|
|
38828
39714
|
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
38829
39715
|
}
|
|
38830
|
-
return parseWorkspaceLeaveResult(body, captured.context.membershipId,
|
|
39716
|
+
return parseWorkspaceLeaveResult(body, captured.context.membershipId, identity4.organization.id);
|
|
38831
39717
|
}
|
|
38832
39718
|
listWorkspaceInvitations(context, options = {}) {
|
|
38833
39719
|
return this.requestWorkspaceInvitation(context, "list", options);
|
|
@@ -38853,8 +39739,8 @@ class RemoteSkillsClient {
|
|
|
38853
39739
|
const identityValue = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
38854
39740
|
if (!identityValue || typeof identityValue !== "object" || identityValue.authMethod !== "jwt")
|
|
38855
39741
|
throw new RemoteWorkspaceInvitationError("INTERACTIVE_SESSION_REQUIRED");
|
|
38856
|
-
const
|
|
38857
|
-
if (
|
|
39742
|
+
const identity4 = parseWorkspaceIdentity(identityValue, target.userId);
|
|
39743
|
+
if (identity4.user.membershipId !== target.membershipId)
|
|
38858
39744
|
throw new WorkspaceIdentityMismatchError;
|
|
38859
39745
|
const request = invitationRequest(action, captured), read2 = action === "list" || action === "get";
|
|
38860
39746
|
let response, value;
|
|
@@ -38871,7 +39757,7 @@ class RemoteSkillsClient {
|
|
|
38871
39757
|
throw read2 ? new RemoteWorkspaceInvitationReadError : new RemoteWorkspaceInvitationUnconfirmedError;
|
|
38872
39758
|
}
|
|
38873
39759
|
try {
|
|
38874
|
-
return parseInvitationResult(action, value, captured,
|
|
39760
|
+
return parseInvitationResult(action, value, captured, identity4.organization.id);
|
|
38875
39761
|
} catch {
|
|
38876
39762
|
throw read2 ? new RemoteWorkspaceInvitationReadError : new RemoteWorkspaceInvitationUnconfirmedError;
|
|
38877
39763
|
}
|
|
@@ -38912,7 +39798,7 @@ class RemoteSkillsClient {
|
|
|
38912
39798
|
} catch {
|
|
38913
39799
|
throw new RemoteRecurringUnavailableError;
|
|
38914
39800
|
}
|
|
38915
|
-
let
|
|
39801
|
+
let identity4, identityValue;
|
|
38916
39802
|
try {
|
|
38917
39803
|
const response = await connection.request("/api/auth/whoami");
|
|
38918
39804
|
if (!response.ok) {
|
|
@@ -38921,8 +39807,8 @@ class RemoteSkillsClient {
|
|
|
38921
39807
|
}
|
|
38922
39808
|
identityValue = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readBoundedResponse(response, 64 * 1024)));
|
|
38923
39809
|
const observedUser = identityValue.user?.id;
|
|
38924
|
-
|
|
38925
|
-
if (target &&
|
|
39810
|
+
identity4 = parseWorkspaceIdentity(identityValue, target?.userId ?? workspaceExpectedUserId(observedUser));
|
|
39811
|
+
if (target && identity4.user.membershipId !== target.membershipId)
|
|
38926
39812
|
throw new WorkspaceIdentityMismatchError;
|
|
38927
39813
|
} catch {
|
|
38928
39814
|
throw new RemoteRecurringReadError;
|
|
@@ -38932,16 +39818,16 @@ class RemoteSkillsClient {
|
|
|
38932
39818
|
throw new RemoteRecurringError("RECURRING_HUMAN_APPROVAL_REQUIRED");
|
|
38933
39819
|
assertRecurringCapability(capability, "draft");
|
|
38934
39820
|
const selected = captured;
|
|
38935
|
-
const draft = await connection.dispatchRecurring("draft", { draftId: selected.draftId },
|
|
39821
|
+
const draft = await connection.dispatchRecurring("draft", { draftId: selected.draftId }, identity4);
|
|
38936
39822
|
if (!draft || draft.termsSha256 !== selected.approval.acceptedTermsSha256)
|
|
38937
39823
|
throw new RemoteRecurringError("RECURRING_TERMS_UNAVAILABLE");
|
|
38938
39824
|
}
|
|
38939
39825
|
let selectedConsent;
|
|
38940
39826
|
if (action === "occurrences") {
|
|
38941
39827
|
assertRecurringCapability(capability, "get");
|
|
38942
|
-
selectedConsent = await connection.dispatchRecurring("get", { consentId: captured.consentId },
|
|
39828
|
+
selectedConsent = await connection.dispatchRecurring("get", { consentId: captured.consentId }, identity4);
|
|
38943
39829
|
}
|
|
38944
|
-
const result = await connection.dispatchRecurring(action, captured,
|
|
39830
|
+
const result = await connection.dispatchRecurring(action, captured, identity4);
|
|
38945
39831
|
if (action === "occurrences") {
|
|
38946
39832
|
const items = result.items;
|
|
38947
39833
|
if (items.length && (!selectedConsent || items.some((item) => item.scheduleId !== selectedConsent.scheduleId)))
|
|
@@ -38949,7 +39835,7 @@ class RemoteSkillsClient {
|
|
|
38949
39835
|
}
|
|
38950
39836
|
return result;
|
|
38951
39837
|
}
|
|
38952
|
-
async dispatchRecurring(action, input2,
|
|
39838
|
+
async dispatchRecurring(action, input2, identity4) {
|
|
38953
39839
|
const request = recurringRequest(action, input2);
|
|
38954
39840
|
let response, value;
|
|
38955
39841
|
try {
|
|
@@ -38969,7 +39855,7 @@ class RemoteSkillsClient {
|
|
|
38969
39855
|
try {
|
|
38970
39856
|
if (response.status !== 200 || value === null)
|
|
38971
39857
|
throw new RemoteRecurringReadError;
|
|
38972
|
-
return parseRecurringResult(action, value, input2,
|
|
39858
|
+
return parseRecurringResult(action, value, input2, identity4);
|
|
38973
39859
|
} catch {
|
|
38974
39860
|
throw request.read ? new RemoteRecurringReadError : new RemoteRecurringUnconfirmedError;
|
|
38975
39861
|
}
|
|
@@ -39632,13 +40518,13 @@ var init_remote_invitation_recovery = __esm(() => {
|
|
|
39632
40518
|
});
|
|
39633
40519
|
|
|
39634
40520
|
// src/lib/remote-private-publications.ts
|
|
39635
|
-
import { createHash as
|
|
40521
|
+
import { createHash as createHash23 } from "crypto";
|
|
39636
40522
|
function checkedPublicationDeclaration(value) {
|
|
39637
|
-
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 || !
|
|
40523
|
+
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)
|
|
39638
40524
|
return bad();
|
|
39639
40525
|
try {
|
|
39640
40526
|
const manifest = JSON.parse(value.manifestText);
|
|
39641
|
-
if (!record9(manifest) || manifest.version !== value.version || validatePortableManifestContract(manifest, { strict: true }).length || !
|
|
40527
|
+
if (!record9(manifest) || manifest.version !== value.version || validatePortableManifestContract(manifest, { strict: true }).length || !hash4(manifest.provenance?.content_hash))
|
|
39642
40528
|
return bad();
|
|
39643
40529
|
} catch {
|
|
39644
40530
|
return bad();
|
|
@@ -39646,7 +40532,7 @@ function checkedPublicationDeclaration(value) {
|
|
|
39646
40532
|
return Object.freeze({ ...value });
|
|
39647
40533
|
}
|
|
39648
40534
|
function checkedPublicationView(value, skillId, intentId, declaration) {
|
|
39649
|
-
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)) || !
|
|
40535
|
+
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) || !date5(value.expiresAt) || !date5(value.createdAt) || !(value.versionId === null || publicationUuid(value.versionId)) || value.state === "committed" && value.versionId === null)
|
|
39650
40536
|
return invalid4();
|
|
39651
40537
|
if (declaration && ["version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize"].some((k) => value[k] !== declaration[k]))
|
|
39652
40538
|
return invalid4();
|
|
@@ -39877,12 +40763,12 @@ class RemotePrivatePublicationsClient {
|
|
|
39877
40763
|
previous = view;
|
|
39878
40764
|
if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
|
|
39879
40765
|
return view;
|
|
39880
|
-
await new Promise((
|
|
40766
|
+
await new Promise((resolve23) => {
|
|
39881
40767
|
const timer = setTimeout(done, Math.min(1000, until - Date.now()));
|
|
39882
40768
|
function done() {
|
|
39883
40769
|
clearTimeout(timer);
|
|
39884
40770
|
options.signal?.removeEventListener("abort", done);
|
|
39885
|
-
|
|
40771
|
+
resolve23();
|
|
39886
40772
|
}
|
|
39887
40773
|
options.signal?.addEventListener("abort", done, { once: true });
|
|
39888
40774
|
});
|
|
@@ -39893,7 +40779,7 @@ var PRIVATE_PUBLICATION_MAX_BYTES, failures2, PrivatePublicationError, bad = ()
|
|
|
39893
40779
|
throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
|
|
39894
40780
|
}, invalid4 = () => {
|
|
39895
40781
|
throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
|
|
39896
|
-
}, 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),
|
|
40782
|
+
}, 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), record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v, keys2) => record9(v) && Object.keys(v).sort().join(",") === keys2.sort().join(","), date5 = (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");
|
|
39897
40783
|
var init_remote_private_publications = __esm(() => {
|
|
39898
40784
|
init_fleet_credentials();
|
|
39899
40785
|
init_remote_workspace_selection();
|
|
@@ -40170,10 +41056,10 @@ var init_remote_auth = __esm(() => {
|
|
|
40170
41056
|
});
|
|
40171
41057
|
|
|
40172
41058
|
// src/lib/workspace-profile.ts
|
|
40173
|
-
import { constants as
|
|
41059
|
+
import { constants as constants15, closeSync as closeSync17, fstatSync as fstatSync17, lstatSync as lstatSync23, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, openSync as openSync17, readFileSync as readFileSync19, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
40174
41060
|
function stat2(path) {
|
|
40175
41061
|
try {
|
|
40176
|
-
return
|
|
41062
|
+
return lstatSync23(path);
|
|
40177
41063
|
} catch (error61) {
|
|
40178
41064
|
if (error61.code === "ENOENT")
|
|
40179
41065
|
return null;
|
|
@@ -40183,17 +41069,17 @@ function stat2(path) {
|
|
|
40183
41069
|
function safeText(file2) {
|
|
40184
41070
|
if (stat2(file2) === null)
|
|
40185
41071
|
return null;
|
|
40186
|
-
const fd =
|
|
41072
|
+
const fd = openSync17(file2, constants15.O_RDONLY | constants15.O_NOFOLLOW | constants15.O_NONBLOCK);
|
|
40187
41073
|
try {
|
|
40188
|
-
const s =
|
|
41074
|
+
const s = fstatSync17(fd);
|
|
40189
41075
|
if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
|
|
40190
41076
|
return fail("The selected profile must use bounded owner-only regular files.");
|
|
40191
41077
|
return readFileSync19(fd, "utf8");
|
|
40192
41078
|
} finally {
|
|
40193
|
-
|
|
41079
|
+
closeSync17(fd);
|
|
40194
41080
|
}
|
|
40195
41081
|
}
|
|
40196
|
-
function checkIdentityMetadata(file2,
|
|
41082
|
+
function checkIdentityMetadata(file2, identity4) {
|
|
40197
41083
|
const text3 = safeText(file2);
|
|
40198
41084
|
if (text3 === null)
|
|
40199
41085
|
return;
|
|
@@ -40205,7 +41091,7 @@ function checkIdentityMetadata(file2, identity3) {
|
|
|
40205
41091
|
}
|
|
40206
41092
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
40207
41093
|
return fail("The profile identity metadata is invalid.");
|
|
40208
|
-
for (const [key, expected] of Object.entries({ userId:
|
|
41094
|
+
for (const [key, expected] of Object.entries({ userId: identity4.user.id, orgId: identity4.organization.id })) {
|
|
40209
41095
|
if (value[key] !== undefined && value[key] !== expected)
|
|
40210
41096
|
return fail("The profile identity metadata does not match its authenticated key. Sign in again before managing this workspace.");
|
|
40211
41097
|
}
|
|
@@ -40227,10 +41113,10 @@ function prepareProfileWorkspace(action, source = process.env) {
|
|
|
40227
41113
|
const connection = await resolveSkillsConnection(env);
|
|
40228
41114
|
if (!connection || connection.apiOrigin !== origin)
|
|
40229
41115
|
return fail("The selected profile has no usable credential for this server.");
|
|
40230
|
-
const
|
|
40231
|
-
checkIdentityMetadata(getIdentityFilePath(env),
|
|
41116
|
+
const identity4 = await keyIdentity(connection.apiKey, origin);
|
|
41117
|
+
checkIdentityMetadata(getIdentityFilePath(env), identity4);
|
|
40232
41118
|
unchanged3();
|
|
40233
|
-
const context = { userId:
|
|
41119
|
+
const context = { userId: identity4.user.id, membershipId: identity4.user.membershipId };
|
|
40234
41120
|
return { origin, context, unchanged: unchanged3 };
|
|
40235
41121
|
} };
|
|
40236
41122
|
}
|
|
@@ -40311,15 +41197,15 @@ async function prepareRecurringCustomer(requested, source = process.env) {
|
|
|
40311
41197
|
await assertCurrent();
|
|
40312
41198
|
const value = await new RemoteSkillsClient(connection.apiKey, connection.apiOrigin).getIdentity();
|
|
40313
41199
|
const user = value.user;
|
|
40314
|
-
const
|
|
41200
|
+
const identity4 = parseWorkspaceIdentity(value, context?.userId ?? workspaceExpectedUserId(user?.id));
|
|
40315
41201
|
await assertCurrent();
|
|
40316
|
-
if (value.authMethod !== "api_key" || context &&
|
|
41202
|
+
if (value.authMethod !== "api_key" || context && identity4.user.membershipId !== context.membershipId)
|
|
40317
41203
|
changed();
|
|
40318
|
-
const binding = JSON.stringify([
|
|
41204
|
+
const binding = JSON.stringify([identity4.user.id, identity4.user.membershipId, identity4.organization.id]);
|
|
40319
41205
|
if (originalIdentity !== undefined && originalIdentity !== binding)
|
|
40320
41206
|
changed();
|
|
40321
41207
|
originalIdentity = binding;
|
|
40322
|
-
if (verification(
|
|
41208
|
+
if (verification(identity4.user.email) !== normalized)
|
|
40323
41209
|
throw new RecurringCustomerError("RECURRING_ACCOUNT_EMAIL_MISMATCH", "Use the selected account's current email. No verification request or recurring activation was submitted.");
|
|
40324
41210
|
return normalized;
|
|
40325
41211
|
}
|
|
@@ -40402,8 +41288,8 @@ var init_recurring_customer = __esm(() => {
|
|
|
40402
41288
|
});
|
|
40403
41289
|
|
|
40404
41290
|
// src/lib/recurring-recovery.ts
|
|
40405
|
-
import { constants as
|
|
40406
|
-
import { dirname as
|
|
41291
|
+
import { constants as constants16, closeSync as closeSync18, fsyncSync as fsyncSync5, fstatSync as fstatSync18, lstatSync as lstatSync24, mkdirSync as mkdirSync11, openSync as openSync18, readSync as readSync12, realpathSync as realpathSync11, renameSync as renameSync6, unlinkSync as unlinkSync5, writeFileSync as writeFileSync9 } from "fs";
|
|
41292
|
+
import { dirname as dirname23, isAbsolute as isAbsolute21, join as join34, resolve as resolve23 } from "path";
|
|
40407
41293
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
40408
41294
|
|
|
40409
41295
|
class RecoveryOwner {
|
|
@@ -40411,75 +41297,75 @@ class RecoveryOwner {
|
|
|
40411
41297
|
parents = [];
|
|
40412
41298
|
constructor(directory) {
|
|
40413
41299
|
this.directory = directory;
|
|
40414
|
-
if (!
|
|
41300
|
+
if (!isAbsolute21(directory) || resolve23(directory) !== directory || realpathSync11(directory) !== directory)
|
|
40415
41301
|
invalid5();
|
|
40416
|
-
for (let path = directory;; path =
|
|
40417
|
-
const stat3 =
|
|
41302
|
+
for (let path = directory;; path = dirname23(path)) {
|
|
41303
|
+
const stat3 = lstatSync24(path);
|
|
40418
41304
|
if (!stat3.isDirectory() || stat3.isSymbolicLink())
|
|
40419
41305
|
invalid5();
|
|
40420
41306
|
this.parents.push({ path, dev: stat3.dev, ino: stat3.ino });
|
|
40421
|
-
if (path ===
|
|
41307
|
+
if (path === dirname23(path))
|
|
40422
41308
|
break;
|
|
40423
41309
|
}
|
|
40424
41310
|
this.assert();
|
|
40425
41311
|
}
|
|
40426
41312
|
assert() {
|
|
40427
|
-
if (
|
|
41313
|
+
if (realpathSync11(this.directory) !== this.directory)
|
|
40428
41314
|
invalid5();
|
|
40429
41315
|
for (const prior of this.parents) {
|
|
40430
|
-
const now =
|
|
41316
|
+
const now = lstatSync24(prior.path);
|
|
40431
41317
|
if (!now.isDirectory() || now.isSymbolicLink() || now.dev !== prior.dev || now.ino !== prior.ino)
|
|
40432
41318
|
invalid5();
|
|
40433
41319
|
}
|
|
40434
|
-
const own2 =
|
|
41320
|
+
const own2 = lstatSync24(this.directory);
|
|
40435
41321
|
if ((own2.mode & 511) !== 448 || (own2.mode & 3584) !== 0 || process.getuid && own2.uid !== process.getuid())
|
|
40436
41322
|
invalid5();
|
|
40437
41323
|
}
|
|
40438
41324
|
read(name) {
|
|
40439
41325
|
this.assert();
|
|
40440
|
-
const fd =
|
|
41326
|
+
const fd = openSync18(join34(this.directory, name), constants16.O_RDONLY | constants16.O_NOFOLLOW | constants16.O_NONBLOCK);
|
|
40441
41327
|
try {
|
|
40442
|
-
const before =
|
|
41328
|
+
const before = fstatSync18(fd);
|
|
40443
41329
|
if (!before.isFile() || before.nlink !== 1 || (before.mode & 4095) !== 384 || before.size < 1 || before.size > 8192 || process.getuid && before.uid !== process.getuid())
|
|
40444
41330
|
invalid5();
|
|
40445
41331
|
const bytes = Buffer.alloc(before.size + 1);
|
|
40446
41332
|
let size = 0;
|
|
40447
41333
|
while (size < bytes.length) {
|
|
40448
|
-
const n2 =
|
|
41334
|
+
const n2 = readSync12(fd, bytes, size, bytes.length - size, size);
|
|
40449
41335
|
if (!n2)
|
|
40450
41336
|
break;
|
|
40451
41337
|
size += n2;
|
|
40452
41338
|
}
|
|
40453
|
-
const after =
|
|
41339
|
+
const after = fstatSync18(fd), path = lstatSync24(join34(this.directory, name));
|
|
40454
41340
|
if (size !== before.size || after.size !== before.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || path.dev !== before.dev || path.ino !== before.ino || !path.isFile() || path.isSymbolicLink())
|
|
40455
41341
|
invalid5();
|
|
40456
41342
|
this.assert();
|
|
40457
41343
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size));
|
|
40458
41344
|
} finally {
|
|
40459
|
-
|
|
41345
|
+
closeSync18(fd);
|
|
40460
41346
|
}
|
|
40461
41347
|
}
|
|
40462
41348
|
write(name, value) {
|
|
40463
41349
|
this.assert();
|
|
40464
41350
|
const bytes = canonicalJson2(value, 8192) + `
|
|
40465
41351
|
`;
|
|
40466
|
-
const fd =
|
|
41352
|
+
const fd = openSync18(join34(this.directory, name), constants16.O_WRONLY | constants16.O_CREAT | constants16.O_EXCL | constants16.O_NOFOLLOW, 384);
|
|
40467
41353
|
try {
|
|
40468
41354
|
writeFileSync9(fd, bytes);
|
|
40469
41355
|
fsyncSync5(fd);
|
|
40470
41356
|
} finally {
|
|
40471
|
-
|
|
41357
|
+
closeSync18(fd);
|
|
40472
41358
|
}
|
|
40473
41359
|
this.assert();
|
|
40474
41360
|
this.sync();
|
|
40475
41361
|
}
|
|
40476
41362
|
sync() {
|
|
40477
41363
|
this.assert();
|
|
40478
|
-
const fd =
|
|
41364
|
+
const fd = openSync18(this.directory, constants16.O_RDONLY | constants16.O_NOFOLLOW);
|
|
40479
41365
|
try {
|
|
40480
41366
|
fsyncSync5(fd);
|
|
40481
41367
|
} finally {
|
|
40482
|
-
|
|
41368
|
+
closeSync18(fd);
|
|
40483
41369
|
}
|
|
40484
41370
|
}
|
|
40485
41371
|
saveState(state, expected) {
|
|
@@ -40490,30 +41376,30 @@ class RecoveryOwner {
|
|
|
40490
41376
|
if (this.read("state.json") !== expected)
|
|
40491
41377
|
invalid5();
|
|
40492
41378
|
this.assert();
|
|
40493
|
-
renameSync6(
|
|
41379
|
+
renameSync6(join34(this.directory, name), join34(this.directory, "state.json"));
|
|
40494
41380
|
this.sync();
|
|
40495
41381
|
}
|
|
40496
41382
|
async locked(action) {
|
|
40497
41383
|
this.assert();
|
|
40498
41384
|
let fd;
|
|
40499
41385
|
try {
|
|
40500
|
-
fd =
|
|
41386
|
+
fd = openSync18(join34(this.directory, "operation.lock"), constants16.O_WRONLY | constants16.O_CREAT | constants16.O_EXCL | constants16.O_NOFOLLOW, 384);
|
|
40501
41387
|
} catch {
|
|
40502
41388
|
throw new RecurringCustomerError("RECURRING_RECOVERY_BUSY", "Recovery is locked. Confirm the original process has stopped before explicitly reconciling the retained lock; no request was retried.");
|
|
40503
41389
|
}
|
|
40504
|
-
const owner2 =
|
|
41390
|
+
const owner2 = fstatSync18(fd);
|
|
40505
41391
|
try {
|
|
40506
41392
|
writeFileSync9(fd, JSON.stringify({ pid: process.pid }));
|
|
40507
41393
|
fsyncSync5(fd);
|
|
40508
41394
|
this.sync();
|
|
40509
41395
|
return await action();
|
|
40510
41396
|
} finally {
|
|
40511
|
-
|
|
41397
|
+
closeSync18(fd);
|
|
40512
41398
|
this.assert();
|
|
40513
|
-
const now =
|
|
41399
|
+
const now = lstatSync24(join34(this.directory, "operation.lock"));
|
|
40514
41400
|
if (owner2.dev !== now.dev || owner2.ino !== now.ino || !now.isFile() || now.isSymbolicLink())
|
|
40515
41401
|
invalid5();
|
|
40516
|
-
unlinkSync5(
|
|
41402
|
+
unlinkSync5(join34(this.directory, "operation.lock"));
|
|
40517
41403
|
this.sync();
|
|
40518
41404
|
}
|
|
40519
41405
|
}
|
|
@@ -40546,7 +41432,7 @@ function base(customer, organizationId) {
|
|
|
40546
41432
|
};
|
|
40547
41433
|
}
|
|
40548
41434
|
function create(directory, intent) {
|
|
40549
|
-
if (!
|
|
41435
|
+
if (!isAbsolute21(directory) || resolve23(directory) !== directory || realpathSync11(dirname23(directory)) !== dirname23(directory))
|
|
40550
41436
|
invalid5();
|
|
40551
41437
|
mkdirSync11(directory, { mode: 448 });
|
|
40552
41438
|
const owner2 = new RecoveryOwner(directory), checked = intentSchema.parse(intent), intentSha256 = canonicalJsonSha256(checked);
|
|
@@ -40709,12 +41595,12 @@ var init_recurring_recovery = __esm(() => {
|
|
|
40709
41595
|
});
|
|
40710
41596
|
|
|
40711
41597
|
// src/lib/recurring-surface.ts
|
|
40712
|
-
import { isAbsolute as
|
|
41598
|
+
import { isAbsolute as isAbsolute22 } from "path";
|
|
40713
41599
|
function recurringMcpSchema(action) {
|
|
40714
41600
|
return action === "activate" ? recurringSurfaceSchemas.activate.extend({ code, confirm: exports_external.literal(true) }) : recurringSurfaceSchemas[action];
|
|
40715
41601
|
}
|
|
40716
|
-
function expectedDraft(draft,
|
|
40717
|
-
if (!draft || draft.termsSha256 !==
|
|
41602
|
+
function expectedDraft(draft, hash5) {
|
|
41603
|
+
if (!draft || draft.termsSha256 !== hash5)
|
|
40718
41604
|
throw new RemoteRecurringError("RECURRING_TERMS_UNAVAILABLE");
|
|
40719
41605
|
return draft;
|
|
40720
41606
|
}
|
|
@@ -40814,7 +41700,7 @@ var init_recurring_surface = __esm(() => {
|
|
|
40814
41700
|
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}$/);
|
|
40815
41701
|
context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
|
|
40816
41702
|
page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
|
|
40817
|
-
directory = exports_external.string().max(4096).refine(
|
|
41703
|
+
directory = exports_external.string().max(4096).refine(isAbsolute22, "Use an absolute recovery directory");
|
|
40818
41704
|
email4 = exports_external.string().trim().toLowerCase().email().max(254);
|
|
40819
41705
|
code = exports_external.string().regex(/^\d{6}$/);
|
|
40820
41706
|
recurringSurfaceSchemas = {
|
|
@@ -42643,21 +43529,21 @@ var init_credential_state = __esm(() => {
|
|
|
42643
43529
|
});
|
|
42644
43530
|
|
|
42645
43531
|
// src/lib/run-state.ts
|
|
42646
|
-
import { createHash as
|
|
42647
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync20, readdirSync as
|
|
42648
|
-
import { extname, join as
|
|
43532
|
+
import { createHash as createHash24, randomBytes } from "crypto";
|
|
43533
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
43534
|
+
import { extname, join as join35, relative as relative9 } from "path";
|
|
42649
43535
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
42650
43536
|
const now = new Date;
|
|
42651
43537
|
const id = createRunId(now);
|
|
42652
43538
|
const day = now.toISOString().slice(0, 10);
|
|
42653
43539
|
const skillName = normalizeSkillName(params.skill);
|
|
42654
43540
|
const root = getProjectStateDir(targetDir);
|
|
42655
|
-
const runDir =
|
|
42656
|
-
const logsDir =
|
|
42657
|
-
const exportDir =
|
|
43541
|
+
const runDir = join35(root, "runs", day, id);
|
|
43542
|
+
const logsDir = join35(runDir, "logs");
|
|
43543
|
+
const exportDir = join35(root, "exports", skillName, id);
|
|
42658
43544
|
mkdirSync12(logsDir, { recursive: true });
|
|
42659
43545
|
mkdirSync12(exportDir, { recursive: true });
|
|
42660
|
-
mkdirSync12(
|
|
43546
|
+
mkdirSync12(join35(root, "tmp"), { recursive: true });
|
|
42661
43547
|
const record10 = {
|
|
42662
43548
|
id,
|
|
42663
43549
|
skill: skillName,
|
|
@@ -42709,22 +43595,22 @@ function updateSkillRun(context2, patch) {
|
|
|
42709
43595
|
return context2.record;
|
|
42710
43596
|
}
|
|
42711
43597
|
function writeRunLogs(context2, stdout = "", stderr = "") {
|
|
42712
|
-
writeFileSync10(
|
|
42713
|
-
writeFileSync10(
|
|
43598
|
+
writeFileSync10(join35(context2.logsDir, "stdout.log"), stdout);
|
|
43599
|
+
writeFileSync10(join35(context2.logsDir, "stderr.log"), stderr);
|
|
42714
43600
|
}
|
|
42715
43601
|
function appendRunEvent(context2, event, data = {}) {
|
|
42716
43602
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
42717
43603
|
`;
|
|
42718
|
-
const path =
|
|
43604
|
+
const path = join35(context2.runDir, "events.ndjson");
|
|
42719
43605
|
const previous = existsSync15(path) ? readFileSync20(path, "utf-8") : "";
|
|
42720
43606
|
writeFileSync10(path, previous + line);
|
|
42721
43607
|
}
|
|
42722
43608
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
42723
|
-
const runsRoot =
|
|
43609
|
+
const runsRoot = join35(getProjectStateDir(targetDir), "runs");
|
|
42724
43610
|
if (!existsSync15(runsRoot))
|
|
42725
43611
|
return null;
|
|
42726
|
-
for (const day of
|
|
42727
|
-
const record10 = readRunRecord(
|
|
43612
|
+
for (const day of readdirSync11(runsRoot)) {
|
|
43613
|
+
const record10 = readRunRecord(join35(runsRoot, day, runId));
|
|
42728
43614
|
if (record10)
|
|
42729
43615
|
return record10;
|
|
42730
43616
|
}
|
|
@@ -42741,11 +43627,11 @@ function skillRunEnv(context2) {
|
|
|
42741
43627
|
};
|
|
42742
43628
|
}
|
|
42743
43629
|
function writeRunRecord(context2) {
|
|
42744
|
-
writeFileSync10(
|
|
43630
|
+
writeFileSync10(join35(context2.runDir, "run.json"), JSON.stringify(context2.record, null, 2) + `
|
|
42745
43631
|
`);
|
|
42746
43632
|
}
|
|
42747
43633
|
function writeArtifactsManifest(context2, artifacts) {
|
|
42748
|
-
writeFileSync10(
|
|
43634
|
+
writeFileSync10(join35(context2.runDir, "artifacts.json"), JSON.stringify({ runId: context2.record.id, artifacts }, null, 2) + `
|
|
42749
43635
|
`);
|
|
42750
43636
|
}
|
|
42751
43637
|
function collectRunArtifacts(context2) {
|
|
@@ -42758,14 +43644,14 @@ function collectRunArtifacts(context2) {
|
|
|
42758
43644
|
artifacts.push({
|
|
42759
43645
|
path: toProjectRelative(context2.targetDir, path),
|
|
42760
43646
|
mime: mimeForPath(path),
|
|
42761
|
-
sha256:
|
|
43647
|
+
sha256: createHash24("sha256").update(bytes).digest("hex"),
|
|
42762
43648
|
sizeBytes: stat3.size
|
|
42763
43649
|
});
|
|
42764
43650
|
}
|
|
42765
43651
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
42766
43652
|
}
|
|
42767
43653
|
function readRunRecord(runDir) {
|
|
42768
|
-
const path =
|
|
43654
|
+
const path = join35(runDir, "run.json");
|
|
42769
43655
|
if (!existsSync15(path))
|
|
42770
43656
|
return null;
|
|
42771
43657
|
try {
|
|
@@ -42776,8 +43662,8 @@ function readRunRecord(runDir) {
|
|
|
42776
43662
|
}
|
|
42777
43663
|
function walkFiles(dir) {
|
|
42778
43664
|
const files = [];
|
|
42779
|
-
for (const entry of
|
|
42780
|
-
const full =
|
|
43665
|
+
for (const entry of readdirSync11(dir)) {
|
|
43666
|
+
const full = join35(dir, entry);
|
|
42781
43667
|
if (statSync10(full).isDirectory())
|
|
42782
43668
|
files.push(...walkFiles(full));
|
|
42783
43669
|
else
|
|
@@ -42789,7 +43675,7 @@ function createRunId(now) {
|
|
|
42789
43675
|
return `run_${now.getTime().toString(36)}_${randomBytes(4).toString("hex")}`;
|
|
42790
43676
|
}
|
|
42791
43677
|
function toProjectRelative(targetDir, path) {
|
|
42792
|
-
const rel =
|
|
43678
|
+
const rel = relative9(targetDir, path).split(/[\\/]/).join("/");
|
|
42793
43679
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
42794
43680
|
}
|
|
42795
43681
|
function mimeForPath(path) {
|
|
@@ -43030,11 +43916,11 @@ var init_cloud_executions = __esm(() => {
|
|
|
43030
43916
|
// node_modules/@hasna/secrets/dist/sdk.js
|
|
43031
43917
|
import { isIP as isIP2 } from "net";
|
|
43032
43918
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
43033
|
-
import { closeSync as
|
|
43919
|
+
import { closeSync as closeSync19, fstatSync as fstatSync19, openSync as openSync19, readFileSync as readFileSync21 } from "fs";
|
|
43034
43920
|
import { O_NOFOLLOW as O_NOFOLLOW3, O_NONBLOCK as O_NONBLOCK3, O_RDONLY as O_RDONLY3 } from "constants";
|
|
43035
43921
|
import { createRequire as createRequire2 } from "module";
|
|
43036
43922
|
import { hostname as osHostname2 } from "os";
|
|
43037
|
-
import { isAbsolute as
|
|
43923
|
+
import { isAbsolute as isAbsolute23, join as join36 } from "path";
|
|
43038
43924
|
import { createRequire as createRequire22 } from "module";
|
|
43039
43925
|
function envToken2(name) {
|
|
43040
43926
|
return name.toUpperCase().replace(/-/g, "_");
|
|
@@ -43058,21 +43944,21 @@ function homeDir2(env) {
|
|
|
43058
43944
|
}
|
|
43059
43945
|
function absoluteOverride2(env, key) {
|
|
43060
43946
|
const value = env[key]?.trim();
|
|
43061
|
-
return value &&
|
|
43947
|
+
return value && isAbsolute23(value) ? value : null;
|
|
43062
43948
|
}
|
|
43063
43949
|
function hasnaHomeDir2(env) {
|
|
43064
43950
|
const override = absoluteOverride2(env, HASNA_HOME_ENV_KEY2);
|
|
43065
43951
|
if (override)
|
|
43066
43952
|
return override;
|
|
43067
43953
|
const home = homeDir2(env);
|
|
43068
|
-
return home ?
|
|
43954
|
+
return home ? join36(home, HASNA_HOME_DIR2) : null;
|
|
43069
43955
|
}
|
|
43070
43956
|
function appConfigDir2(name, env) {
|
|
43071
43957
|
const configRoot = absoluteOverride2(env, HASNA_CONFIG_HOME_ENV_KEY2);
|
|
43072
43958
|
if (configRoot)
|
|
43073
|
-
return
|
|
43959
|
+
return join36(configRoot, name);
|
|
43074
43960
|
const root = hasnaHomeDir2(env);
|
|
43075
|
-
return root ?
|
|
43961
|
+
return root ? join36(root, name, CONFIG_SUBDIR2) : null;
|
|
43076
43962
|
}
|
|
43077
43963
|
function credentialDiskSourceList2(name, env, profile = null) {
|
|
43078
43964
|
if (!SAFE_APP_SLUG2.test(name))
|
|
@@ -43081,7 +43967,7 @@ function credentialDiskSourceList2(name, env, profile = null) {
|
|
|
43081
43967
|
if (!directory2)
|
|
43082
43968
|
return [];
|
|
43083
43969
|
const file2 = profile ? `${CREDENTIALS_FILE2}-${profile}` : CREDENTIALS_FILE2;
|
|
43084
|
-
return [{ path:
|
|
43970
|
+
return [{ path: join36(directory2, file2), tier: "disk" }];
|
|
43085
43971
|
}
|
|
43086
43972
|
function credentialDiskSources2(name, env) {
|
|
43087
43973
|
return credentialDiskSourceList2(name, env, null).map((s) => s.path);
|
|
@@ -43135,7 +44021,7 @@ function readAppConfigFile2(path) {
|
|
|
43135
44021
|
};
|
|
43136
44022
|
let fd = -1;
|
|
43137
44023
|
try {
|
|
43138
|
-
fd =
|
|
44024
|
+
fd = openSync19(path, O_RDONLY3 | O_NOFOLLOW3 | O_NONBLOCK3);
|
|
43139
44025
|
} catch (error61) {
|
|
43140
44026
|
const code2 = error61.code;
|
|
43141
44027
|
if (code2 === "ENOENT" || code2 === "ENOTDIR")
|
|
@@ -43145,7 +44031,7 @@ function readAppConfigFile2(path) {
|
|
|
43145
44031
|
unsafe(`the path could not be opened (${code2 ?? "unknown error"})`);
|
|
43146
44032
|
}
|
|
43147
44033
|
try {
|
|
43148
|
-
const before =
|
|
44034
|
+
const before = fstatSync19(fd);
|
|
43149
44035
|
if (!before.isFile())
|
|
43150
44036
|
unsafe("the path is not a regular file");
|
|
43151
44037
|
if (!configFileModeAllowed2(before.mode)) {
|
|
@@ -43157,14 +44043,14 @@ function readAppConfigFile2(path) {
|
|
|
43157
44043
|
if (before.size > MAX_CREDENTIAL_FILE_BYTES2)
|
|
43158
44044
|
unsafe("the file exceeds the size limit");
|
|
43159
44045
|
const bytes = readFileSync21(fd);
|
|
43160
|
-
const after =
|
|
44046
|
+
const after = fstatSync19(fd);
|
|
43161
44047
|
if (!configFileReadsCoherent2(before, after)) {
|
|
43162
44048
|
unsafe("the file changed while being read");
|
|
43163
44049
|
}
|
|
43164
44050
|
return parseEnvFile2(bytes.toString("utf8"));
|
|
43165
44051
|
} finally {
|
|
43166
44052
|
if (fd !== -1)
|
|
43167
|
-
|
|
44053
|
+
closeSync19(fd);
|
|
43168
44054
|
}
|
|
43169
44055
|
}
|
|
43170
44056
|
function readCredentialFile2(path, apiKeyKeys) {
|
|
@@ -44195,7 +45081,7 @@ function createSecretsClientFromEnv(env = process.env, overrides = {}) {
|
|
|
44195
45081
|
});
|
|
44196
45082
|
return new SecretsClient({ ...clientOverrides, baseUrl, apiKey });
|
|
44197
45083
|
}
|
|
44198
|
-
var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), CREDENTIAL_PROFILE_ENV_KEY2 = "HASNA_PROFILE", CredentialResolutionError2, CredentialFileUnsafeError2, HASNA_HOME_ENV_KEY2 = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY2 = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY2 = "HASNA_STATION", HASNA_HOME_DIR2 = ".hasna", CONFIG_SUBDIR2 = "config", CREDENTIALS_FILE2 = "credentials", KEYCHAIN_SECURITY_BIN2 = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX2 = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS2 = 44, KEYCHAIN_SPAWN_TIMEOUT_MS2 = 1e4, MAX_CREDENTIAL_FILE_BYTES2, SAFE_APP_SLUG2, SAFE_PROFILE2, ILLEGAL_IN_HEADER_VALUE2, VAULT_POINTER_SHAPE2, CREDENTIAL_SHAPED_KEY2, INSPECT_CUSTOM2, CREDENTIAL_SEAL2, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE = "caller-supplied CredentialProvider", AMBIENT_ENVIRONMENT2, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk2, DEFAULT_FLEET_GATEWAY_ORIGIN2 = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN2, DNS_LABEL_PATTERN2, ClientTransportConfigurationError2, HasnaHttpError, DEFAULT_RETRY_STATUSES, IDEMPOTENT_METHODS2, AUTHORITY_OVERRIDE_HEADERS2, defaultSleep = (ms) => new Promise((
|
|
45084
|
+
var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), CREDENTIAL_PROFILE_ENV_KEY2 = "HASNA_PROFILE", CredentialResolutionError2, CredentialFileUnsafeError2, HASNA_HOME_ENV_KEY2 = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY2 = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY2 = "HASNA_STATION", HASNA_HOME_DIR2 = ".hasna", CONFIG_SUBDIR2 = "config", CREDENTIALS_FILE2 = "credentials", KEYCHAIN_SECURITY_BIN2 = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX2 = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS2 = 44, KEYCHAIN_SPAWN_TIMEOUT_MS2 = 1e4, MAX_CREDENTIAL_FILE_BYTES2, SAFE_APP_SLUG2, SAFE_PROFILE2, ILLEGAL_IN_HEADER_VALUE2, VAULT_POINTER_SHAPE2, CREDENTIAL_SHAPED_KEY2, INSPECT_CUSTOM2, CREDENTIAL_SEAL2, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE = "caller-supplied CredentialProvider", AMBIENT_ENVIRONMENT2, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk2, DEFAULT_FLEET_GATEWAY_ORIGIN2 = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN2, DNS_LABEL_PATTERN2, ClientTransportConfigurationError2, HasnaHttpError, DEFAULT_RETRY_STATUSES, IDEMPOTENT_METHODS2, AUTHORITY_OVERRIDE_HEADERS2, defaultSleep = (ms) => new Promise((resolve24) => setTimeout(resolve24, ms)), init_transport2, MAX_CREDENTIAL_FILE_BYTES22, INSPECT_CUSTOM22, CREDENTIAL_SEAL22, AMBIENT_ENVIRONMENT22, SECRETS_PACKAGE_SPECIFIER2, requireSecretsSdk22, IDEMPOTENT_METHODS22, AUTHORITY_OVERRIDE_HEADERS22, init_storage, PATHS_RESOLVER_APP_SLUG_RE2, SQLITE_SIDECAR_FILES, LEGACY_SERVICE_FILES, init_data_dir, SecretsTestIsolationError, TEST_ENTRYPOINT_RE, init_test_isolation, init_client, ApiError;
|
|
44199
45085
|
var init_sdk = __esm(() => {
|
|
44200
45086
|
init_transport2 = __esm2(() => {
|
|
44201
45087
|
CredentialResolutionError2 = class CredentialResolutionError3 extends Error {
|
|
@@ -44330,75 +45216,75 @@ var init_sdk = __esm(() => {
|
|
|
44330
45216
|
});
|
|
44331
45217
|
|
|
44332
45218
|
// src/lib/execution-secrets.ts
|
|
44333
|
-
import { constants as
|
|
45219
|
+
import { constants as constants17, closeSync as closeSync20, fstatSync as fstatSync20, openSync as openSync20, readSync as readSync13, realpathSync as realpathSync12 } from "fs";
|
|
44334
45220
|
import { hostname as stationHostname } from "os";
|
|
44335
45221
|
function selectedSecretBindingsTemplate(scope, names, createClient = createSecretsClientFromEnv) {
|
|
44336
45222
|
let secretsAuthority;
|
|
44337
45223
|
try {
|
|
44338
45224
|
secretsAuthority = createClient().baseUrl;
|
|
44339
45225
|
} catch {
|
|
44340
|
-
return
|
|
45226
|
+
return refuse5("LOCAL_SECRET_UNAVAILABLE", "Configure the Secrets CLI authority and authentication before preparing execution bindings.");
|
|
44341
45227
|
}
|
|
44342
45228
|
const { authority, workspaceId, profileRevision, slug: slug2, version: version2, bundleDigest } = scope.selection;
|
|
44343
45229
|
return {
|
|
44344
45230
|
schema: "hasna.skills-secret-bindings.v1",
|
|
44345
45231
|
selection: { authority, workspaceId, profileId: scope.profileId, profileRevision, slug: slug2, version: version2, bundleDigest },
|
|
44346
|
-
consumer: { stationId: process.env.HASNA_STATION || stationHostname(), workspaceDirectory:
|
|
45232
|
+
consumer: { stationId: process.env.HASNA_STATION || stationHostname(), workspaceDirectory: realpathSync12(scope.cwd) },
|
|
44347
45233
|
secretsAuthority,
|
|
44348
45234
|
bindings: Object.fromEntries(names.map((name) => [name, ""]))
|
|
44349
45235
|
};
|
|
44350
45236
|
}
|
|
44351
|
-
function
|
|
45237
|
+
function refuse5(code2, message) {
|
|
44352
45238
|
throw new SkillSelectionError(code2, message);
|
|
44353
45239
|
}
|
|
44354
45240
|
function record10(value) {
|
|
44355
45241
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
44356
45242
|
}
|
|
44357
|
-
function
|
|
45243
|
+
function exactKeys3(value, keys2) {
|
|
44358
45244
|
return Object.keys(value).length === keys2.length && keys2.every((key) => Object.hasOwn(value, key));
|
|
44359
45245
|
}
|
|
44360
|
-
function
|
|
45246
|
+
function identifier4(value) {
|
|
44361
45247
|
return typeof value === "string" && value.length > 0 && value.length <= 2048 && !/[\x00-\x20\x7f]/.test(value);
|
|
44362
45248
|
}
|
|
44363
45249
|
function declaredSecretNames(value) {
|
|
44364
45250
|
if (value === undefined)
|
|
44365
45251
|
return [];
|
|
44366
45252
|
if (!Array.isArray(value) || value.length > 32 || value.some((name) => typeof name !== "string" || !/^[A-Z][A-Z0-9_]{0,127}$/.test(name)) || new Set(value).size !== value.length) {
|
|
44367
|
-
return
|
|
45253
|
+
return refuse5("INVALID_SKILL_MANIFEST", "Runtime environment references must be unique uppercase identifiers (at most 32).");
|
|
44368
45254
|
}
|
|
44369
45255
|
if (value.some((name) => /^(?:PATH|HOME|TMPDIR|TEMP|TMP|LANG|LC_ALL|TERM|SHELL|ENV|BASH_ENV|IFS|NODE_OPTIONS|NODE_PATH)$/.test(name) || /^(?:SKILLS_|BUN_|PYTHON|LD_|DYLD_)/.test(name))) {
|
|
44370
|
-
return
|
|
45256
|
+
return refuse5("INVALID_SKILL_MANIFEST", "Runtime environment references cannot override execution controls.");
|
|
44371
45257
|
}
|
|
44372
45258
|
return value;
|
|
44373
45259
|
}
|
|
44374
45260
|
function validateSelectedSecretBindings(value, scope, names) {
|
|
44375
|
-
if (!record10(value) || !
|
|
44376
|
-
return
|
|
45261
|
+
if (!record10(value) || !exactKeys3(value, ["schema", "selection", "consumer", "secretsAuthority", "bindings"]) || value.schema !== "hasna.skills-secret-bindings.v1" || !record10(value.selection) || !exactKeys3(value.selection, ["authority", "workspaceId", "profileId", "profileRevision", "slug", "version", "bundleDigest"]) || !record10(value.consumer) || !exactKeys3(value.consumer, ["stationId", "workspaceDirectory"]) || !record10(value.bindings)) {
|
|
45262
|
+
return refuse5("INVALID_SECRET_BINDINGS", "Secret bindings do not match the hasna.skills-secret-bindings.v1 contract.");
|
|
44377
45263
|
}
|
|
44378
45264
|
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 })) {
|
|
44379
45265
|
if (value.selection[key] !== expected)
|
|
44380
|
-
return
|
|
45266
|
+
return refuse5("SECRET_BINDING_SCOPE_MISMATCH", "Secret bindings do not match the exact selected authority, workspace, profile, revision, version and digest.");
|
|
44381
45267
|
}
|
|
44382
45268
|
let workspaceDirectory;
|
|
44383
45269
|
try {
|
|
44384
|
-
workspaceDirectory =
|
|
45270
|
+
workspaceDirectory = realpathSync12(scope.cwd);
|
|
44385
45271
|
} catch {
|
|
44386
|
-
return
|
|
45272
|
+
return refuse5("SECRET_BINDING_CONSUMER_MISMATCH", "The execution workspace cannot be resolved.");
|
|
44387
45273
|
}
|
|
44388
45274
|
if (value.consumer.stationId !== (process.env.HASNA_STATION || stationHostname()) || value.consumer.workspaceDirectory !== workspaceDirectory) {
|
|
44389
|
-
return
|
|
45275
|
+
return refuse5("SECRET_BINDING_CONSUMER_MISMATCH", "Secret bindings do not match this station and canonical workspace directory.");
|
|
44390
45276
|
}
|
|
44391
45277
|
try {
|
|
44392
|
-
if (!
|
|
45278
|
+
if (!identifier4(value.secretsAuthority))
|
|
44393
45279
|
throw new Error;
|
|
44394
45280
|
const url2 = new URL(value.secretsAuthority);
|
|
44395
45281
|
if (url2.protocol !== "https:" && !(url2.protocol === "http:" && ["127.0.0.1", "[::1]", "localhost"].includes(url2.hostname)) || url2.username || url2.password || url2.search || url2.hash || !url2.pathname.endsWith("/v1") || url2.href !== value.secretsAuthority)
|
|
44396
45282
|
throw new Error;
|
|
44397
45283
|
} catch {
|
|
44398
|
-
return
|
|
45284
|
+
return refuse5("INVALID_SECRET_BINDINGS", "Secret bindings require a canonical HTTPS Secrets /v1 authority (loopback HTTP is allowed).");
|
|
44399
45285
|
}
|
|
44400
|
-
if (!
|
|
44401
|
-
return
|
|
45286
|
+
if (!exactKeys3(value.bindings, names) || Object.values(value.bindings).some((key) => !identifier4(key) || !/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(key) || key.split("/").some((part) => !part || part === "." || part === ".."))) {
|
|
45287
|
+
return refuse5("INVALID_SECRET_BINDINGS", "Bind every declared environment name exactly once to a vault key; undeclared bindings and values are refused.");
|
|
44402
45288
|
}
|
|
44403
45289
|
return structuredClone(value);
|
|
44404
45290
|
}
|
|
@@ -44409,10 +45295,10 @@ async function resolveSelectedSecrets(bindings, createClient = createSecretsClie
|
|
|
44409
45295
|
try {
|
|
44410
45296
|
client = createClient();
|
|
44411
45297
|
} catch {
|
|
44412
|
-
return
|
|
45298
|
+
return refuse5("LOCAL_SECRET_UNAVAILABLE", "The configured Secrets client could not authenticate. No ambient credential fallback was attempted.");
|
|
44413
45299
|
}
|
|
44414
45300
|
if (client.baseUrl !== bindings.secretsAuthority)
|
|
44415
|
-
return
|
|
45301
|
+
return refuse5("SECRET_BINDING_AUTHORITY_MISMATCH", "The configured Secrets authority differs from the reviewed binding. No secret was requested.");
|
|
44416
45302
|
const env = {};
|
|
44417
45303
|
const signal = AbortSignal.timeout(15000);
|
|
44418
45304
|
try {
|
|
@@ -44424,7 +45310,7 @@ async function resolveSelectedSecrets(bindings, createClient = createSecretsClie
|
|
|
44424
45310
|
}
|
|
44425
45311
|
return env;
|
|
44426
45312
|
} catch {
|
|
44427
|
-
return
|
|
45313
|
+
return refuse5("LOCAL_SECRET_UNAVAILABLE", "A declared secret could not be resolved, was invalid, or has expired. The skill was not started; no fallback was attempted.");
|
|
44428
45314
|
}
|
|
44429
45315
|
}
|
|
44430
45316
|
function redactExecutionSecrets(text3, env) {
|
|
@@ -44572,10 +45458,10 @@ __export(exports_selected_run, {
|
|
|
44572
45458
|
prepareSelectedSecretBindings: () => prepareSelectedSecretBindings,
|
|
44573
45459
|
executeSelectedLocal: () => executeSelectedLocal
|
|
44574
45460
|
});
|
|
44575
|
-
import { createHash as
|
|
45461
|
+
import { createHash as createHash25 } from "crypto";
|
|
44576
45462
|
import { spawn as spawn2 } from "child_process";
|
|
44577
|
-
import { mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync11, realpathSync as
|
|
44578
|
-
import { dirname as
|
|
45463
|
+
import { mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync11, realpathSync as realpathSync13 } from "fs";
|
|
45464
|
+
import { dirname as dirname24, join as join37, resolve as resolve24 } from "path";
|
|
44579
45465
|
import { tmpdir, hostname as stationHostname2 } from "os";
|
|
44580
45466
|
async function resolveSelectedRun(spec, profileId, options = {}) {
|
|
44581
45467
|
const context2 = await resolveSelectionContext(profileId, options);
|
|
@@ -44655,7 +45541,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
44655
45541
|
throw new SkillSelectionError("INVALID_SECRET_BINDINGS", "Explicit environment values must be bounded strings without NUL bytes.");
|
|
44656
45542
|
secretEnv[name] = value;
|
|
44657
45543
|
}
|
|
44658
|
-
const runDirectory = mkdtempSync3(
|
|
45544
|
+
const runDirectory = mkdtempSync3(join37(tmpdir(), "skills-execution-"));
|
|
44659
45545
|
const paths = new SkillEntryPaths;
|
|
44660
45546
|
for (const entry of entries) {
|
|
44661
45547
|
paths.add(entry.path, 100, () => {
|
|
@@ -44663,12 +45549,12 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
44663
45549
|
}, () => {
|
|
44664
45550
|
throw new SkillSelectionError("INVALID_BUNDLE_PATH", "The execution bundle path exceeds its limit.");
|
|
44665
45551
|
});
|
|
44666
|
-
const destination =
|
|
44667
|
-
mkdirSync13(
|
|
45552
|
+
const destination = join37(runDirectory, entry.path);
|
|
45553
|
+
mkdirSync13(dirname24(destination), { recursive: true, mode: 448 });
|
|
44668
45554
|
writeFileSync11(destination, entry.bytes, { flag: "wx", mode: entry.mode & 511 });
|
|
44669
45555
|
}
|
|
44670
45556
|
env.SKILLS_INPUT_JSON = input2;
|
|
44671
|
-
env.SKILLS_WORKSPACE_DIR =
|
|
45557
|
+
env.SKILLS_WORKSPACE_DIR = resolve24(options.cwd ?? process.cwd());
|
|
44672
45558
|
env.SKILLS_RUN_DIR = runDirectory;
|
|
44673
45559
|
if (runtime.version) {
|
|
44674
45560
|
const version2 = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [], "--version"], runDirectory, env, "", Math.min(timeoutMs, 5000));
|
|
@@ -44681,7 +45567,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
44681
45567
|
const { authority: authority2, workspaceId, profileRevision, slug: slug2, version: version2, bundleDigest } = selection2;
|
|
44682
45568
|
const request2 = {
|
|
44683
45569
|
selection: { authority: authority2, workspaceId, profileId, profileRevision, slug: slug2, version: version2, bundleDigest },
|
|
44684
|
-
consumer: { stationId: process.env.HASNA_STATION || stationHostname2(), workspaceDirectory:
|
|
45570
|
+
consumer: { stationId: process.env.HASNA_STATION || stationHostname2(), workspaceDirectory: realpathSync13(options.cwd ?? process.cwd()) }
|
|
44685
45571
|
};
|
|
44686
45572
|
const grant = await (options.resolveExecutionGrant ?? resolveExecutionGrant)(request2);
|
|
44687
45573
|
if (!grant || !grantIdentifier(grant.policyRevision) || !grantIdentifier(grant.grantId))
|
|
@@ -44691,20 +45577,20 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
44691
45577
|
}
|
|
44692
45578
|
if (bindings)
|
|
44693
45579
|
secretEnv = await resolveSelectedSecrets(bindings, options.createSecretsClient);
|
|
44694
|
-
const result = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [],
|
|
45580
|
+
const result = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [], join37(runDirectory, declaredEntry), ...args], runDirectory, { ...env, ...secretEnv }, input2, timeoutMs);
|
|
44695
45581
|
result.stdout = redactExecutionSecrets(result.stdout, secretEnv);
|
|
44696
45582
|
result.stderr = redactExecutionSecrets(result.stderr, secretEnv);
|
|
44697
45583
|
const receipt = {
|
|
44698
45584
|
selection: selection2,
|
|
44699
45585
|
target: "local",
|
|
44700
45586
|
runtime: runtimeName,
|
|
44701
|
-
inputDigest: `sha256:${
|
|
45587
|
+
inputDigest: `sha256:${createHash25("sha256").update(input2).digest("hex")}`,
|
|
44702
45588
|
runDirectory,
|
|
44703
45589
|
...executionGrant ? { executionGrant } : {},
|
|
44704
45590
|
...bindings ? { secretBinding: { schema: bindings.schema, selection: bindings.selection, consumer: bindings.consumer, secretsAuthority: bindings.secretsAuthority, bindings: bindings.bindings } } : {},
|
|
44705
45591
|
...result
|
|
44706
45592
|
};
|
|
44707
|
-
writeFileSync11(
|
|
45593
|
+
writeFileSync11(join37(runDirectory, ".execution-receipt.json"), JSON.stringify({ ...receipt, stdout: undefined, stderr: undefined }), { mode: 384 });
|
|
44708
45594
|
return receipt;
|
|
44709
45595
|
}
|
|
44710
45596
|
async function subprocess(command, cwd, env, input2, timeoutMs) {
|
|
@@ -44893,8 +45779,8 @@ var init_blog_article = __esm(() => {
|
|
|
44893
45779
|
});
|
|
44894
45780
|
|
|
44895
45781
|
// src/mcp/operation-tools.ts
|
|
44896
|
-
import { existsSync as existsSync16, readdirSync as
|
|
44897
|
-
import { join as
|
|
45782
|
+
import { existsSync as existsSync16, readdirSync as readdirSync12, statSync as statSync11 } from "fs";
|
|
45783
|
+
import { join as join38 } from "path";
|
|
44898
45784
|
function registerOperationTools(server) {
|
|
44899
45785
|
server.registerTool("cloud_skill_eligibility", {
|
|
44900
45786
|
title: "Cloud Skill Eligibility",
|
|
@@ -45370,8 +46256,8 @@ function registerOperationTools(server) {
|
|
|
45370
46256
|
let skillCount = 0;
|
|
45371
46257
|
if (exists) {
|
|
45372
46258
|
try {
|
|
45373
|
-
skillCount =
|
|
45374
|
-
const full =
|
|
46259
|
+
skillCount = readdirSync12(agentSkillsPath).filter((f) => {
|
|
46260
|
+
const full = join38(agentSkillsPath, f);
|
|
45375
46261
|
return !f.startsWith(".") && statSync11(full).isDirectory();
|
|
45376
46262
|
}).length;
|
|
45377
46263
|
} catch {}
|
|
@@ -45439,14 +46325,14 @@ var init_operation_tools = __esm(() => {
|
|
|
45439
46325
|
|
|
45440
46326
|
// src/lib/feedback.ts
|
|
45441
46327
|
import { appendFileSync, existsSync as existsSync17, mkdirSync as mkdirSync14 } from "fs";
|
|
45442
|
-
import { dirname as
|
|
46328
|
+
import { dirname as dirname25, join as join39 } from "path";
|
|
45443
46329
|
import { Database } from "bun:sqlite";
|
|
45444
46330
|
function getFeedbackDbPath() {
|
|
45445
|
-
return
|
|
46331
|
+
return join39(getDataDir(), "skills.db");
|
|
45446
46332
|
}
|
|
45447
46333
|
function getFeedbackDb() {
|
|
45448
46334
|
const dbPath = getFeedbackDbPath();
|
|
45449
|
-
const dir =
|
|
46335
|
+
const dir = dirname25(dbPath);
|
|
45450
46336
|
if (!existsSync17(dir))
|
|
45451
46337
|
mkdirSync14(dir, { recursive: true });
|
|
45452
46338
|
const db = new Database(dbPath);
|
|
@@ -45474,8 +46360,8 @@ function saveFeedback(input2) {
|
|
|
45474
46360
|
throw new Error("Feedback message is required");
|
|
45475
46361
|
const category = input2.category ?? "general";
|
|
45476
46362
|
if (isApiMode()) {
|
|
45477
|
-
const path =
|
|
45478
|
-
const dir =
|
|
46363
|
+
const path = join39(getDataDir(), "feedback.jsonl");
|
|
46364
|
+
const dir = dirname25(path);
|
|
45479
46365
|
if (!existsSync17(dir))
|
|
45480
46366
|
mkdirSync14(dir, { recursive: true });
|
|
45481
46367
|
appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
@@ -45639,9 +46525,9 @@ var init_resource_meta_tools = __esm(() => {
|
|
|
45639
46525
|
|
|
45640
46526
|
// src/lib/scheduler.ts
|
|
45641
46527
|
import { existsSync as existsSync18, readFileSync as readFileSync22, writeFileSync as writeFileSync12, mkdirSync as mkdirSync15 } from "fs";
|
|
45642
|
-
import { join as
|
|
46528
|
+
import { join as join40 } from "path";
|
|
45643
46529
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
45644
|
-
return
|
|
46530
|
+
return join40(targetDir, ".skills", "schedules.json");
|
|
45645
46531
|
}
|
|
45646
46532
|
function loadSchedules(targetDir = process.cwd()) {
|
|
45647
46533
|
const path = getSchedulesPath(targetDir);
|
|
@@ -45654,7 +46540,7 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
45654
46540
|
}
|
|
45655
46541
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
45656
46542
|
const path = getSchedulesPath(targetDir);
|
|
45657
|
-
const dir =
|
|
46543
|
+
const dir = join40(targetDir, ".skills");
|
|
45658
46544
|
if (!existsSync18(dir))
|
|
45659
46545
|
mkdirSync15(dir, { recursive: true });
|
|
45660
46546
|
writeFileSync12(path, JSON.stringify(data, null, 2));
|
|
@@ -45926,16 +46812,16 @@ var init_schedule_tools = __esm(() => {
|
|
|
45926
46812
|
});
|
|
45927
46813
|
|
|
45928
46814
|
// src/lib/native-storage.ts
|
|
45929
|
-
import { createHash as
|
|
46815
|
+
import { createHash as createHash26, createHmac } from "crypto";
|
|
45930
46816
|
import {
|
|
45931
46817
|
existsSync as existsSync19,
|
|
45932
46818
|
mkdirSync as mkdirSync16,
|
|
45933
46819
|
readFileSync as readFileSync23,
|
|
45934
|
-
readdirSync as
|
|
46820
|
+
readdirSync as readdirSync13,
|
|
45935
46821
|
statSync as statSync12,
|
|
45936
46822
|
writeFileSync as writeFileSync13
|
|
45937
46823
|
} from "fs";
|
|
45938
|
-
import { dirname as
|
|
46824
|
+
import { dirname as dirname26, join as join41, normalize as normalize5, relative as relative10, sep as sep12 } from "path";
|
|
45939
46825
|
function resolveSkillsNativeStorageConfig(env = process.env) {
|
|
45940
46826
|
assertNoRetiredModeEnvVars(env, {
|
|
45941
46827
|
app: SKILLS_ENV_NAMESPACE,
|
|
@@ -45976,7 +46862,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
45976
46862
|
local: {
|
|
45977
46863
|
dataDir: getDataDir(),
|
|
45978
46864
|
projectStateDir: getProjectStateDir(targetDir),
|
|
45979
|
-
feedbackDbPath:
|
|
46865
|
+
feedbackDbPath: join41(getDataDir(), "skills.db")
|
|
45980
46866
|
},
|
|
45981
46867
|
remote: {
|
|
45982
46868
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -45999,11 +46885,11 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
45999
46885
|
if (existsSync19(projectStateDir)) {
|
|
46000
46886
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
46001
46887
|
const bytes = readFileSync23(filePath);
|
|
46002
|
-
const relativePath = toPosix(
|
|
46888
|
+
const relativePath = toPosix(relative10(targetDir, filePath));
|
|
46003
46889
|
files.push({
|
|
46004
46890
|
path: relativePath,
|
|
46005
46891
|
sizeBytes: bytes.byteLength,
|
|
46006
|
-
sha256:
|
|
46892
|
+
sha256: createHash26("sha256").update(bytes).digest("hex"),
|
|
46007
46893
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
46008
46894
|
});
|
|
46009
46895
|
}
|
|
@@ -46056,8 +46942,8 @@ function parsePositiveInteger(value) {
|
|
|
46056
46942
|
}
|
|
46057
46943
|
function walkFiles2(dir) {
|
|
46058
46944
|
const files = [];
|
|
46059
|
-
for (const entry of
|
|
46060
|
-
const full =
|
|
46945
|
+
for (const entry of readdirSync13(dir)) {
|
|
46946
|
+
const full = join41(dir, entry);
|
|
46061
46947
|
const stats = statSync12(full);
|
|
46062
46948
|
if (stats.isDirectory())
|
|
46063
46949
|
files.push(...walkFiles2(full));
|
|
@@ -46295,73 +47181,73 @@ var init_private_publication_customer = __esm(() => {
|
|
|
46295
47181
|
});
|
|
46296
47182
|
|
|
46297
47183
|
// src/lib/private-publication-recovery.ts
|
|
46298
|
-
import { constants as
|
|
46299
|
-
import { dirname as
|
|
47184
|
+
import { constants as constants18, closeSync as closeSync21, fsyncSync as fsyncSync6, fstatSync as fstatSync21, lstatSync as lstatSync25, mkdirSync as mkdirSync17, openSync as openSync21, readSync as readSync14, realpathSync as realpathSync14, renameSync as renameSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync14 } from "fs";
|
|
47185
|
+
import { dirname as dirname27, isAbsolute as isAbsolute24, join as join42, resolve as resolve25 } from "path";
|
|
46300
47186
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
46301
47187
|
function safeDirectory(directory2) {
|
|
46302
|
-
if (!
|
|
47188
|
+
if (!isAbsolute24(directory2) || directory2 !== resolve25(directory2) || realpathSync14(directory2) !== directory2)
|
|
46303
47189
|
return fail2();
|
|
46304
|
-
for (let path = directory2;; path =
|
|
46305
|
-
const stat3 =
|
|
47190
|
+
for (let path = directory2;; path = dirname27(path)) {
|
|
47191
|
+
const stat3 = lstatSync25(path);
|
|
46306
47192
|
if (!stat3.isDirectory() || stat3.isSymbolicLink())
|
|
46307
47193
|
return fail2();
|
|
46308
|
-
if (path ===
|
|
47194
|
+
if (path === dirname27(path))
|
|
46309
47195
|
break;
|
|
46310
47196
|
}
|
|
46311
|
-
const own2 =
|
|
47197
|
+
const own2 = lstatSync25(directory2);
|
|
46312
47198
|
if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
|
|
46313
47199
|
return fail2();
|
|
46314
47200
|
return { dev: own2.dev, ino: own2.ino };
|
|
46315
47201
|
}
|
|
46316
|
-
function unchangedDirectory(directory2,
|
|
47202
|
+
function unchangedDirectory(directory2, identity4) {
|
|
46317
47203
|
const now = safeDirectory(directory2);
|
|
46318
|
-
if (now.dev !==
|
|
47204
|
+
if (now.dev !== identity4.dev || now.ino !== identity4.ino)
|
|
46319
47205
|
return fail2();
|
|
46320
47206
|
}
|
|
46321
47207
|
function readOwned(directory2, name, max) {
|
|
46322
|
-
const
|
|
46323
|
-
const fd =
|
|
47208
|
+
const identity4 = safeDirectory(directory2), file2 = join42(directory2, name);
|
|
47209
|
+
const fd = openSync21(file2, constants18.O_RDONLY | constants18.O_NOFOLLOW);
|
|
46324
47210
|
try {
|
|
46325
|
-
const stat3 =
|
|
47211
|
+
const stat3 = fstatSync21(fd);
|
|
46326
47212
|
if (!stat3.isFile() || stat3.nlink !== 1 || stat3.size > max || stat3.size < 1 || (stat3.mode & 63) !== 0 || process.getuid && stat3.uid !== process.getuid())
|
|
46327
47213
|
return fail2();
|
|
46328
47214
|
const buffer = Buffer.alloc(Math.min(max, stat3.size) + 1);
|
|
46329
47215
|
let length = 0;
|
|
46330
47216
|
while (length < buffer.length) {
|
|
46331
|
-
const count =
|
|
47217
|
+
const count = readSync14(fd, buffer, length, buffer.length - length, length);
|
|
46332
47218
|
if (count === 0)
|
|
46333
47219
|
break;
|
|
46334
47220
|
length += count;
|
|
46335
47221
|
}
|
|
46336
|
-
const bytes = buffer.subarray(0, length), after =
|
|
46337
|
-
unchangedDirectory(directory2,
|
|
47222
|
+
const bytes = buffer.subarray(0, length), after = fstatSync21(fd);
|
|
47223
|
+
unchangedDirectory(directory2, identity4);
|
|
46338
47224
|
if (bytes.length !== stat3.size || stat3.size !== after.size || stat3.mtimeMs !== after.mtimeMs || stat3.ctimeMs !== after.ctimeMs)
|
|
46339
47225
|
return fail2();
|
|
46340
47226
|
return bytes;
|
|
46341
47227
|
} finally {
|
|
46342
|
-
|
|
47228
|
+
closeSync21(fd);
|
|
46343
47229
|
}
|
|
46344
47230
|
}
|
|
46345
47231
|
function writeOwned(directory2, name, bytes) {
|
|
46346
|
-
const fd =
|
|
47232
|
+
const fd = openSync21(join42(directory2, name), constants18.O_WRONLY | constants18.O_CREAT | constants18.O_EXCL | constants18.O_NOFOLLOW, 384);
|
|
46347
47233
|
try {
|
|
46348
47234
|
writeFileSync14(fd, bytes);
|
|
46349
47235
|
fsyncSync6(fd);
|
|
46350
47236
|
} finally {
|
|
46351
|
-
|
|
47237
|
+
closeSync21(fd);
|
|
46352
47238
|
}
|
|
46353
47239
|
}
|
|
46354
47240
|
function save(directory2, value) {
|
|
46355
|
-
const
|
|
47241
|
+
const identity4 = safeDirectory(directory2), temporary = `.receipt-${randomUUID3()}.json`;
|
|
46356
47242
|
writeOwned(directory2, temporary, JSON.stringify(value) + `
|
|
46357
47243
|
`);
|
|
46358
|
-
unchangedDirectory(directory2,
|
|
46359
|
-
renameSync7(
|
|
46360
|
-
const fd =
|
|
47244
|
+
unchangedDirectory(directory2, identity4);
|
|
47245
|
+
renameSync7(join42(directory2, temporary), join42(directory2, "receipt.json"));
|
|
47246
|
+
const fd = openSync21(directory2, constants18.O_RDONLY | constants18.O_NOFOLLOW);
|
|
46361
47247
|
try {
|
|
46362
47248
|
fsyncSync6(fd);
|
|
46363
47249
|
} finally {
|
|
46364
|
-
|
|
47250
|
+
closeSync21(fd);
|
|
46365
47251
|
}
|
|
46366
47252
|
}
|
|
46367
47253
|
function bind(client, receipt) {
|
|
@@ -46369,23 +47255,23 @@ function bind(client, receipt) {
|
|
|
46369
47255
|
throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
|
|
46370
47256
|
}
|
|
46371
47257
|
async function locked(directory2, action) {
|
|
46372
|
-
const
|
|
47258
|
+
const identity4 = safeDirectory(directory2), file2 = join42(directory2, "operation.lock");
|
|
46373
47259
|
let fd;
|
|
46374
47260
|
try {
|
|
46375
|
-
fd =
|
|
47261
|
+
fd = openSync21(file2, constants18.O_WRONLY | constants18.O_CREAT | constants18.O_EXCL | constants18.O_NOFOLLOW, 384);
|
|
46376
47262
|
} catch {
|
|
46377
47263
|
throw new PrivatePublicationError("PUBLICATION_RECOVERY_BUSY", "Another operation holds this recovery directory. If it crashed, confirm that process has stopped before explicitly removing operation.lock and resuming.");
|
|
46378
47264
|
}
|
|
46379
|
-
const lock =
|
|
47265
|
+
const lock = fstatSync21(fd);
|
|
46380
47266
|
try {
|
|
46381
47267
|
writeFileSync14(fd, JSON.stringify({ pid: process.pid }) + `
|
|
46382
47268
|
`);
|
|
46383
47269
|
fsyncSync6(fd);
|
|
46384
47270
|
return await action();
|
|
46385
47271
|
} finally {
|
|
46386
|
-
|
|
46387
|
-
unchangedDirectory(directory2,
|
|
46388
|
-
const now =
|
|
47272
|
+
closeSync21(fd);
|
|
47273
|
+
unchangedDirectory(directory2, identity4);
|
|
47274
|
+
const now = lstatSync25(file2);
|
|
46389
47275
|
if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
|
|
46390
47276
|
fail2();
|
|
46391
47277
|
unlinkSync6(file2);
|
|
@@ -46437,15 +47323,15 @@ async function preparePrivatePublication(client, sourceDirectory, recoveryDirect
|
|
|
46437
47323
|
phase: "prepared",
|
|
46438
47324
|
intent: null
|
|
46439
47325
|
};
|
|
46440
|
-
if (!
|
|
47326
|
+
if (!isAbsolute24(recoveryDirectory) || resolve25(recoveryDirectory) !== recoveryDirectory || realpathSync14(dirname27(recoveryDirectory)) !== dirname27(recoveryDirectory))
|
|
46441
47327
|
return fail2();
|
|
46442
47328
|
mkdirSync17(recoveryDirectory, { mode: 448 });
|
|
46443
47329
|
safeDirectory(recoveryDirectory);
|
|
46444
|
-
const parent =
|
|
47330
|
+
const parent = openSync21(dirname27(recoveryDirectory), constants18.O_RDONLY | constants18.O_NOFOLLOW);
|
|
46445
47331
|
try {
|
|
46446
47332
|
fsyncSync6(parent);
|
|
46447
47333
|
} finally {
|
|
46448
|
-
|
|
47334
|
+
closeSync21(parent);
|
|
46449
47335
|
}
|
|
46450
47336
|
writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
|
|
46451
47337
|
save(recoveryDirectory, receipt);
|
|
@@ -46538,10 +47424,10 @@ var init_private_publication_recovery = __esm(() => {
|
|
|
46538
47424
|
});
|
|
46539
47425
|
|
|
46540
47426
|
// src/mcp/private-publication-tools.ts
|
|
46541
|
-
import { isAbsolute as
|
|
47427
|
+
import { isAbsolute as isAbsolute25 } from "path";
|
|
46542
47428
|
function registerPrivatePublicationTools(server) {
|
|
46543
47429
|
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}$/);
|
|
46544
|
-
const directory2 = exports_external.string().max(4096).refine(
|
|
47430
|
+
const directory2 = exports_external.string().max(4096).refine(isAbsolute25, "Use an absolute local directory");
|
|
46545
47431
|
const verification2 = { email: exports_external.string().email().max(254), code: exports_external.string().regex(/^\d{6}$/), userId: uuid9, membershipId: uuid9, recoveryDirectory: directory2 };
|
|
46546
47432
|
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.";
|
|
46547
47433
|
const handler = (action) => async (value) => {
|
|
@@ -46974,19 +47860,19 @@ async function connectMcpForNode() {
|
|
|
46974
47860
|
return { server: server2, transport };
|
|
46975
47861
|
}
|
|
46976
47862
|
function readBody(req) {
|
|
46977
|
-
return new Promise((
|
|
47863
|
+
return new Promise((resolve26, reject) => {
|
|
46978
47864
|
const chunks = [];
|
|
46979
47865
|
req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
46980
47866
|
req.on("end", () => {
|
|
46981
47867
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
46982
47868
|
if (!raw.trim()) {
|
|
46983
|
-
|
|
47869
|
+
resolve26(undefined);
|
|
46984
47870
|
return;
|
|
46985
47871
|
}
|
|
46986
47872
|
try {
|
|
46987
|
-
|
|
47873
|
+
resolve26(JSON.parse(raw));
|
|
46988
47874
|
} catch {
|
|
46989
|
-
|
|
47875
|
+
resolve26(undefined);
|
|
46990
47876
|
}
|
|
46991
47877
|
});
|
|
46992
47878
|
req.on("error", reject);
|
|
@@ -47028,9 +47914,9 @@ async function startSkillsMcpHttpServer(options = {}) {
|
|
|
47028
47914
|
}
|
|
47029
47915
|
}
|
|
47030
47916
|
});
|
|
47031
|
-
await new Promise((
|
|
47917
|
+
await new Promise((resolve26, reject) => {
|
|
47032
47918
|
httpServer.once("error", reject);
|
|
47033
|
-
httpServer.listen(port, hostname4, () =>
|
|
47919
|
+
httpServer.listen(port, hostname4, () => resolve26());
|
|
47034
47920
|
});
|
|
47035
47921
|
const address = httpServer.address();
|
|
47036
47922
|
const listenPort = typeof address === "object" && address ? address.port : port;
|