@hasna/skills 0.10.19 → 0.10.22
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 +48 -12
- package/bin/index.js +761 -443
- package/bin/mcp.js +308 -119
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +520 -207
- package/dist/lib/agent-discovery.d.ts +4 -1
- package/dist/lib/agent-integration.d.ts +4 -0
- package/dist/lib/codex-hook-rpc.d.ts +1 -1
- package/dist/lib/codex-native-compatibility.d.ts +3 -0
- package/dist/lib/codex-native-skill-catalog.d.ts +20 -0
- package/dist/lib/codex-plugin-skill-controls.d.ts +2 -1
- package/dist/lib/codex-settings-witness-v1.d.ts +12 -0
- package/dist/lib/codex-settings-witness.d.ts +13 -5
- package/dist/sdk/index.js +594 -284
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.10.
|
|
36871
|
+
version: "0.10.22",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -39931,12 +39931,14 @@ function assertAgentPolicyCollections(policy) {
|
|
|
39931
39931
|
}
|
|
39932
39932
|
if (bridge.codexPluginSkills !== undefined)
|
|
39933
39933
|
for (const control of array(bridge.codexPluginSkills, 4096)) {
|
|
39934
|
-
requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256"].includes(key)));
|
|
39934
|
+
requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "remotePluginId"].includes(key)));
|
|
39935
39935
|
text(control.name, 129);
|
|
39936
39936
|
text(control.pluginId, 1024);
|
|
39937
39937
|
text(control.namespace, 64);
|
|
39938
39938
|
text(control.pluginParent);
|
|
39939
39939
|
requireBound(isAbsolute2(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
|
|
39940
|
+
if (control.remotePluginId !== undefined)
|
|
39941
|
+
requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
|
|
39940
39942
|
if (control.appSha256 !== undefined)
|
|
39941
39943
|
requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
|
|
39942
39944
|
}
|
|
@@ -39959,8 +39961,8 @@ function assertAgentPolicyCollections(policy) {
|
|
|
39959
39961
|
text(source.path);
|
|
39960
39962
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
39961
39963
|
if (source.hashMode !== undefined)
|
|
39962
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
39963
|
-
if (source.hashMode === "codex-settings-v1")
|
|
39964
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
39965
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2")
|
|
39964
39966
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve3(source.path) === source.path && basename2(source.path) === "config.toml");
|
|
39965
39967
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
39966
39968
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve3(source.path) === source.path && basename2(source.path) === "settings.json");
|
|
@@ -50884,6 +50886,28 @@ var init_claude_settings_witness = __esm(() => {
|
|
|
50884
50886
|
PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
|
|
50885
50887
|
});
|
|
50886
50888
|
|
|
50889
|
+
// src/lib/codex-native-compatibility.ts
|
|
50890
|
+
function supportsCodexNativeCapability(version, capability) {
|
|
50891
|
+
return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
|
|
50892
|
+
}
|
|
50893
|
+
var MEASURED_CODEX_CAPABILITIES, SUPPORTED_CODEX_HOOK_VERSIONS;
|
|
50894
|
+
var init_codex_native_compatibility = __esm(() => {
|
|
50895
|
+
MEASURED_CODEX_CAPABILITIES = {
|
|
50896
|
+
"codex-cli 0.153.0": ["hooks"],
|
|
50897
|
+
"codex-cli 0.154.0": ["hooks"],
|
|
50898
|
+
"codex-cli 0.155.0": ["hooks"],
|
|
50899
|
+
"codex-cli 0.155.1": ["hooks"],
|
|
50900
|
+
"codex-cli 0.156.1": ["hooks"],
|
|
50901
|
+
"codex-cli 0.157.0": ["hooks"],
|
|
50902
|
+
"codex-cli 0.157.1": ["hooks"],
|
|
50903
|
+
"codex-cli 0.158.0": ["hooks"],
|
|
50904
|
+
"codex-cli 0.159.0": ["hooks"],
|
|
50905
|
+
"codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
|
|
50906
|
+
"codex-cli 0.160.0": ["hooks", "qualified-skill-catalog"]
|
|
50907
|
+
};
|
|
50908
|
+
SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
|
|
50909
|
+
});
|
|
50910
|
+
|
|
50887
50911
|
// src/lib/codex-hook-rpc.ts
|
|
50888
50912
|
import { execFile, spawn as spawn2 } from "child_process";
|
|
50889
50913
|
import { realpathSync as realpathSync3, statSync as statSync7 } from "fs";
|
|
@@ -50906,7 +50930,7 @@ async function connectCodexHookRpc(options) {
|
|
|
50906
50930
|
} catch {
|
|
50907
50931
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE");
|
|
50908
50932
|
}
|
|
50909
|
-
if (!
|
|
50933
|
+
if (!supportsCodexNativeCapability(version, "hooks"))
|
|
50910
50934
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED_VERSION");
|
|
50911
50935
|
const current = statSync7(binary);
|
|
50912
50936
|
if (realpathSync3(found) !== binary || ["dev", "ino", "size", "mtimeMs", "ctimeMs"].some((k) => stat[k] !== current[k]))
|
|
@@ -51025,9 +51049,9 @@ async function connectCodexHookRpc(options) {
|
|
|
51025
51049
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED: native hooks/list and versioned config writes are required");
|
|
51026
51050
|
}
|
|
51027
51051
|
}
|
|
51028
|
-
var SUPPORTED_CODEX_HOOK_VERSIONS;
|
|
51029
51052
|
var init_codex_hook_rpc = __esm(() => {
|
|
51030
|
-
|
|
51053
|
+
init_codex_native_compatibility();
|
|
51054
|
+
init_codex_native_compatibility();
|
|
51031
51055
|
});
|
|
51032
51056
|
|
|
51033
51057
|
// src/lib/codex-native-skill-catalog.ts
|
|
@@ -51052,6 +51076,47 @@ function projectCodexNativeSkillCatalog(response, cwd2) {
|
|
|
51052
51076
|
return { name: value.name, path: value.path, enabled: value.enabled, pluginId: value.pluginId ?? null };
|
|
51053
51077
|
});
|
|
51054
51078
|
}
|
|
51079
|
+
function projectCodexInstalledPlugins(response) {
|
|
51080
|
+
if (!object4(response) || !Array.isArray(response.marketplaces) || !Array.isArray(response.marketplaceLoadErrors) || response.marketplaceLoadErrors.length !== 0 || response.marketplaces.length > MAX_SKILLS)
|
|
51081
|
+
refuse();
|
|
51082
|
+
const plugins = [];
|
|
51083
|
+
for (const marketplace of response.marketplaces) {
|
|
51084
|
+
if (!object4(marketplace) || !scalar(marketplace.name, MAX_NAME_BYTES) || !Array.isArray(marketplace.plugins) || marketplace.plugins.length > MAX_SKILLS)
|
|
51085
|
+
refuse();
|
|
51086
|
+
for (const value of marketplace.plugins) {
|
|
51087
|
+
if (!object4(value) || !scalar(value.id, MAX_PLUGIN_ID_BYTES) || !scalar(value.name, MAX_NAME_BYTES) || value.id !== `${value.name}@${marketplace.name}`)
|
|
51088
|
+
refuse();
|
|
51089
|
+
plugins.push({
|
|
51090
|
+
...value,
|
|
51091
|
+
marketplace: marketplace.name,
|
|
51092
|
+
sourceType: object4(value.source) && value.source.type === "remote" ? "remote" : undefined
|
|
51093
|
+
});
|
|
51094
|
+
}
|
|
51095
|
+
}
|
|
51096
|
+
return projectCodexInstalledPluginEntries(plugins);
|
|
51097
|
+
}
|
|
51098
|
+
function projectCodexInstalledPluginEntries(value) {
|
|
51099
|
+
if (!Array.isArray(value) || value.length > MAX_SKILLS)
|
|
51100
|
+
refuse();
|
|
51101
|
+
const ids = new Set;
|
|
51102
|
+
return value.map((item) => {
|
|
51103
|
+
if (!object4(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && item.sourceType !== "remote")
|
|
51104
|
+
refuse();
|
|
51105
|
+
const at2 = item.id.lastIndexOf("@");
|
|
51106
|
+
if (at2 < 1 || item.id.slice(0, at2) !== item.name || at2 === item.id.length - 1 || ids.has(item.id))
|
|
51107
|
+
refuse();
|
|
51108
|
+
ids.add(item.id);
|
|
51109
|
+
return {
|
|
51110
|
+
id: item.id,
|
|
51111
|
+
name: item.name,
|
|
51112
|
+
installed: item.installed,
|
|
51113
|
+
enabled: item.enabled,
|
|
51114
|
+
localVersion: item.localVersion ?? null,
|
|
51115
|
+
...item.sourceType === "remote" ? { sourceType: "remote" } : {},
|
|
51116
|
+
...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
|
|
51117
|
+
};
|
|
51118
|
+
});
|
|
51119
|
+
}
|
|
51055
51120
|
async function captureCodexNativeSkillCatalog(options, connect = connectCodexHookRpc) {
|
|
51056
51121
|
if (!absolutePath2(options.cwd) || !absolutePath2(options.home))
|
|
51057
51122
|
refuse();
|
|
@@ -51062,10 +51127,16 @@ async function captureCodexNativeSkillCatalog(options, connect = connectCodexHoo
|
|
|
51062
51127
|
...options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }
|
|
51063
51128
|
});
|
|
51064
51129
|
try {
|
|
51065
|
-
if (!
|
|
51130
|
+
if (!supportsCodexNativeCapability(rpc.version, "qualified-skill-catalog"))
|
|
51066
51131
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_UNSUPPORTED_VERSION");
|
|
51067
51132
|
const response = await rpc.request("skills/list", { cwds: [options.cwd], forceReload: true });
|
|
51068
|
-
|
|
51133
|
+
const installed = await rpc.request("plugin/installed", { cwds: [options.cwd] });
|
|
51134
|
+
return {
|
|
51135
|
+
version: rpc.version,
|
|
51136
|
+
cwd: options.cwd,
|
|
51137
|
+
skills: projectCodexNativeSkillCatalog(response, options.cwd),
|
|
51138
|
+
plugins: projectCodexInstalledPlugins(installed)
|
|
51139
|
+
};
|
|
51069
51140
|
} finally {
|
|
51070
51141
|
await rpc.close();
|
|
51071
51142
|
}
|
|
@@ -51107,18 +51178,33 @@ function isCodexNativeSkillDisabled(skill, rules) {
|
|
|
51107
51178
|
}
|
|
51108
51179
|
return disabled;
|
|
51109
51180
|
}
|
|
51110
|
-
var MAX_SKILLS = 4096, MAX_NAME_BYTES = 1024, MAX_PATH_BYTES = 16384,
|
|
51181
|
+
var MAX_SKILLS = 4096, MAX_NAME_BYTES = 1024, MAX_PATH_BYTES = 16384, MAX_PLUGIN_ID_BYTES = 1024, object4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), scalar = (value, max2) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max2 && !/[\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);
|
|
51111
51182
|
var init_codex_native_skill_catalog = __esm(() => {
|
|
51112
51183
|
init_codex_hook_rpc();
|
|
51113
|
-
|
|
51184
|
+
init_codex_native_compatibility();
|
|
51114
51185
|
});
|
|
51115
51186
|
|
|
51116
51187
|
// src/lib/codex-plugin-skill-controls.ts
|
|
51117
|
-
import { dirname as dirname10, join as join18, resolve as resolve10, sep as sep4 } from "path";
|
|
51188
|
+
import { basename as basename5, dirname as dirname10, join as join18, relative as relative4, resolve as resolve10, sep as sep4 } from "path";
|
|
51118
51189
|
import { lstatSync as lstatSync12, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "fs";
|
|
51119
51190
|
function refuse2() {
|
|
51120
51191
|
throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
|
|
51121
51192
|
}
|
|
51193
|
+
function remoteInstallationMatches(parent, remotePluginId, read2) {
|
|
51194
|
+
try {
|
|
51195
|
+
if (!remotePluginIdentifier(remotePluginId) || !lstatSync12(parent).isDirectory() || realpathSync5(parent) !== parent)
|
|
51196
|
+
return false;
|
|
51197
|
+
const path = join18(parent, ".codex-remote-plugin-install.json"), stat = lstatSync12(path, { throwIfNoEntry: false });
|
|
51198
|
+
if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
|
|
51199
|
+
return false;
|
|
51200
|
+
const text2 = read2(path);
|
|
51201
|
+
hashNativeJsonControls(text2);
|
|
51202
|
+
const value = JSON.parse(text2);
|
|
51203
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 2 && value.schema_version === 1 && value.remote_plugin_id === remotePluginId);
|
|
51204
|
+
} catch {
|
|
51205
|
+
return false;
|
|
51206
|
+
}
|
|
51207
|
+
}
|
|
51122
51208
|
function appControls(path, read2) {
|
|
51123
51209
|
const text2 = read2(path);
|
|
51124
51210
|
let value, sha2562;
|
|
@@ -51154,6 +51240,9 @@ function rootControls(root, read2) {
|
|
|
51154
51240
|
}
|
|
51155
51241
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
51156
51242
|
refuse2();
|
|
51243
|
+
const cacheVersion = basename5(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
51244
|
+
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
51245
|
+
refuse2();
|
|
51157
51246
|
if (manifest.hooks !== undefined || manifest.mcpServers !== undefined || manifest.commands !== undefined || manifest.apps !== undefined && manifest.apps !== "./.app.json" || ["hooks", ".mcp.json"].some((path) => lstatSync12(join18(root, path), { throwIfNoEntry: false })))
|
|
51158
51247
|
refuse2();
|
|
51159
51248
|
const app = join18(root, ".app.json"), present = lstatSync12(app, { throwIfNoEntry: false });
|
|
@@ -51205,6 +51294,10 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
|
|
|
51205
51294
|
for (const parent of new Set(controls.map((control) => control.pluginParent))) {
|
|
51206
51295
|
if (!parent.startsWith(cache3 + sep4) || !lstatSync12(parent, { throwIfNoEntry: false })?.isDirectory())
|
|
51207
51296
|
return false;
|
|
51297
|
+
for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
|
|
51298
|
+
if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read2))
|
|
51299
|
+
return false;
|
|
51300
|
+
}
|
|
51208
51301
|
const names = readdirSync6(parent);
|
|
51209
51302
|
entries += names.length;
|
|
51210
51303
|
if (entries > 4096)
|
|
@@ -51230,22 +51323,56 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
|
|
|
51230
51323
|
return false;
|
|
51231
51324
|
}
|
|
51232
51325
|
}
|
|
51233
|
-
function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2) {
|
|
51234
|
-
if (catalog?.version
|
|
51326
|
+
function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2, rules) {
|
|
51327
|
+
if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd2)
|
|
51235
51328
|
refuse2();
|
|
51236
51329
|
const skills = projectCodexNativeSkillCatalog({ data: [{ cwd: cwd2, errors: [], skills: catalog.skills }] }, cwd2);
|
|
51237
51330
|
const allowed = new Set(documents), result2 = [];
|
|
51238
|
-
|
|
51239
|
-
|
|
51331
|
+
let installedPlugins;
|
|
51332
|
+
try {
|
|
51333
|
+
installedPlugins = projectCodexInstalledPluginEntries(catalog.plugins ?? []);
|
|
51334
|
+
} catch {
|
|
51335
|
+
refuse2();
|
|
51336
|
+
}
|
|
51337
|
+
const candidates = catalog.plugins === undefined ? skills.map((skill) => skill.path).filter((path) => allowed.has(path)) : allowed;
|
|
51338
|
+
for (const document3 of candidates) {
|
|
51339
|
+
if (!document3.startsWith(cache3 + sep4))
|
|
51240
51340
|
continue;
|
|
51241
|
-
|
|
51341
|
+
const parsed = identity4(document3, cache3, read2);
|
|
51342
|
+
if (parsed.name === "skills-cli" || parsed.name.endsWith(":skills-cli"))
|
|
51242
51343
|
refuse2();
|
|
51243
|
-
|
|
51344
|
+
const cacheParts = relative4(cache3, parsed.pluginParent).split(sep4);
|
|
51345
|
+
if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier2(part)))
|
|
51244
51346
|
refuse2();
|
|
51245
|
-
const
|
|
51246
|
-
if (
|
|
51347
|
+
const [marketplace, pluginName] = cacheParts;
|
|
51348
|
+
if (pluginName !== parsed.namespace)
|
|
51247
51349
|
refuse2();
|
|
51248
|
-
|
|
51350
|
+
const expectedPluginId = `${pluginName}@${marketplace}`;
|
|
51351
|
+
const listed = skills.filter((skill) => skill.path === document3);
|
|
51352
|
+
if (listed.length > 1)
|
|
51353
|
+
refuse2();
|
|
51354
|
+
let remotePluginId;
|
|
51355
|
+
if (listed.length === 1) {
|
|
51356
|
+
if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
|
|
51357
|
+
refuse2();
|
|
51358
|
+
} else {
|
|
51359
|
+
const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
|
|
51360
|
+
if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
|
|
51361
|
+
refuse2();
|
|
51362
|
+
const plugin = installed[0];
|
|
51363
|
+
if (plugin.localVersion !== null) {
|
|
51364
|
+
if (plugin.localVersion !== basename5(dirname10(dirname10(dirname10(document3)))))
|
|
51365
|
+
continue;
|
|
51366
|
+
} else {
|
|
51367
|
+
if (plugin.sourceType !== "remote" || !plugin.remotePluginId || !remoteInstallationMatches(parsed.pluginParent, plugin.remotePluginId, read2) || !lstatSync12(document3).isFile() || realpathSync5(document3) !== document3)
|
|
51368
|
+
refuse2();
|
|
51369
|
+
remotePluginId = plugin.remotePluginId;
|
|
51370
|
+
}
|
|
51371
|
+
const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document3 && rule?.enabled === false);
|
|
51372
|
+
if (!exactPathDeny || !isCodexNativeSkillDisabled({ name: parsed.name, path: document3, pluginId: expectedPluginId, enabled: true }, rules))
|
|
51373
|
+
refuse2();
|
|
51374
|
+
}
|
|
51375
|
+
result2.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
|
|
51249
51376
|
}
|
|
51250
51377
|
if (!result2.length || new Set(result2.map((item) => item.name)).size !== result2.length)
|
|
51251
51378
|
refuse2();
|
|
@@ -51261,7 +51388,7 @@ function isReviewedCodexPluginSkillDisabled(document3, cache3, controls, rules,
|
|
|
51261
51388
|
return false;
|
|
51262
51389
|
}
|
|
51263
51390
|
const reviewed = controls.find((item) => item.name === parsed.name && item.namespace === parsed.namespace && item.pluginParent === parsed.pluginParent && item.manifestSha256 === parsed.manifestSha256 && item.appSha256 === parsed.appSha256);
|
|
51264
|
-
if (!reviewed)
|
|
51391
|
+
if (!reviewed || reviewed.remotePluginId !== undefined && !remoteInstallationMatches(reviewed.pluginParent, reviewed.remotePluginId, read2))
|
|
51265
51392
|
return false;
|
|
51266
51393
|
return isCodexNativeSkillDisabled({ name: parsed.name, path: document3, pluginId: reviewed.pluginId, enabled: true }, rules);
|
|
51267
51394
|
}
|
|
@@ -51279,13 +51406,14 @@ enabled = false
|
|
|
51279
51406
|
}
|
|
51280
51407
|
return text2;
|
|
51281
51408
|
}
|
|
51282
|
-
var identifier2 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v);
|
|
51409
|
+
var identifier2 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v), CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
|
|
51283
51410
|
var init_codex_plugin_skill_controls = __esm(() => {
|
|
51284
51411
|
init_claude_settings_witness();
|
|
51412
|
+
init_codex_native_compatibility();
|
|
51285
51413
|
init_codex_native_skill_catalog();
|
|
51286
51414
|
});
|
|
51287
51415
|
|
|
51288
|
-
// src/lib/codex-settings-witness.ts
|
|
51416
|
+
// src/lib/codex-settings-witness-v1.ts
|
|
51289
51417
|
import { createHash as createHash9 } from "crypto";
|
|
51290
51418
|
function need3(value) {
|
|
51291
51419
|
if (!value)
|
|
@@ -51376,21 +51504,173 @@ function captureCodexSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
|
51376
51504
|
const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
51377
51505
|
return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
|
|
51378
51506
|
}
|
|
51507
|
+
var sha = (text2) => createHash9("sha256").update(text2).digest("hex"), EFFORT, VERBOSITY;
|
|
51508
|
+
var init_codex_settings_witness_v1 = __esm(() => {
|
|
51509
|
+
init_claude_settings_witness();
|
|
51510
|
+
EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
51511
|
+
VERBOSITY = new Set(["low", "medium", "high"]);
|
|
51512
|
+
});
|
|
51513
|
+
|
|
51514
|
+
// src/lib/codex-settings-witness.ts
|
|
51515
|
+
import { createHash as createHash10 } from "crypto";
|
|
51516
|
+
function need4(value) {
|
|
51517
|
+
if (!value)
|
|
51518
|
+
throw new Error("Invalid Codex settings witness");
|
|
51519
|
+
}
|
|
51520
|
+
function isRecord3(value) {
|
|
51521
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
51522
|
+
}
|
|
51523
|
+
function isDate2(value) {
|
|
51524
|
+
return value instanceof Date;
|
|
51525
|
+
}
|
|
51526
|
+
function numericTokens2(text2) {
|
|
51527
|
+
const result2 = [];
|
|
51528
|
+
for (let at2 = 0;at2 < text2.length; ) {
|
|
51529
|
+
const char = text2[at2];
|
|
51530
|
+
if (char === "#") {
|
|
51531
|
+
while (at2 < text2.length && text2[at2] !== `
|
|
51532
|
+
`)
|
|
51533
|
+
at2++;
|
|
51534
|
+
continue;
|
|
51535
|
+
}
|
|
51536
|
+
if (char === '"' || char === "'") {
|
|
51537
|
+
const quote = char, triple = text2.slice(at2, at2 + 3) === quote.repeat(3);
|
|
51538
|
+
at2 += triple ? 3 : 1;
|
|
51539
|
+
while (at2 < text2.length) {
|
|
51540
|
+
if (quote === '"' && text2[at2] === "\\") {
|
|
51541
|
+
at2 += 2;
|
|
51542
|
+
continue;
|
|
51543
|
+
}
|
|
51544
|
+
if (text2.slice(at2, at2 + (triple ? 3 : 1)) === quote.repeat(triple ? 3 : 1)) {
|
|
51545
|
+
at2 += triple ? 3 : 1;
|
|
51546
|
+
break;
|
|
51547
|
+
}
|
|
51548
|
+
at2++;
|
|
51549
|
+
}
|
|
51550
|
+
continue;
|
|
51551
|
+
}
|
|
51552
|
+
if (/[0-9+-]/.test(char)) {
|
|
51553
|
+
const start = at2;
|
|
51554
|
+
while (at2 < text2.length && !/[\s,\]{}#=]/.test(text2[at2]))
|
|
51555
|
+
at2++;
|
|
51556
|
+
if (at2 > start) {
|
|
51557
|
+
result2.push(text2.slice(start, at2));
|
|
51558
|
+
continue;
|
|
51559
|
+
}
|
|
51560
|
+
}
|
|
51561
|
+
at2++;
|
|
51562
|
+
}
|
|
51563
|
+
return result2.sort();
|
|
51564
|
+
}
|
|
51565
|
+
function canonical4(value, depth = 0, budget = { remaining: 65536 }) {
|
|
51566
|
+
need4(depth <= 32 && --budget.remaining >= 0);
|
|
51567
|
+
if (value === null)
|
|
51568
|
+
return ["null"];
|
|
51569
|
+
if (typeof value === "string")
|
|
51570
|
+
return ["string", value];
|
|
51571
|
+
if (typeof value === "boolean")
|
|
51572
|
+
return ["boolean", value];
|
|
51573
|
+
if (typeof value === "number") {
|
|
51574
|
+
need4(Number.isFinite(value));
|
|
51575
|
+
return ["number", String(value)];
|
|
51576
|
+
}
|
|
51577
|
+
if (isDate2(value)) {
|
|
51578
|
+
need4(Number.isFinite(value.getTime()));
|
|
51579
|
+
return ["date", value.toISOString()];
|
|
51580
|
+
}
|
|
51581
|
+
if (Array.isArray(value))
|
|
51582
|
+
return ["array", value.map((child) => canonical4(child, depth + 1, budget))];
|
|
51583
|
+
need4(isRecord3(value));
|
|
51584
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget)])];
|
|
51585
|
+
}
|
|
51586
|
+
function normalize3(config) {
|
|
51587
|
+
if (isRecord3(config.hooks)) {
|
|
51588
|
+
delete config.hooks.state;
|
|
51589
|
+
if (!Object.keys(config.hooks).length)
|
|
51590
|
+
delete config.hooks;
|
|
51591
|
+
}
|
|
51592
|
+
if (isRecord3(config.skills) && Array.isArray(config.skills.config)) {
|
|
51593
|
+
const skills = config.skills;
|
|
51594
|
+
const retained = skills.config.filter((entry) => !disablesOneSkill(entry));
|
|
51595
|
+
if (retained.length !== skills.config.length) {
|
|
51596
|
+
if (retained.length)
|
|
51597
|
+
skills.config = retained;
|
|
51598
|
+
else
|
|
51599
|
+
delete skills.config;
|
|
51600
|
+
}
|
|
51601
|
+
if (!Object.keys(skills).length)
|
|
51602
|
+
delete config.skills;
|
|
51603
|
+
}
|
|
51604
|
+
inference(config);
|
|
51605
|
+
if (isRecord3(config.profiles)) {
|
|
51606
|
+
for (const profile of Object.values(config.profiles))
|
|
51607
|
+
if (isRecord3(profile))
|
|
51608
|
+
inference(profile);
|
|
51609
|
+
}
|
|
51610
|
+
}
|
|
51611
|
+
function disablesOneSkill(entry) {
|
|
51612
|
+
if (!isRecord3(entry) || entry.enabled !== false)
|
|
51613
|
+
return false;
|
|
51614
|
+
const keys2 = Object.keys(entry);
|
|
51615
|
+
if (!keys2.every((key) => key === "path" || key === "name" || key === "enabled"))
|
|
51616
|
+
return false;
|
|
51617
|
+
const selector = ["path", "name"].filter((key) => Object.hasOwn(entry, key));
|
|
51618
|
+
return selector.length === 1 && typeof entry[selector[0]] === "string" && entry[selector[0]].length > 0;
|
|
51619
|
+
}
|
|
51620
|
+
function inference(target) {
|
|
51621
|
+
for (const key of INFERENCE)
|
|
51622
|
+
if (Object.hasOwn(target, key)) {
|
|
51623
|
+
const value = target[key];
|
|
51624
|
+
if (key === "model")
|
|
51625
|
+
need4(typeof value === "string" && MODEL.test(value));
|
|
51626
|
+
else
|
|
51627
|
+
need4(typeof value === "string" && (key === "model_reasoning_effort" ? EFFORT2.has(value) : VERBOSITY2.has(value)));
|
|
51628
|
+
delete target[key];
|
|
51629
|
+
}
|
|
51630
|
+
}
|
|
51631
|
+
function settingsDigest2(text2) {
|
|
51632
|
+
let config;
|
|
51633
|
+
try {
|
|
51634
|
+
config = Bun.TOML.parse(text2);
|
|
51635
|
+
} catch {
|
|
51636
|
+
throw new Error("Invalid Codex settings witness");
|
|
51637
|
+
}
|
|
51638
|
+
need4(isRecord3(config));
|
|
51639
|
+
normalize3(config);
|
|
51640
|
+
return sha2(`hasna.skills.codex-settings.v2\x00${JSON.stringify([canonical4(config), numericTokens2(text2)])}`);
|
|
51641
|
+
}
|
|
51642
|
+
function hashCodexSettingsReplacementV2(text2, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
51643
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
|
|
51644
|
+
budget.remaining -= Buffer.byteLength(text2);
|
|
51645
|
+
return settingsDigest2(text2);
|
|
51646
|
+
}
|
|
51647
|
+
function captureCodexSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
51648
|
+
const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
51649
|
+
return { path, hashMode: "codex-settings-v2", sha256: settingsDigest2(text2) };
|
|
51650
|
+
}
|
|
51651
|
+
function readCodexSettingsPreimage(path, budget = { remaining: 2 * 1024 * 1024 }) {
|
|
51652
|
+
return readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
51653
|
+
}
|
|
51379
51654
|
function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
|
|
51380
|
-
|
|
51655
|
+
need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "codex-settings-v1") && /^[a-f0-9]{64}$/.test(previous.sha256));
|
|
51381
51656
|
const budget = { remaining: 2 * 1024 * 1024 };
|
|
51382
51657
|
const before2 = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
|
|
51383
|
-
|
|
51384
|
-
const sha2562 =
|
|
51385
|
-
if (
|
|
51658
|
+
need4((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before2) : sha2(before2)) === previous.sha256);
|
|
51659
|
+
const sha2562 = settingsDigest2(current);
|
|
51660
|
+
if (settingsDigest2(before2) !== sha2562)
|
|
51386
51661
|
throw new Error("Codex non-preference settings changed; explicit discovery review required");
|
|
51387
|
-
return { path: previous.path, hashMode: "codex-settings-
|
|
51662
|
+
return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
|
|
51388
51663
|
}
|
|
51389
|
-
var
|
|
51664
|
+
var sha2 = (text2) => createHash10("sha256").update(text2).digest("hex"), EFFORT2, VERBOSITY2, MODEL, CODEX_DISCOVERY_PROJECTION_FIELDS, INFERENCE;
|
|
51390
51665
|
var init_codex_settings_witness = __esm(() => {
|
|
51666
|
+
init_codex_settings_witness_v1();
|
|
51667
|
+
init_codex_settings_witness_v1();
|
|
51391
51668
|
init_claude_settings_witness();
|
|
51392
|
-
|
|
51393
|
-
|
|
51669
|
+
EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
51670
|
+
VERBOSITY2 = new Set(["low", "medium", "high"]);
|
|
51671
|
+
MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
51672
|
+
CODEX_DISCOVERY_PROJECTION_FIELDS = ["plugins", "marketplaces", "skills"];
|
|
51673
|
+
INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
|
|
51394
51674
|
});
|
|
51395
51675
|
|
|
51396
51676
|
// src/lib/managed-policy.ts
|
|
@@ -51479,7 +51759,7 @@ var init_managed_policy = __esm(() => {
|
|
|
51479
51759
|
});
|
|
51480
51760
|
|
|
51481
51761
|
// src/lib/agent-bridge.ts
|
|
51482
|
-
import { createHash as
|
|
51762
|
+
import { createHash as createHash11 } from "crypto";
|
|
51483
51763
|
import { lstatSync as lstatSync14, readFileSync as readFileSync12, readdirSync as readdirSync7 } from "fs";
|
|
51484
51764
|
import { join as join20 } from "path";
|
|
51485
51765
|
function isOwnedCliBridge(path, expectedPaths) {
|
|
@@ -51513,7 +51793,7 @@ Use the Skills CLI as the skill authority for this station.
|
|
|
51513
51793
|
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.
|
|
51514
51794
|
`, CLI_BRIDGE_DIGEST, CLI_BRIDGE_FILES;
|
|
51515
51795
|
var init_agent_bridge = __esm(() => {
|
|
51516
|
-
CLI_BRIDGE_DIGEST =
|
|
51796
|
+
CLI_BRIDGE_DIGEST = createHash11("sha256").update(markdown).digest("hex");
|
|
51517
51797
|
CLI_BRIDGE_FILES = Object.freeze({
|
|
51518
51798
|
"SKILL.md": markdown,
|
|
51519
51799
|
".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
|
|
@@ -51792,9 +52072,10 @@ var init_agent_codex = __esm(() => {
|
|
|
51792
52072
|
});
|
|
51793
52073
|
|
|
51794
52074
|
// src/lib/agent-integration.ts
|
|
51795
|
-
import { createHash as
|
|
52075
|
+
import { createHash as createHash12, randomUUID as randomUUID5 } from "crypto";
|
|
52076
|
+
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
51796
52077
|
import { existsSync as existsSync12, lstatSync as lstatSync15, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync13, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync6, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync8, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, readSync as readSync6, constants as constants8, linkSync } from "fs";
|
|
51797
|
-
import { dirname as dirname11, join as join21, resolve as resolve11, relative as
|
|
52078
|
+
import { dirname as dirname11, join as join21, resolve as resolve11, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
|
|
51798
52079
|
import { homedir as homedir3 } from "os";
|
|
51799
52080
|
function assertSafePath(path) {
|
|
51800
52081
|
let cursor = resolve11(path);
|
|
@@ -51818,7 +52099,7 @@ function readRootAlias(home, agent) {
|
|
|
51818
52099
|
const targetStat = lstatSync15(targetPath, { throwIfNoEntry: false });
|
|
51819
52100
|
if (!targetStat?.isDirectory())
|
|
51820
52101
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
51821
|
-
const target = realpathSync6(targetPath), inside =
|
|
52102
|
+
const target = realpathSync6(targetPath), inside = relative5(realHome, target);
|
|
51822
52103
|
if (!inside || inside === ".." || inside.startsWith(`..${sep5}`) || isAbsolute11(inside))
|
|
51823
52104
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
51824
52105
|
return { agent, home: realHome, alias, target, link: link2, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
@@ -51836,7 +52117,7 @@ function rootAliases(home, allowed = false) {
|
|
|
51836
52117
|
}
|
|
51837
52118
|
function canonicalAgentPath(path, aliases) {
|
|
51838
52119
|
const absolute = resolve11(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
|
|
51839
|
-
return binding ? join21(binding.target,
|
|
52120
|
+
return binding ? join21(binding.target, relative5(binding.alias, absolute)) : absolute;
|
|
51840
52121
|
}
|
|
51841
52122
|
function disabledCodexSkillPaths(config, aliases) {
|
|
51842
52123
|
const skills = config.skills;
|
|
@@ -51901,13 +52182,13 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
51901
52182
|
}
|
|
51902
52183
|
}
|
|
51903
52184
|
function treeHash(root) {
|
|
51904
|
-
const hash2 =
|
|
52185
|
+
const hash2 = createHash12("sha256");
|
|
51905
52186
|
let bytes = 0, files = 0;
|
|
51906
52187
|
function visit2(path) {
|
|
51907
52188
|
const stat = lstatSync15(path);
|
|
51908
52189
|
if (stat.isSymbolicLink())
|
|
51909
52190
|
throw new Error(`Refusing symlink in skill: ${path}`);
|
|
51910
|
-
const rel =
|
|
52191
|
+
const rel = relative5(root, path);
|
|
51911
52192
|
if (stat.isDirectory()) {
|
|
51912
52193
|
hash2.update(`d\x00${rel}\x00`);
|
|
51913
52194
|
for (const name of readdirSync8(path).sort())
|
|
@@ -51928,7 +52209,7 @@ function treeHash(root) {
|
|
|
51928
52209
|
return hash2.digest("hex");
|
|
51929
52210
|
}
|
|
51930
52211
|
function parseNativeMigrationTargetManifest(input) {
|
|
51931
|
-
const digest =
|
|
52212
|
+
const digest = sha3(input);
|
|
51932
52213
|
let value;
|
|
51933
52214
|
try {
|
|
51934
52215
|
value = JSON.parse(input.toString());
|
|
@@ -51958,7 +52239,7 @@ function parseNativeMigrationTargetManifest(input) {
|
|
|
51958
52239
|
assertSafePath(projectRoot);
|
|
51959
52240
|
if (isAbsolute11(item.path) || item.path.includes("\\") || item.path.length === 0 || item.path.includes("\x00"))
|
|
51960
52241
|
throw new Error(`Native migration target path must be normalized and project-relative: ${item.path}`);
|
|
51961
|
-
const resolved = resolve11(projectRoot, item.path), normalized =
|
|
52242
|
+
const resolved = resolve11(projectRoot, item.path), normalized = relative5(projectRoot, resolved);
|
|
51962
52243
|
if (!normalized || normalized === ".." || normalized.startsWith(`..${sep5}`) || isAbsolute11(normalized) || normalized !== item.path)
|
|
51963
52244
|
throw new Error(`Native migration target path escapes or is not normalized: ${item.path}`);
|
|
51964
52245
|
if (!/^[0-9a-f]{64}$/.test(item.treeSha256))
|
|
@@ -51980,7 +52261,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
51980
52261
|
const identities = new Set;
|
|
51981
52262
|
const selected = [];
|
|
51982
52263
|
for (const target of manifest.targets) {
|
|
51983
|
-
const projectRoot = resolve11(target.projectRoot), normalized =
|
|
52264
|
+
const projectRoot = resolve11(target.projectRoot), normalized = relative5(projectRoot, resolve11(projectRoot, target.path));
|
|
51984
52265
|
if (!isAbsolute11(target.projectRoot) || projectRoot !== target.projectRoot || !existsSync12(projectRoot) || !lstatSync15(projectRoot).isDirectory())
|
|
51985
52266
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
51986
52267
|
assertSafePath(projectRoot);
|
|
@@ -52333,9 +52614,9 @@ function planAgentIntegration(options) {
|
|
|
52333
52614
|
const observedNativeSources = [];
|
|
52334
52615
|
const codexPluginSkills = options.codexNativeCatalog ? reviewCodexPluginSkillControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join21(entry.path, "SKILL.md")), canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), resolve11(options.projectDir ?? home), (path) => {
|
|
52335
52616
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
52336
|
-
observedNativeSources.push({ path, sha256:
|
|
52617
|
+
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
52337
52618
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
52338
|
-
}) : policy.bridge?.codexPluginSkills ?? [];
|
|
52619
|
+
}, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
|
|
52339
52620
|
const changes = [];
|
|
52340
52621
|
for (const agent of [...new Set(options.agents)]) {
|
|
52341
52622
|
if (!INTEGRATION_AGENTS.includes(agent))
|
|
@@ -52361,7 +52642,7 @@ function planAgentIntegration(options) {
|
|
|
52361
52642
|
throw new Error("Refusing to overwrite an unrecognized Hermes supervisor");
|
|
52362
52643
|
if (supervisorBefore !== supervisorAfter)
|
|
52363
52644
|
changes.push({ path: supervisorPath, before: supervisorBefore, after: supervisorAfter });
|
|
52364
|
-
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256:
|
|
52645
|
+
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256: sha3(supervisorAfter) };
|
|
52365
52646
|
const after3 = configureHermesHooks(before2, supervisor, previous);
|
|
52366
52647
|
if (before2 !== after3)
|
|
52367
52648
|
changes.push({ path, before: before2, after: after3 });
|
|
@@ -52408,12 +52689,12 @@ function planAgentIntegration(options) {
|
|
|
52408
52689
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
52409
52690
|
home: resolve11(home),
|
|
52410
52691
|
includeVendor: true,
|
|
52411
|
-
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join21(dataDir3, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256:
|
|
52692
|
+
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join21(dataDir3, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
|
|
52412
52693
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
52413
52694
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
52414
52695
|
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
52415
52696
|
rootAliases: aliases,
|
|
52416
|
-
...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256:
|
|
52697
|
+
...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join21(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join21(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview } : {},
|
|
52417
52698
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
52418
52699
|
} };
|
|
52419
52700
|
const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
|
|
@@ -52451,7 +52732,7 @@ function atomicWrite2(path, content) {
|
|
|
52451
52732
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
52452
52733
|
const home = resolve11(options.home ?? homedir3()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
|
|
52453
52734
|
const snapshot = readManagedSkillPolicySnapshot(dataDir3);
|
|
52454
|
-
if (!snapshot ||
|
|
52735
|
+
if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
|
|
52455
52736
|
throw new Error("Managed policy preimage changed");
|
|
52456
52737
|
const policy = snapshot.value, aliases = policy.bridge?.rootAliases ?? [];
|
|
52457
52738
|
recheckRootAliases(aliases);
|
|
@@ -52460,7 +52741,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
52460
52741
|
throw new Error("Settings witness upgrade requires an existing reviewed native binding");
|
|
52461
52742
|
const configPath = canonicalAgentPath(join21(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
|
|
52462
52743
|
const settings = readOptional(configPath);
|
|
52463
|
-
if (settings === null ||
|
|
52744
|
+
if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
|
|
52464
52745
|
throw new Error("Native settings preimage changed");
|
|
52465
52746
|
const sources = binding.sources.filter((source) => source.path === configPath && source.format === undefined && source.fields === undefined);
|
|
52466
52747
|
if (sources.length !== 1)
|
|
@@ -52468,15 +52749,40 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
52468
52749
|
const previous = sources[0];
|
|
52469
52750
|
if (typeof previous.sha256 !== "string")
|
|
52470
52751
|
throw new Error("Settings witness upgrade requires a preserved settings preimage");
|
|
52752
|
+
const replaced = [previous];
|
|
52753
|
+
if (options.agent === "codex") {
|
|
52754
|
+
if (previous.hashMode !== undefined && previous.hashMode !== "bytes" && previous.hashMode !== "codex-settings-v1")
|
|
52755
|
+
throw new Error("Codex settings witness upgrade requires a preserved raw or codex-settings-v1 configuration witness");
|
|
52756
|
+
const preimage = readCodexSettingsPreimage(options.reviewedPreimage);
|
|
52757
|
+
for (const source of binding.sources) {
|
|
52758
|
+
if (source === previous || source.path !== configPath)
|
|
52759
|
+
continue;
|
|
52760
|
+
const fields = source.fields;
|
|
52761
|
+
if (source.hashMode !== undefined || source.format !== "toml" || !Array.isArray(fields) || !isDeepStrictEqual4(fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]) || typeof source.sha256 !== "string")
|
|
52762
|
+
throw new Error("Codex settings witness upgrade found an unrecognized configuration witness");
|
|
52763
|
+
if (projectNativeDiscoveryFields(preimage, "toml", fields, configPath) !== source.sha256)
|
|
52764
|
+
throw new Error("Reviewed preimage does not explain the Codex discovery projection witness");
|
|
52765
|
+
replaced.push(source);
|
|
52766
|
+
}
|
|
52767
|
+
}
|
|
52471
52768
|
const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
52472
|
-
const
|
|
52769
|
+
const rebound = [];
|
|
52770
|
+
for (const source of binding.sources) {
|
|
52771
|
+
if (!replaced.includes(source)) {
|
|
52772
|
+
rebound.push(source);
|
|
52773
|
+
continue;
|
|
52774
|
+
}
|
|
52775
|
+
if (source === replaced[0])
|
|
52776
|
+
rebound.push(next);
|
|
52777
|
+
}
|
|
52778
|
+
const replacement = { ...binding, sources: rebound };
|
|
52473
52779
|
verifyAgentDiscovery(replacement);
|
|
52474
52780
|
const after2 = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
52475
|
-
return { dataDir: dataDir3, profileId: policy.profileId, changes: [{ path: join21(dataDir3, "agent-policy.json"), before: snapshot.text, after: after2 }], nativeSkills: [], observedPolicy: { path: join21(dataDir3, "agent-policy.json"), before: snapshot.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256 } };
|
|
52781
|
+
return { dataDir: dataDir3, profileId: policy.profileId, changes: [{ path: join21(dataDir3, "agent-policy.json"), before: snapshot.text, after: after2 }], nativeSkills: [], observedPolicy: { path: join21(dataDir3, "agent-policy.json"), before: snapshot.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
|
|
52476
52782
|
}
|
|
52477
52783
|
function applyAgentIntegration(plan) {
|
|
52478
52784
|
for (const source of plan.observedNativeSources ?? [])
|
|
52479
|
-
if (
|
|
52785
|
+
if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
|
|
52480
52786
|
throw new Error("Native identity source changed after planning");
|
|
52481
52787
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
52482
52788
|
throw new Error("Native settings changed after witness planning");
|
|
@@ -52496,7 +52802,7 @@ function applyAgentIntegration(plan) {
|
|
|
52496
52802
|
const upgrade = plan.settingsWitnessUpgrade;
|
|
52497
52803
|
if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
|
|
52498
52804
|
throw new Error("Invalid settings witness upgrade plan");
|
|
52499
|
-
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256:
|
|
52805
|
+
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
52500
52806
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
|
|
52501
52807
|
throw new Error("Settings witness upgrade plan changed");
|
|
52502
52808
|
provenDiscovery = verified.discoveryBefore[0];
|
|
@@ -52524,7 +52830,7 @@ function applyAgentIntegration(plan) {
|
|
|
52524
52830
|
chmodSync(backupRoot, 448);
|
|
52525
52831
|
for (const [index, change] of plan.changes.entries())
|
|
52526
52832
|
if (change.before !== null) {
|
|
52527
|
-
const backup = join21(backupRoot, `${index}-${
|
|
52833
|
+
const backup = join21(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
|
|
52528
52834
|
writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
|
|
52529
52835
|
if (readFileSync13(backup, "utf8") !== change.before)
|
|
52530
52836
|
throw new Error("Native configuration preservation readback failed");
|
|
@@ -52543,7 +52849,7 @@ function applyAgentIntegration(plan) {
|
|
|
52543
52849
|
}
|
|
52544
52850
|
recheckRootAliases(aliases);
|
|
52545
52851
|
for (const source of plan.observedNativeSources ?? [])
|
|
52546
|
-
if (
|
|
52852
|
+
if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
|
|
52547
52853
|
throw new Error("Native identity source changed during application");
|
|
52548
52854
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
52549
52855
|
throw new Error("Native settings changed during witness application");
|
|
@@ -52555,7 +52861,7 @@ function applyAgentIntegration(plan) {
|
|
|
52555
52861
|
}
|
|
52556
52862
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
52557
52863
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
52558
|
-
atomicWrite2(join21(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null :
|
|
52864
|
+
atomicWrite2(join21(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha3(change.before), afterHash: sha3(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
|
52559
52865
|
`);
|
|
52560
52866
|
} catch (error) {
|
|
52561
52867
|
for (const change of written.reverse()) {
|
|
@@ -52634,8 +52940,8 @@ function archiveNativeSkills(inventory, options) {
|
|
|
52634
52940
|
const operationId = randomUUID5(), archiveRoot = join21(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join21(archiveRoot, "receipt.json");
|
|
52635
52941
|
const entries = selected.map((entry, index) => ({
|
|
52636
52942
|
source: entry.vendor ? join21(entry.path, "SKILL.md") : entry.path,
|
|
52637
|
-
archive: join21(archiveRoot, `${index}-${
|
|
52638
|
-
hash: entry.vendor ?
|
|
52943
|
+
archive: join21(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
|
|
52944
|
+
hash: entry.vendor ? sha3(readNativeBytes(join21(entry.path, "SKILL.md"))) : entry.hash,
|
|
52639
52945
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
52640
52946
|
status: "planned"
|
|
52641
52947
|
}));
|
|
@@ -52646,7 +52952,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
52646
52952
|
const stat = lstatSync15(entry.archive);
|
|
52647
52953
|
if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
|
|
52648
52954
|
throw new Error("Archive type changed");
|
|
52649
|
-
if ((entry.discoveryOnly ?
|
|
52955
|
+
if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
52650
52956
|
throw new Error("Archive bytes changed");
|
|
52651
52957
|
};
|
|
52652
52958
|
assertSafePath(archiveRoot);
|
|
@@ -52766,7 +53072,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
52766
53072
|
throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
|
|
52767
53073
|
if (agent === "hermes") {
|
|
52768
53074
|
const supervisor = binding.supervisors?.hermes;
|
|
52769
|
-
if (supervisor?.path !== join21(dataDir3, "agent-hooks", "hermes.js") || supervisor?.sha256 !==
|
|
53075
|
+
if (supervisor?.path !== join21(dataDir3, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
|
|
52770
53076
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
52771
53077
|
assertHermesProtection(home, config, command, profile, supervisor);
|
|
52772
53078
|
} else if (agent === "opencode") {
|
|
@@ -52882,7 +53188,7 @@ function assertNativeExportAllowed(dataDir3 = getDataDirReadOnly()) {
|
|
|
52882
53188
|
if (requiresCliSkillLoading(dataDir3))
|
|
52883
53189
|
throw new Error("NATIVE_SKILL_EXPORT_DISABLED: this station loads skills through the Skills CLI; use skills sync --selection-profile <id>");
|
|
52884
53190
|
}
|
|
52885
|
-
var
|
|
53191
|
+
var sha3 = (data) => createHash12("sha256").update(data).digest("hex"), HOOK_EVENTS, ROOTS, SKILLS_LOADING_POLICY = "Skills loading policy: discover skills with `skills list` or `skills search`, and read selected instructions with `skills load <slug>`. Use `skills sync` to refresh the shared profile. The only native skill is the owned skills-cli bridge. Create and edit payload skills in the Skills authoring workspace; do not copy payload instructions into native discovery folders. Context loading does not authorize execution; use `skills run` only when the task calls for running a skill.";
|
|
52886
53192
|
var init_agent_integration = __esm(() => {
|
|
52887
53193
|
init_codex_plugin_skill_controls();
|
|
52888
53194
|
init_codex_native_skill_catalog();
|
|
@@ -53051,7 +53357,7 @@ import {
|
|
|
53051
53357
|
writeFileSync as writeFileSync6
|
|
53052
53358
|
} from "fs";
|
|
53053
53359
|
import { homedir as homedir4 } from "os";
|
|
53054
|
-
import { basename as
|
|
53360
|
+
import { basename as basename6, dirname as dirname12, join as join25 } from "path";
|
|
53055
53361
|
function isSkillsOwnershipMarker(marker) {
|
|
53056
53362
|
return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
|
|
53057
53363
|
}
|
|
@@ -53285,7 +53591,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
53285
53591
|
return { action, path: skillMdPath };
|
|
53286
53592
|
const parentDir = dirname12(dir);
|
|
53287
53593
|
mkdirSync7(parentDir, { recursive: true });
|
|
53288
|
-
const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${
|
|
53594
|
+
const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${basename6(dir)}-`));
|
|
53289
53595
|
const candidateDir = join25(transactionDir, "candidate");
|
|
53290
53596
|
const backupDir = join25(transactionDir, "backup");
|
|
53291
53597
|
const candidateSkillMdPath = join25(candidateDir, "SKILL.md");
|
|
@@ -54464,7 +54770,7 @@ var init_selection_resolver = __esm(() => {
|
|
|
54464
54770
|
});
|
|
54465
54771
|
|
|
54466
54772
|
// src/lib/plugin-projection.ts
|
|
54467
|
-
import { createHash as
|
|
54773
|
+
import { createHash as createHash13 } from "crypto";
|
|
54468
54774
|
import { posix } from "path";
|
|
54469
54775
|
function pluginRefusal(message) {
|
|
54470
54776
|
throw new SkillSelectionError("PLUGIN_ADMISSION_REFUSED", message);
|
|
@@ -54657,7 +54963,7 @@ function buildPluginProjection(entries) {
|
|
|
54657
54963
|
files.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
54658
54964
|
return { manifest, payloads: manifest.payloads, files, witnesses: pluginFileWitnesses(files), originalTreeDigest, projectionTreeDigest: pluginTreeDigest(files), removed: removed.length ? pluginFileWitnesses(removed) : [] };
|
|
54659
54965
|
}
|
|
54660
|
-
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json", PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) =>
|
|
54966
|
+
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json", PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) => createHash13("sha256").update(bytes).digest("hex"), MANIFEST_FIELDS;
|
|
54661
54967
|
var init_plugin_projection = __esm(() => {
|
|
54662
54968
|
init_selection_cache();
|
|
54663
54969
|
PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -54665,7 +54971,7 @@ var init_plugin_projection = __esm(() => {
|
|
|
54665
54971
|
});
|
|
54666
54972
|
|
|
54667
54973
|
// src/lib/plugin-projection-store.ts
|
|
54668
|
-
import { createHash as
|
|
54974
|
+
import { createHash as createHash14, randomUUID as randomUUID6 } from "crypto";
|
|
54669
54975
|
import { constants as constants9, closeSync as closeSync9, fchmodSync, fstatSync as fstatSync9, fsyncSync as fsyncSync3, linkSync as linkSync2, lstatSync as lstatSync17, mkdirSync as mkdirSync9, openSync as openSync9, opendirSync as opendirSync4, readSync as readSync7, renameSync as renameSync5, rmdirSync as rmdirSync3, rmSync as rmSync4, unlinkSync as unlinkSync4, writeSync as writeSync2 } from "fs";
|
|
54670
54976
|
import { dirname as dirname13, isAbsolute as isAbsolute12, join as join29, parse as parse5, resolve as resolve12 } from "path";
|
|
54671
54977
|
function stat(path) {
|
|
@@ -54677,7 +54983,7 @@ function stat(path) {
|
|
|
54677
54983
|
return pluginRefusal("Cannot inspect plugin filesystem entry");
|
|
54678
54984
|
}
|
|
54679
54985
|
}
|
|
54680
|
-
function
|
|
54986
|
+
function canonical5(path) {
|
|
54681
54987
|
pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute12(path) && resolve12(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
|
|
54682
54988
|
}
|
|
54683
54989
|
function owner(stats, system = false) {
|
|
@@ -54688,7 +54994,7 @@ function same3(left, right, identityOnly = false) {
|
|
|
54688
54994
|
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");
|
|
54689
54995
|
}
|
|
54690
54996
|
function ancestors(path, create3 = false) {
|
|
54691
|
-
|
|
54997
|
+
canonical5(path);
|
|
54692
54998
|
const result2 = [], parent = dirname13(path), filesystemRoot = parse5(parent).root;
|
|
54693
54999
|
let current = filesystemRoot;
|
|
54694
55000
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
@@ -54798,7 +55104,7 @@ function directoryNames(path) {
|
|
|
54798
55104
|
function snapshotPluginTree(root, options = {}) {
|
|
54799
55105
|
const chain = ancestors(root), entries = [], files = [], directories = [];
|
|
54800
55106
|
let bytes = 0, visited = 0;
|
|
54801
|
-
const visit2 = (directory,
|
|
55107
|
+
const visit2 = (directory, relative6) => {
|
|
54802
55108
|
const before2 = stat(directory);
|
|
54803
55109
|
pluginNeed(before2 && before2.isDirectory() && !before2.isSymbolicLink(), "Plugin tree must contain real directories");
|
|
54804
55110
|
const names = directoryNames(directory);
|
|
@@ -54806,18 +55112,18 @@ function snapshotPluginTree(root, options = {}) {
|
|
|
54806
55112
|
directories.push({ path: directory, stat: before2, names });
|
|
54807
55113
|
for (const name of names) {
|
|
54808
55114
|
pluginNeed(++visited <= PLUGIN_PROJECTION_LIMITS.files * 50, "Plugin tree membership exceeds its limit");
|
|
54809
|
-
const path =
|
|
55115
|
+
const path = relative6 ? `${relative6}/${name}` : name;
|
|
54810
55116
|
pluginNeed(new TextEncoder().encode(path).byteLength <= 100 && !/[\\:\x00-\x1f\x7f]/u.test(path), "Plugin entry path exceeds its limit or is unsafe");
|
|
54811
55117
|
const absolute = join29(directory, name), entryStat = stat(absolute);
|
|
54812
55118
|
pluginNeed(entryStat, "Plugin tree member disappeared");
|
|
54813
|
-
if (
|
|
55119
|
+
if (relative6 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
|
|
54814
55120
|
const bytes2 = readBytes(absolute, 32, "native-pin");
|
|
54815
55121
|
pluginNeed(/^[1-9][0-9]{12}$/.test(new TextDecoder().decode(bytes2)), "Malformed native orphan timestamp");
|
|
54816
55122
|
same3(entryStat, stat(absolute));
|
|
54817
55123
|
files.push({ path: absolute, stat: entryStat });
|
|
54818
55124
|
continue;
|
|
54819
55125
|
}
|
|
54820
|
-
if (
|
|
55126
|
+
if (relative6 === "" && name === ".in_use" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
|
|
54821
55127
|
pluginNeed(entryStat.isDirectory() && !entryStat.isSymbolicLink(), "Native plugin pins require a real directory");
|
|
54822
55128
|
owner(entryStat);
|
|
54823
55129
|
const pins = directoryNames(absolute);
|
|
@@ -55003,7 +55309,7 @@ function pluginExecutableDigest(path) {
|
|
|
55003
55309
|
const opened = fstatSync9(fd, { bigint: true });
|
|
55004
55310
|
same3(before2, opened);
|
|
55005
55311
|
fileShape(opened, maximum, "executable");
|
|
55006
|
-
const hash2 =
|
|
55312
|
+
const hash2 = createHash14("sha256"), buffer = new Uint8Array(64 * 1024);
|
|
55007
55313
|
let remaining = Number(opened.size);
|
|
55008
55314
|
while (remaining > 0) {
|
|
55009
55315
|
const count = readSync7(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
|
|
@@ -55439,7 +55745,7 @@ var init_plugin_admission = __esm(() => {
|
|
|
55439
55745
|
|
|
55440
55746
|
// src/lib/plugin-discovery.ts
|
|
55441
55747
|
import { closeSync as closeSync10, constants as constants10, fstatSync as fstatSync10, lstatSync as lstatSync18, openSync as openSync10, opendirSync as opendirSync5, readSync as readSync8 } from "fs";
|
|
55442
|
-
import { basename as
|
|
55748
|
+
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute14, join as join31, resolve as resolve14 } from "path";
|
|
55443
55749
|
function validateManagedPluginWitnesses(value) {
|
|
55444
55750
|
pluginNeed(Array.isArray(value) && value.length > 0 && value.length <= PLUGIN_PROJECTION_LIMITS.registrations, "Invalid managed plugin witness collection");
|
|
55445
55751
|
const seen = new Set;
|
|
@@ -55522,7 +55828,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
55522
55828
|
pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join31(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
|
|
55523
55829
|
const approval = (producer) => {
|
|
55524
55830
|
pluginText(producer);
|
|
55525
|
-
const digest = `sha256:${
|
|
55831
|
+
const digest = `sha256:${basename7(producer)}`;
|
|
55526
55832
|
const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest);
|
|
55527
55833
|
pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
|
|
55528
55834
|
if (!verified.has(producer)) {
|
|
@@ -55586,21 +55892,21 @@ var init_plugin_discovery = __esm(() => {
|
|
|
55586
55892
|
});
|
|
55587
55893
|
|
|
55588
55894
|
// src/lib/claude-marketplace-registry.ts
|
|
55589
|
-
import { createHash as
|
|
55895
|
+
import { createHash as createHash15 } from "crypto";
|
|
55590
55896
|
import { closeSync as closeSync11, constants as constants11, fstatSync as fstatSync11, lstatSync as lstatSync19, openSync as openSync11, readSync as readSync9 } from "fs";
|
|
55591
|
-
import { basename as
|
|
55592
|
-
function
|
|
55897
|
+
import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute15, resolve as resolve15 } from "path";
|
|
55898
|
+
function need5(value, reason) {
|
|
55593
55899
|
if (!value)
|
|
55594
55900
|
throw new Error(`Claude marketplace registry ${reason}`);
|
|
55595
55901
|
}
|
|
55596
55902
|
function absolutePath3(value) {
|
|
55597
|
-
|
|
55903
|
+
need5(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve15(value) === value, "requires a normalized absolute path");
|
|
55598
55904
|
}
|
|
55599
55905
|
function parents2(path) {
|
|
55600
55906
|
const result2 = [];
|
|
55601
55907
|
for (let at2 = dirname15(path);; at2 = dirname15(at2)) {
|
|
55602
55908
|
const stat2 = lstatSync19(at2, { bigint: true });
|
|
55603
|
-
|
|
55909
|
+
need5(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
55604
55910
|
result2.push([at2, stat2]);
|
|
55605
55911
|
if (at2 === dirname15(at2))
|
|
55606
55912
|
return result2;
|
|
@@ -55608,15 +55914,15 @@ function parents2(path) {
|
|
|
55608
55914
|
}
|
|
55609
55915
|
function readRegistry2(path, budget) {
|
|
55610
55916
|
absolutePath3(path);
|
|
55611
|
-
|
|
55612
|
-
|
|
55917
|
+
need5(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
|
|
55918
|
+
need5(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
|
|
55613
55919
|
const ancestors2 = parents2(path), initial2 = lstatSync19(path, { bigint: true });
|
|
55614
|
-
|
|
55615
|
-
|
|
55920
|
+
need5(initial2.isFile() && !initial2.isSymbolicLink() && initial2.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
|
|
55921
|
+
need5(initial2.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
|
|
55616
55922
|
const fd = openSync11(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
|
|
55617
55923
|
try {
|
|
55618
55924
|
const opened = fstatSync11(fd, { bigint: true });
|
|
55619
|
-
|
|
55925
|
+
need5(opened.isFile() && sameFile2(initial2, opened), "changed during open");
|
|
55620
55926
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
55621
55927
|
let length = 0;
|
|
55622
55928
|
while (length < bytes.length) {
|
|
@@ -55626,10 +55932,10 @@ function readRegistry2(path, budget) {
|
|
|
55626
55932
|
length += count;
|
|
55627
55933
|
}
|
|
55628
55934
|
const after2 = fstatSync11(fd, { bigint: true }), current = lstatSync19(path, { bigint: true });
|
|
55629
|
-
|
|
55935
|
+
need5(current.isFile() && sameFile2(opened, after2) && sameFile2(after2, current) && BigInt(length) === opened.size, "changed during read");
|
|
55630
55936
|
for (const [ancestor, before2] of ancestors2) {
|
|
55631
55937
|
const now3 = lstatSync19(ancestor, { bigint: true });
|
|
55632
|
-
|
|
55938
|
+
need5(now3.isDirectory() && before2.dev === now3.dev && before2.ino === now3.ino && before2.mode === now3.mode, "parent changed during read");
|
|
55633
55939
|
}
|
|
55634
55940
|
budget.remaining -= length;
|
|
55635
55941
|
try {
|
|
@@ -55649,7 +55955,7 @@ function parse6(text2) {
|
|
|
55649
55955
|
at2++;
|
|
55650
55956
|
};
|
|
55651
55957
|
function string() {
|
|
55652
|
-
|
|
55958
|
+
need5(text2[at2] === '"', "contains invalid JSON");
|
|
55653
55959
|
const start = at2++;
|
|
55654
55960
|
while (at2 < text2.length) {
|
|
55655
55961
|
const character = text2[at2++];
|
|
@@ -55660,7 +55966,7 @@ function parse6(text2) {
|
|
|
55660
55966
|
} catch {
|
|
55661
55967
|
throw new Error("Claude marketplace registry contains invalid JSON string");
|
|
55662
55968
|
}
|
|
55663
|
-
|
|
55969
|
+
need5(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
|
|
55664
55970
|
return value2;
|
|
55665
55971
|
}
|
|
55666
55972
|
if (character === "\\")
|
|
@@ -55669,7 +55975,7 @@ function parse6(text2) {
|
|
|
55669
55975
|
throw new Error("Claude marketplace registry contains an unterminated JSON string");
|
|
55670
55976
|
}
|
|
55671
55977
|
function value(depth) {
|
|
55672
|
-
|
|
55978
|
+
need5(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
55673
55979
|
space();
|
|
55674
55980
|
const character = text2[at2];
|
|
55675
55981
|
if (character === '"')
|
|
@@ -55683,17 +55989,17 @@ function parse6(text2) {
|
|
|
55683
55989
|
if (object5) {
|
|
55684
55990
|
space();
|
|
55685
55991
|
const key = string();
|
|
55686
|
-
|
|
55992
|
+
need5(!keys2.has(key), "contains a duplicate JSON key");
|
|
55687
55993
|
keys2.add(key);
|
|
55688
55994
|
space();
|
|
55689
|
-
|
|
55995
|
+
need5(text2[at2++] === ":", "contains invalid JSON");
|
|
55690
55996
|
entries.push([key, value(depth + 1)]);
|
|
55691
55997
|
} else
|
|
55692
55998
|
items.push(value(depth + 1));
|
|
55693
55999
|
space();
|
|
55694
56000
|
if (text2[at2] === end)
|
|
55695
56001
|
break;
|
|
55696
|
-
|
|
56002
|
+
need5(text2[at2++] === ",", "contains invalid JSON");
|
|
55697
56003
|
}
|
|
55698
56004
|
at2++;
|
|
55699
56005
|
return object5 ? { kind: "object", entries } : { kind: "array", items };
|
|
@@ -55705,21 +56011,21 @@ function parse6(text2) {
|
|
|
55705
56011
|
}
|
|
55706
56012
|
numberPattern.lastIndex = at2;
|
|
55707
56013
|
const number = numberPattern.exec(text2);
|
|
55708
|
-
|
|
56014
|
+
need5(number, "contains invalid JSON");
|
|
55709
56015
|
at2 += number[0].length;
|
|
55710
56016
|
return { kind: "number", value: number[0] };
|
|
55711
56017
|
}
|
|
55712
56018
|
const root2 = value(0);
|
|
55713
56019
|
space();
|
|
55714
|
-
|
|
55715
|
-
|
|
56020
|
+
need5(at2 === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
|
|
56021
|
+
need5(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
|
|
55716
56022
|
return root2;
|
|
55717
56023
|
}
|
|
55718
56024
|
function exactKeys(value, keys2) {
|
|
55719
56025
|
return value?.kind === "object" && value.entries.length === keys2.length && value.entries.every(([key]) => keys2.includes(key));
|
|
55720
56026
|
}
|
|
55721
56027
|
function textField(value) {
|
|
55722
|
-
|
|
56028
|
+
need5(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
|
|
55723
56029
|
return value.value;
|
|
55724
56030
|
}
|
|
55725
56031
|
function normalizeRow(row, version = 1) {
|
|
@@ -55727,7 +56033,7 @@ function normalizeRow(row, version = 1) {
|
|
|
55727
56033
|
if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
55728
56034
|
return;
|
|
55729
56035
|
const autoUpdate = field(row, "autoUpdate");
|
|
55730
|
-
|
|
56036
|
+
need5(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
55731
56037
|
}
|
|
55732
56038
|
const source = field(row, "source");
|
|
55733
56039
|
if (source?.kind !== "object")
|
|
@@ -55743,23 +56049,23 @@ function normalizeRow(row, version = 1) {
|
|
|
55743
56049
|
absolutePath3(sourceLocation);
|
|
55744
56050
|
absolutePath3(textField(field(row, "installLocation")));
|
|
55745
56051
|
const updated = textField(field(row, "lastUpdated"));
|
|
55746
|
-
|
|
56052
|
+
need5(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
|
|
55747
56053
|
const millis = Date.parse(updated);
|
|
55748
|
-
|
|
56054
|
+
need5(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
55749
56055
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
55750
56056
|
}
|
|
55751
56057
|
function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
55752
56058
|
const value = parse6(readRegistry2(path, budget));
|
|
55753
56059
|
for (const [, row] of value.entries)
|
|
55754
56060
|
normalizeRow(row);
|
|
55755
|
-
const sha2562 =
|
|
56061
|
+
const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
55756
56062
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
55757
56063
|
}
|
|
55758
56064
|
function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
55759
56065
|
const value = parse6(readRegistry2(path, budget));
|
|
55760
56066
|
for (const [, row] of value.entries)
|
|
55761
56067
|
normalizeRow(row, 2);
|
|
55762
|
-
const sha2562 =
|
|
56068
|
+
const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
55763
56069
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
55764
56070
|
}
|
|
55765
56071
|
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS, sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs, field = (object5, key) => object5.entries.find(([name]) => name === key)?.[1];
|
|
@@ -55769,17 +56075,17 @@ var init_claude_marketplace_registry = __esm(() => {
|
|
|
55769
56075
|
|
|
55770
56076
|
// src/lib/codex-hook-trust-files.ts
|
|
55771
56077
|
import { closeSync as closeSync12, constants as constants12, fstatSync as fstatSync12, fsyncSync as fsyncSync4, lstatSync as lstatSync20, openSync as openSync12, readSync as readSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
55772
|
-
import { createHash as
|
|
56078
|
+
import { createHash as createHash16 } from "crypto";
|
|
55773
56079
|
import { dirname as dirname16 } from "path";
|
|
55774
|
-
function
|
|
56080
|
+
function need6(value, code) {
|
|
55775
56081
|
if (!value)
|
|
55776
56082
|
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
55777
56083
|
}
|
|
55778
56084
|
function safeParents(file) {
|
|
55779
56085
|
for (let p = dirname16(file);; p = dirname16(p)) {
|
|
55780
56086
|
const s = lstatSync20(p);
|
|
55781
|
-
|
|
55782
|
-
|
|
56087
|
+
need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
|
|
56088
|
+
need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
55783
56089
|
if (dirname16(p) === p)
|
|
55784
56090
|
break;
|
|
55785
56091
|
}
|
|
@@ -55788,12 +56094,12 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
55788
56094
|
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
55789
56095
|
safeParents(file);
|
|
55790
56096
|
const initial2 = lstatSync20(file, { bigint: true });
|
|
55791
|
-
|
|
55792
|
-
|
|
56097
|
+
need6(initial2.isFile() && initial2.uid === BigInt(process.getuid()) && (initial2.nlink === 1n || readOnlyPackage && initial2.nlink > 1n) && (initial2.mode & 0o022n) === 0n && (!privateFile || (initial2.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
|
|
56098
|
+
need6(initial2.size <= 8n * 1024n * 1024n, "FILE_BOUND");
|
|
55793
56099
|
const fd = openSync12(file, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
|
|
55794
56100
|
try {
|
|
55795
56101
|
const before2 = fstatSync12(fd, { bigint: true });
|
|
55796
|
-
|
|
56102
|
+
need6(sameStat(initial2, before2), "INPUT_CHANGED");
|
|
55797
56103
|
const bytes = Buffer.alloc(Number(before2.size) + 1);
|
|
55798
56104
|
let length = 0;
|
|
55799
56105
|
while (length < bytes.length) {
|
|
@@ -55802,16 +56108,16 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
55802
56108
|
break;
|
|
55803
56109
|
length += n2;
|
|
55804
56110
|
}
|
|
55805
|
-
|
|
56111
|
+
need6(length === Number(before2.size) && sameStat(before2, fstatSync12(fd, { bigint: true })) && sameStat(before2, lstatSync20(file, { bigint: true })), "INPUT_CHANGED");
|
|
55806
56112
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
55807
|
-
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before2, sha256:
|
|
56113
|
+
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before2, sha256: createHash16("sha256").update(content).digest("hex") };
|
|
55808
56114
|
} finally {
|
|
55809
56115
|
closeSync12(fd);
|
|
55810
56116
|
}
|
|
55811
56117
|
}
|
|
55812
56118
|
function unchanged2(before2) {
|
|
55813
56119
|
const after2 = snapshot(before2.file, before2.privateFile, before2.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
55814
|
-
|
|
56120
|
+
need6(sameStat(before2.stat, after2.stat) && after2.sha256 === before2.sha256, "INPUT_CHANGED");
|
|
55815
56121
|
}
|
|
55816
56122
|
function save(file, content) {
|
|
55817
56123
|
safeParents(file);
|
|
@@ -55827,7 +56133,7 @@ var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs
|
|
|
55827
56133
|
var init_codex_hook_trust_files = () => {};
|
|
55828
56134
|
|
|
55829
56135
|
// src/lib/codex-hook-trust-layout.ts
|
|
55830
|
-
import { isDeepStrictEqual as
|
|
56136
|
+
import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
|
|
55831
56137
|
function codexTrustTextWitness(text2, keys2, canonicalize = true) {
|
|
55832
56138
|
const parsed = Bun.TOML.parse(text2), wanted = new Set(keys2);
|
|
55833
56139
|
const headers = [...text2.matchAll(/^[ \t]*(\[[^\r\n]+\])[ \t]*(#[^\r\n]*)?(?:\r?\n|$)/gm)];
|
|
@@ -55892,7 +56198,7 @@ function codexTrustTextWitness(text2, keys2, canonicalize = true) {
|
|
|
55892
56198
|
if (expected.hooks)
|
|
55893
56199
|
delete expected.hooks.state;
|
|
55894
56200
|
try {
|
|
55895
|
-
if (!
|
|
56201
|
+
if (!isDeepStrictEqual5(prune(Bun.TOML.parse(noState)), prune(expected)))
|
|
55896
56202
|
throw fail2();
|
|
55897
56203
|
} catch {
|
|
55898
56204
|
throw fail2();
|
|
@@ -55962,55 +56268,55 @@ var init_codex_hook_trust_layout = () => {};
|
|
|
55962
56268
|
|
|
55963
56269
|
// src/lib/codex-hook-additions-review.ts
|
|
55964
56270
|
import { isAbsolute as isAbsolute16 } from "path";
|
|
55965
|
-
import { isDeepStrictEqual as
|
|
56271
|
+
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
55966
56272
|
function exactKeys2(value, keys2) {
|
|
55967
|
-
|
|
56273
|
+
need6(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual6(Object.keys(value).sort(), [...keys2].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
55968
56274
|
}
|
|
55969
56275
|
function readReviewedAdditionTrustStates(proof, before2, current, journalHooks) {
|
|
55970
56276
|
const captured = proofs.get(proof);
|
|
55971
|
-
|
|
56277
|
+
need6(!!captured && captured.before.file === before2.file && captured.before.sha256 === before2.sha256 && captured.current.file === current.file && captured.current.sha256 === current.sha256 && isDeepStrictEqual6(captured.journalHooks, journalHooks.map(({ key, currentHash }) => ({ key, currentHash }))), "RECONCILE_ADDITIONS_PROOF_CHANGED");
|
|
55972
56278
|
captured.recheck();
|
|
55973
56279
|
unchanged2(before2);
|
|
55974
56280
|
unchanged2(current);
|
|
55975
56281
|
return captured.states;
|
|
55976
56282
|
}
|
|
55977
56283
|
function reviewNativeHookAdditions(options) {
|
|
55978
|
-
|
|
56284
|
+
need6(isAbsolute16(options.path), "RECONCILE_ADDITIONS_REVIEW_PATH");
|
|
55979
56285
|
const reviewFile = snapshot(options.path, true), review = JSON.parse(reviewFile.text);
|
|
55980
56286
|
exactKeys2(review, ["version", "kind", "reason", "producerEvidence", "journalIntentSha256", "policySha256", "configCurrentSha256", "hooksCurrentSha256", "producerBefore", "additions"]);
|
|
55981
|
-
|
|
56287
|
+
need6((review.version === 1 || review.version === 2) && review.kind === "native-hook-additions" && text2(review.reason) && text2(review.producerEvidence), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
55982
56288
|
for (const [name, expected] of [["journalIntentSha256", options.intent.sha256], ["policySha256", options.policy.sha256], ["configCurrentSha256", options.configCurrent.sha256], ["hooksCurrentSha256", options.hooksCurrent.sha256]])
|
|
55983
|
-
|
|
56289
|
+
need6(hash2(review[name]) && review[name] === expected, "RECONCILE_ADDITIONS_REVIEW_CHANGED");
|
|
55984
56290
|
exactKeys2(review.producerBefore, ["configPath", "configSha256", "hooksPath", "hooksSha256"]);
|
|
55985
56291
|
const before2 = review.producerBefore;
|
|
55986
|
-
|
|
56292
|
+
need6(isAbsolute16(before2.configPath) && isAbsolute16(before2.hooksPath) && hash2(before2.configSha256) && hash2(before2.hooksSha256), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
55987
56293
|
const producerConfig = snapshot(before2.configPath, true), producerHooks = snapshot(before2.hooksPath, true);
|
|
55988
|
-
|
|
56294
|
+
need6(producerConfig.sha256 === before2.configSha256 && producerHooks.sha256 === before2.hooksSha256 && producerHooks.sha256 === options.hooksBefore.sha256, "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
55989
56295
|
const original = structure(options.configBefore.text), producer = structure(producerConfig.text), expectedProducer = structuredClone(original), originalKeys = options.journalHooks.map((hook) => hook.key);
|
|
55990
56296
|
expectedProducer.hooks ??= {};
|
|
55991
56297
|
expectedProducer.hooks.state ??= {};
|
|
55992
56298
|
for (const hook of options.journalHooks)
|
|
55993
56299
|
expectedProducer.hooks.state[hook.key] = { ...expectedProducer.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
55994
|
-
|
|
55995
|
-
|
|
56300
|
+
need6(isDeepStrictEqual6(expectedProducer, producer) && codexTrustReconcileWitness(options.configBefore.text, originalKeys) === codexTrustReconcileWitness(producerConfig.text, originalKeys) && codexTrustUnmanagedSemantic(options.configBefore.text, originalKeys) === codexTrustUnmanagedSemantic(producerConfig.text, originalKeys), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
56301
|
+
need6(Array.isArray(review.additions) && review.additions.length > 0 && review.additions.length <= 20, "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
55996
56302
|
const expectedHooks = JSON.parse(options.hooksBefore.text), currentHooks = JSON.parse(options.hooksCurrent.text), expectedConfig = structuredClone(producer);
|
|
55997
|
-
|
|
56303
|
+
need6(expectedHooks.hooks && typeof expectedHooks.hooks === "object" && !Array.isArray(expectedHooks.hooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
55998
56304
|
const added = [], states = [], seen = new Set, commands = new Set(options.recordedNativeHooks.map((hook) => hook.command));
|
|
55999
56305
|
for (const addition of review.additions) {
|
|
56000
56306
|
exactKeys2(addition, review.version === 1 ? ["event", "nativeHook"] : ["event", "nativeHook", "trustState"]);
|
|
56001
|
-
|
|
56307
|
+
need6(Object.hasOwn(review.version === 1 ? events : matchedEvents, addition.event), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56002
56308
|
const { event, nativeHook: hook } = addition;
|
|
56003
56309
|
exactKeys2(hook, nativeKeys);
|
|
56004
56310
|
const groups = expectedHooks.hooks[event] ?? [];
|
|
56005
|
-
|
|
56311
|
+
need6(Array.isArray(groups), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56006
56312
|
const key = `${options.nativeHooksPath}:${matchedEvents[event][1]}:${groups.length}:0`;
|
|
56007
|
-
|
|
56008
|
-
|
|
56313
|
+
need6(hook.key === key && !seen.has(key) && !options.recordedNativeHooks.some((originalHook) => originalHook.key === key) && !Object.hasOwn(expectedConfig.hooks.state, key), "RECONCILE_ADDITIONS_IDENTITY");
|
|
56314
|
+
need6(hook.eventName === matchedEvents[event][0] && hook.handlerType === "command" && (review.version === 2 ? v2Command(hook.command) : text2(hook.command, 16384)) && !commands.has(hook.command) && Number.isInteger(hook.timeoutSec) && hook.timeoutSec > 0 && hook.timeoutSec <= 60 && (event === "PreToolUse" ? text2(hook.matcher) : hook.matcher === null) && hook.async === false && hook.statusMessage === null && hook.additionalContextLimit === null && hook.sourcePath === options.nativeHooksPath && hook.source === "user" && hook.pluginId === null && hook.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash) && hook.enabled === true && hook.trustStatus === "trusted", "RECONCILE_ADDITIONS_IDENTITY");
|
|
56009
56315
|
let trustState = { enabled: true, trusted_hash: hook.currentHash };
|
|
56010
56316
|
if (review.version === 2) {
|
|
56011
56317
|
const state = addition.trustState;
|
|
56012
56318
|
exactKeys2(state, state && Object.hasOwn(state, "enabled") ? ["enabled", "trusted_hash"] : ["trusted_hash"]);
|
|
56013
|
-
|
|
56319
|
+
need6(state.trusted_hash === hook.currentHash && (!Object.hasOwn(state, "enabled") || state.enabled === true), "RECONCILE_ADDITIONS_TRUST_STATE");
|
|
56014
56320
|
trustState = { ...state };
|
|
56015
56321
|
}
|
|
56016
56322
|
seen.add(key);
|
|
@@ -56020,9 +56326,9 @@ function reviewNativeHookAdditions(options) {
|
|
|
56020
56326
|
expectedHooks.hooks[event] = [...groups, { ...event === "PreToolUse" ? { matcher: hook.matcher } : {}, hooks: [{ type: "command", command: hook.command, timeout: hook.timeoutSec }] }];
|
|
56021
56327
|
expectedConfig.hooks.state[key] = trustState;
|
|
56022
56328
|
}
|
|
56023
|
-
|
|
56329
|
+
need6(isDeepStrictEqual6(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56024
56330
|
const keys2 = added.map((hook) => hook.key);
|
|
56025
|
-
|
|
56331
|
+
need6(isDeepStrictEqual6(expectedConfig, structure(options.configCurrent.text)) && codexTrustReconcileWitness(producerConfig.text, keys2) === codexTrustReconcileWitness(options.configCurrent.text, keys2) && codexTrustUnmanagedSemantic(producerConfig.text, keys2) === codexTrustUnmanagedSemantic(options.configCurrent.text, keys2), "RECONCILE_ADDITIONS_CONFIG");
|
|
56026
56332
|
const inputs = [reviewFile, producerConfig, producerHooks, options.intent, options.policy, options.configBefore, options.configCurrent, options.hooksBefore, options.hooksCurrent];
|
|
56027
56333
|
const recheck = () => {
|
|
56028
56334
|
for (const input of inputs)
|
|
@@ -56044,8 +56350,8 @@ var init_codex_hook_additions_review = __esm(() => {
|
|
|
56044
56350
|
});
|
|
56045
56351
|
|
|
56046
56352
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
56047
|
-
import { createHash as
|
|
56048
|
-
import { isDeepStrictEqual as
|
|
56353
|
+
import { createHash as createHash17 } from "crypto";
|
|
56354
|
+
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
56049
56355
|
function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, additions) {
|
|
56050
56356
|
const journalHooks = hooks.map(({ key, currentHash }) => ({ key, currentHash }));
|
|
56051
56357
|
const additionStates = additions ? readReviewedAdditionTrustStates(additions, before2, current, journalHooks) : [];
|
|
@@ -56056,16 +56362,16 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
|
|
|
56056
56362
|
const raw = binding.sources.filter((source) => source.path === current.file && source.hashMode === "bytes");
|
|
56057
56363
|
if (!raw.some((source) => source.sha256 !== current.sha256))
|
|
56058
56364
|
return;
|
|
56059
|
-
|
|
56060
|
-
|
|
56365
|
+
need6(binding.agent === "codex" && binding.method === "reviewed" && raw.length === 1 && raw[0].sha256 === before2.sha256 && before2.sha256 !== current.sha256, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
|
|
56366
|
+
need6(raw[0].format === undefined && raw[0].fields === undefined && raw[0].managedPlugins === undefined, "RECONCILE_DISCOVERY_CONFIG_WITNESS");
|
|
56061
56367
|
const typed = binding.sources.filter((source) => source.path === current.file && source.format === "toml");
|
|
56062
|
-
|
|
56368
|
+
need6(typed.length === 1 && typed[0].hashMode === undefined && isDeepStrictEqual7(typed[0].fields, fields), "RECONCILE_DISCOVERY_TYPED_WITNESS");
|
|
56063
56369
|
const original = structure2(before2.text), actual = structure2(current.text);
|
|
56064
56370
|
const project = (value) => Object.fromEntries(fields.map((field2) => [field2, value[field2] ?? null]));
|
|
56065
|
-
const projection =
|
|
56066
|
-
|
|
56371
|
+
const projection = sha4(JSON.stringify(project(original)));
|
|
56372
|
+
need6(typed[0].sha256 === projection && sha4(JSON.stringify(project(actual))) === projection && isDeepStrictEqual7(project(original), project(actual)), "RECONCILE_DISCOVERY_TYPED_WITNESS");
|
|
56067
56373
|
const keys2 = [...journalHooks.map((hook) => hook.key), ...additionStates.map((hook) => hook.key)];
|
|
56068
|
-
|
|
56374
|
+
need6(journalHooks.length > 0 && keys2.length <= 100 && new Set(keys2).size === keys2.length && journalHooks.every((hook) => typeof hook.key === "string" && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash)), "RECONCILE_DISCOVERY_TRUST_WITNESS");
|
|
56069
56375
|
const expected = structuredClone(original);
|
|
56070
56376
|
expected.hooks ??= {};
|
|
56071
56377
|
expected.hooks.state ??= {};
|
|
@@ -56073,16 +56379,16 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
|
|
|
56073
56379
|
expected.hooks.state[hook.key] = { ...expected.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
56074
56380
|
for (const { key, state } of additionStates)
|
|
56075
56381
|
expected.hooks.state[key] = { ...state };
|
|
56076
|
-
|
|
56382
|
+
need6(isDeepStrictEqual7(expected, actual) && codexTrustReconcileWitness(before2.text, keys2) === codexTrustReconcileWitness(current.text, keys2) && codexTrustUnmanagedSemantic(before2.text, keys2) === codexTrustUnmanagedSemantic(current.text, keys2), "RECONCILE_DISCOVERY_TRUST_WITNESS");
|
|
56077
56383
|
unchanged2(before2);
|
|
56078
56384
|
unchanged2(current);
|
|
56079
|
-
const witness = Object.freeze({ configPath: current.file, beforeSha256: before2.sha256, currentSha256: current.sha256, discoverySha256:
|
|
56385
|
+
const witness = Object.freeze({ configPath: current.file, beforeSha256: before2.sha256, currentSha256: current.sha256, discoverySha256: sha4(JSON.stringify(binding)), typedProjectionSha256: projection });
|
|
56080
56386
|
proofs2.set(witness, { before: before2, current, recheckAdditions });
|
|
56081
56387
|
return witness;
|
|
56082
56388
|
}
|
|
56083
56389
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
56084
56390
|
const proof = proofs2.get(witness);
|
|
56085
|
-
|
|
56391
|
+
need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
56086
56392
|
unchanged2(proof.before);
|
|
56087
56393
|
unchanged2(proof.current);
|
|
56088
56394
|
proof.recheckAdditions();
|
|
@@ -56091,23 +56397,24 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
56091
56397
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
56092
56398
|
return false;
|
|
56093
56399
|
assertCodexHookDiscoveryRecovery(binding, witness);
|
|
56094
|
-
|
|
56400
|
+
need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
56095
56401
|
return true;
|
|
56096
56402
|
}
|
|
56097
|
-
var
|
|
56403
|
+
var sha4 = (text3) => createHash17("sha256").update(text3).digest("hex"), fields, structure2 = (text3) => JSON.parse(JSON.stringify(Bun.TOML.parse(text3))), proofs2;
|
|
56098
56404
|
var init_codex_hook_discovery_recovery = __esm(() => {
|
|
56099
56405
|
init_codex_hook_trust_files();
|
|
56100
56406
|
init_codex_hook_trust_layout();
|
|
56101
56407
|
init_codex_hook_additions_review();
|
|
56102
|
-
|
|
56408
|
+
init_codex_settings_witness();
|
|
56409
|
+
fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
|
|
56103
56410
|
proofs2 = new WeakMap;
|
|
56104
56411
|
});
|
|
56105
56412
|
|
|
56106
56413
|
// src/lib/agent-discovery.ts
|
|
56107
|
-
import { createHash as
|
|
56414
|
+
import { createHash as createHash18 } from "crypto";
|
|
56108
56415
|
import { lstatSync as lstatSync21, readFileSync as readFileSync18, readdirSync as readdirSync11, realpathSync as realpathSync7 } from "fs";
|
|
56109
|
-
import { basename as
|
|
56110
|
-
import { isDeepStrictEqual as
|
|
56416
|
+
import { basename as basename9, dirname as dirname17, isAbsolute as isAbsolute17, join as join32, resolve as resolve16, sep as sep6 } from "path";
|
|
56417
|
+
import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
|
|
56111
56418
|
function parseConfig(text3, path, toml = false) {
|
|
56112
56419
|
try {
|
|
56113
56420
|
const value = toml ? Bun.TOML.parse(text3) : JSON.parse(text3);
|
|
@@ -56139,14 +56446,26 @@ function read2(path, changes) {
|
|
|
56139
56446
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
56140
56447
|
return readFileSync18(path, "utf8");
|
|
56141
56448
|
}
|
|
56449
|
+
function projectNativeDiscoveryFields(text3, format, fields2, path) {
|
|
56450
|
+
const object5 = format === "yaml" ? parseHermesConfig(text3) : parseConfig(text3, path, format === "toml");
|
|
56451
|
+
if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
|
|
56452
|
+
throw new Error("Expected native discovery configuration object");
|
|
56453
|
+
return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object5[field2] ?? null]))));
|
|
56454
|
+
}
|
|
56455
|
+
function isSupersededCodexProjection(agent, configPath, source) {
|
|
56456
|
+
return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual8(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
|
|
56457
|
+
}
|
|
56458
|
+
function supersedesCodexProjection(sources, configPath) {
|
|
56459
|
+
return sources.some((source) => source.path === configPath && source.hashMode === "codex-settings-v2");
|
|
56460
|
+
}
|
|
56142
56461
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
56143
|
-
if (source.hashMode === "codex-settings-v1") {
|
|
56462
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") {
|
|
56144
56463
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
56145
56464
|
throw new Error("Codex settings witnesses require exact typed metadata");
|
|
56146
|
-
const current = captureCodexSettings(source.path, budget).sha256;
|
|
56465
|
+
const current = (source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
|
|
56147
56466
|
if (current !== source.sha256)
|
|
56148
56467
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
56149
|
-
return changes?.has(source.path) ? hashCodexSettingsReplacement(changes.get(source.path), budget) : current;
|
|
56468
|
+
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
|
|
56150
56469
|
}
|
|
56151
56470
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
56152
56471
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
@@ -56185,10 +56504,7 @@ function projected(source, changes, budget = discoveryByteBudget()) {
|
|
|
56185
56504
|
return null;
|
|
56186
56505
|
if (!source.format)
|
|
56187
56506
|
return digest(text3);
|
|
56188
|
-
|
|
56189
|
-
if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
|
|
56190
|
-
throw new Error("Expected native discovery configuration object");
|
|
56191
|
-
return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field2) => [field2, object5[field2] ?? null]))));
|
|
56507
|
+
return projectNativeDiscoveryFields(text3, source.format, source.fields ?? [], source.path);
|
|
56192
56508
|
}
|
|
56193
56509
|
function verifyAgentDiscovery(binding, codexRecovery) {
|
|
56194
56510
|
if (codexRecovery)
|
|
@@ -56216,9 +56532,9 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
56216
56532
|
safe4(root2);
|
|
56217
56533
|
}
|
|
56218
56534
|
function assertMarketplaceBinding(binding, source) {
|
|
56219
|
-
if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" ||
|
|
56535
|
+
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
56220
56536
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
56221
|
-
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" ||
|
|
56537
|
+
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename9(source.path) !== "settings.json"))
|
|
56222
56538
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
56223
56539
|
if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
56224
56540
|
throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
|
|
@@ -56233,8 +56549,8 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
56233
56549
|
return { ...source, sha256: sha2562 };
|
|
56234
56550
|
}) };
|
|
56235
56551
|
}
|
|
56236
|
-
function assertProjectDiscovery(agent, directories, home,
|
|
56237
|
-
const homeConfigPath =
|
|
56552
|
+
function assertProjectDiscovery(agent, directories, home, canonical6 = resolve16, reviewed) {
|
|
56553
|
+
const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
|
|
56238
56554
|
for (const directory of directories) {
|
|
56239
56555
|
const isHome = resolve16(directory) === resolve16(home);
|
|
56240
56556
|
if (agent === "hermes") {
|
|
@@ -56245,7 +56561,7 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve16
|
|
|
56245
56561
|
continue;
|
|
56246
56562
|
const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : [".cursor/hooks.json"];
|
|
56247
56563
|
for (const suffix of paths) {
|
|
56248
|
-
const path =
|
|
56564
|
+
const path = canonical6(join32(directory, suffix)), raw = read2(path);
|
|
56249
56565
|
if (raw === null)
|
|
56250
56566
|
continue;
|
|
56251
56567
|
const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
|
|
@@ -56261,14 +56577,14 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve16
|
|
|
56261
56577
|
const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
|
|
56262
56578
|
if (exact && projected(exact) === exact.sha256 && managed.length) {
|
|
56263
56579
|
const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
|
|
56264
|
-
admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath ===
|
|
56580
|
+
admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath === canonical6(directory))));
|
|
56265
56581
|
}
|
|
56266
56582
|
}
|
|
56267
56583
|
if (keys2.some((key) => config[key] !== undefined && !(key === "enabledPlugins" && admittedPlugins)) || config.disableAllHooks === true || config.disableBundledSkills === false || config.hooksConfig?.enabled === false || config.permission?.skill !== undefined || config.permissions?.deny?.some((rule) => typeof rule === "string" && /^Skill(?:\(|$)/.test(rule)))
|
|
56268
56584
|
throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
|
|
56269
56585
|
}
|
|
56270
56586
|
if (agent === "claude") {
|
|
56271
|
-
const commands =
|
|
56587
|
+
const commands = canonical6(join32(directory, ".claude/commands"));
|
|
56272
56588
|
safe4(commands);
|
|
56273
56589
|
if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync11(commands).length)
|
|
56274
56590
|
throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
|
|
@@ -56279,10 +56595,10 @@ function agentDiscoveryConfigPath(home, agent) {
|
|
|
56279
56595
|
return join32(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
56280
56596
|
}
|
|
56281
56597
|
function resolveAgentDiscovery(options) {
|
|
56282
|
-
const
|
|
56598
|
+
const canonical6 = options.canonical ?? resolve16, home = resolve16(options.home), agent = options.agent;
|
|
56283
56599
|
const sources = [], roots = new Set, builtinNames = [];
|
|
56284
56600
|
const witness = (path, format, fields2) => {
|
|
56285
|
-
const source = { path:
|
|
56601
|
+
const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
|
|
56286
56602
|
source.sha256 = projected(source);
|
|
56287
56603
|
sources.push(source);
|
|
56288
56604
|
return read2(source.path);
|
|
@@ -56290,7 +56606,7 @@ function resolveAgentDiscovery(options) {
|
|
|
56290
56606
|
if (agent === "hermes")
|
|
56291
56607
|
assertHermesEnvironment(home);
|
|
56292
56608
|
const configPath = agentDiscoveryConfigPath(home, agent);
|
|
56293
|
-
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [
|
|
56609
|
+
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : ["version"]);
|
|
56294
56610
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
56295
56611
|
const unresolved = (detail) => {
|
|
56296
56612
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
@@ -56349,11 +56665,12 @@ function resolveAgentDiscovery(options) {
|
|
|
56349
56665
|
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
56350
56666
|
unresolved("invalid retained review");
|
|
56351
56667
|
verifyAgentDiscovery(retained);
|
|
56352
|
-
if (!retained.sources.some((source) => source.path ===
|
|
56668
|
+
if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
|
|
56353
56669
|
unresolved("retained review is missing the full agent configuration source");
|
|
56354
|
-
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "codex-settings-v1")) && source.path !==
|
|
56670
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2"))) && source.path !== canonical6(configPath)))
|
|
56355
56671
|
unresolved("retained settings witness names another configuration source");
|
|
56356
|
-
|
|
56672
|
+
const covered = supersedesCodexProjection(retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
|
|
56673
|
+
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual8(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
56357
56674
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
56358
56675
|
return retained;
|
|
56359
56676
|
}
|
|
@@ -56367,14 +56684,15 @@ function resolveAgentDiscovery(options) {
|
|
|
56367
56684
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
56368
56685
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
56369
56686
|
verifyAgentDiscovery(supplied);
|
|
56370
|
-
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "codex-settings-v1")) && source.path !==
|
|
56687
|
+
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2"))) && source.path !== canonical6(configPath)))
|
|
56371
56688
|
throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
|
|
56372
|
-
if (!review.sources.some((source) => source.path ===
|
|
56689
|
+
if (!review.sources.some((source) => source.path === canonical6(configPath)))
|
|
56373
56690
|
throw new Error("Discovery review must include the agent configuration source");
|
|
56374
|
-
|
|
56691
|
+
const reviewedSources = supersedesCodexProjection(review.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
|
|
56692
|
+
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...reviewedSources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
|
|
56375
56693
|
}
|
|
56376
56694
|
function plugin(root2) {
|
|
56377
|
-
root2 =
|
|
56695
|
+
root2 = canonical6(root2);
|
|
56378
56696
|
safe4(root2);
|
|
56379
56697
|
const manifestPath = join32(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
56380
56698
|
const raw = witness(manifestPath);
|
|
@@ -56406,7 +56724,7 @@ function resolveAgentDiscovery(options) {
|
|
|
56406
56724
|
if (agent === "claude") {
|
|
56407
56725
|
const registrations = witness(join32(home, ".claude/plugins/installed_plugins.json"));
|
|
56408
56726
|
const installed = registrations === null ? {} : parseConfig(registrations, join32(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
56409
|
-
const commands =
|
|
56727
|
+
const commands = canonical6(join32(home, ".claude/commands"));
|
|
56410
56728
|
safe4(commands);
|
|
56411
56729
|
if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync11(commands).length)
|
|
56412
56730
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
@@ -56480,9 +56798,9 @@ function resolveAgentDiscovery(options) {
|
|
|
56480
56798
|
roots.add(join32(root2, "skills"));
|
|
56481
56799
|
}
|
|
56482
56800
|
}
|
|
56483
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")].map((path) =>
|
|
56801
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")].map((path) => canonical6(path))) } : {} };
|
|
56484
56802
|
}
|
|
56485
|
-
var digest = (text3) =>
|
|
56803
|
+
var digest = (text3) => createHash18("sha256").update(text3).digest("hex");
|
|
56486
56804
|
var init_agent_discovery = __esm(() => {
|
|
56487
56805
|
init_agent_policy_limits();
|
|
56488
56806
|
init_agent_hermes();
|
|
@@ -56501,7 +56819,7 @@ var init_agent_discovery = __esm(() => {
|
|
|
56501
56819
|
// src/lib/authoring-path.ts
|
|
56502
56820
|
import { lstatSync as lstatSync22, realpathSync as realpathSync8 } from "fs";
|
|
56503
56821
|
import { homedir as homedir7 } from "os";
|
|
56504
|
-
import { basename as
|
|
56822
|
+
import { basename as basename10, dirname as dirname18, join as join33, resolve as resolve17, sep as sep7 } from "path";
|
|
56505
56823
|
function pathParts(path) {
|
|
56506
56824
|
return resolve17(path).split(sep7).filter(Boolean);
|
|
56507
56825
|
}
|
|
@@ -56520,7 +56838,7 @@ function physicalPath(path) {
|
|
|
56520
56838
|
let cursor = resolve17(path);
|
|
56521
56839
|
const suffix = [];
|
|
56522
56840
|
while (!lstatSync22(cursor, { throwIfNoEntry: false })) {
|
|
56523
|
-
suffix.unshift(
|
|
56841
|
+
suffix.unshift(basename10(cursor));
|
|
56524
56842
|
const parent = dirname18(cursor);
|
|
56525
56843
|
if (parent === cursor)
|
|
56526
56844
|
return resolve17(path);
|
|
@@ -56615,7 +56933,7 @@ import {
|
|
|
56615
56933
|
statSync as statSync11,
|
|
56616
56934
|
writeFileSync as writeFileSync9
|
|
56617
56935
|
} from "fs";
|
|
56618
|
-
import { basename as
|
|
56936
|
+
import { basename as basename11, dirname as dirname19, isAbsolute as isAbsolute18, join as join34, normalize as normalize4, relative as relative6, resolve as resolve18, sep as sep8 } from "path";
|
|
56619
56937
|
function getPortableSkillsRoot(options = {}) {
|
|
56620
56938
|
return resolvePortableSkillsRoot(options, true);
|
|
56621
56939
|
}
|
|
@@ -56713,7 +57031,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
56713
57031
|
return { name: skillName, path: skillPath, manifest, created: true };
|
|
56714
57032
|
}
|
|
56715
57033
|
function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
56716
|
-
const absoluteSource =
|
|
57034
|
+
const absoluteSource = normalize4(sourceDir);
|
|
56717
57035
|
if (!existsSync18(absoluteSource) || !statSync11(absoluteSource).isDirectory()) {
|
|
56718
57036
|
throw new Error(`Import directory not found: ${sourceDir}`);
|
|
56719
57037
|
}
|
|
@@ -56763,7 +57081,7 @@ function physicalImportPath(path) {
|
|
|
56763
57081
|
let ancestor = resolve18(path);
|
|
56764
57082
|
const suffix = [];
|
|
56765
57083
|
while (!existsSync18(ancestor)) {
|
|
56766
|
-
suffix.unshift(
|
|
57084
|
+
suffix.unshift(basename11(ancestor));
|
|
56767
57085
|
ancestor = dirname19(ancestor);
|
|
56768
57086
|
}
|
|
56769
57087
|
return join34(realpathSync9(ancestor), ...suffix);
|
|
@@ -56772,7 +57090,7 @@ function assertDisjointImportPaths(source, destination) {
|
|
|
56772
57090
|
const sourcePath = realpathSync9(source);
|
|
56773
57091
|
const destinationPath = physicalImportPath(destination);
|
|
56774
57092
|
const contains2 = (parent, child) => {
|
|
56775
|
-
const path =
|
|
57093
|
+
const path = relative6(parent, child);
|
|
56776
57094
|
return path === "" || !isAbsolute18(path) && path !== ".." && !path.startsWith(`..${sep8}`);
|
|
56777
57095
|
};
|
|
56778
57096
|
if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
|
|
@@ -56780,7 +57098,7 @@ function assertDisjointImportPaths(source, destination) {
|
|
|
56780
57098
|
}
|
|
56781
57099
|
}
|
|
56782
57100
|
function portPortableSkill(sourcePath, options = {}) {
|
|
56783
|
-
const absoluteSource =
|
|
57101
|
+
const absoluteSource = normalize4(sourcePath);
|
|
56784
57102
|
if (!existsSync18(absoluteSource) || !statSync11(absoluteSource).isDirectory()) {
|
|
56785
57103
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
56786
57104
|
}
|
|
@@ -56788,8 +57106,8 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
56788
57106
|
const explicitName = options.name != null;
|
|
56789
57107
|
const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
|
|
56790
57108
|
if (isOfficialSkillName(skillName) && !options.allowShadow) {
|
|
56791
|
-
const sourceSlug = safeNormalizeName(
|
|
56792
|
-
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${
|
|
57109
|
+
const sourceSlug = safeNormalizeName(basename11(absoluteSource));
|
|
57110
|
+
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.`;
|
|
56793
57111
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
56794
57112
|
}
|
|
56795
57113
|
const readOnlyDestination = join34(resolvePortableSkillsRoot(options, false), skillName);
|
|
@@ -56992,7 +57310,7 @@ function safeIsDirectory(path) {
|
|
|
56992
57310
|
function isSafeRelativePath2(value) {
|
|
56993
57311
|
if (!value.trim() || isAbsolute18(value))
|
|
56994
57312
|
return false;
|
|
56995
|
-
const normalized =
|
|
57313
|
+
const normalized = normalize4(value).replace(/\\/g, "/");
|
|
56996
57314
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
56997
57315
|
}
|
|
56998
57316
|
function add4(target, code, message) {
|
|
@@ -58998,13 +59316,13 @@ var require_cli_spinners = __commonJS((exports, module) => {
|
|
|
58998
59316
|
|
|
58999
59317
|
// src/lib/auth-store.ts
|
|
59000
59318
|
import { chmodSync as chmodSync2, existsSync as existsSync20, mkdirSync as mkdirSync11, readFileSync as readFileSync20, renameSync as renameSync7, statSync as statSync12, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
|
|
59001
|
-
import { basename as
|
|
59319
|
+
import { basename as basename12, dirname as dirname20, join as join36 } from "path";
|
|
59002
59320
|
function getAuthFilePath(env3 = process.env) {
|
|
59003
59321
|
return skillsCredentialFilePath(env3);
|
|
59004
59322
|
}
|
|
59005
59323
|
function getIdentityFilePath(env3 = process.env) {
|
|
59006
59324
|
const file = skillsCredentialFilePath(env3);
|
|
59007
|
-
return join36(dirname20(file),
|
|
59325
|
+
return join36(dirname20(file), basename12(file).replace(/^credentials/, "identity") + ".json");
|
|
59008
59326
|
}
|
|
59009
59327
|
function getAuthIdentity(env3 = process.env) {
|
|
59010
59328
|
return readIdentity(env3);
|
|
@@ -59418,7 +59736,7 @@ var init_remote_workspace_selection = __esm(() => {
|
|
|
59418
59736
|
});
|
|
59419
59737
|
|
|
59420
59738
|
// src/lib/remote-private-publications.ts
|
|
59421
|
-
import { createHash as
|
|
59739
|
+
import { createHash as createHash19 } from "crypto";
|
|
59422
59740
|
function checkedPublicationDeclaration(value) {
|
|
59423
59741
|
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 || !hash3(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
|
|
59424
59742
|
return bad();
|
|
@@ -59679,7 +59997,7 @@ var PRIVATE_PUBLICATION_MAX_BYTES, failures, PrivatePublicationError, bad = () =
|
|
|
59679
59997
|
throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
|
|
59680
59998
|
}, invalid3 = () => {
|
|
59681
59999
|
throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
|
|
59682
|
-
}, 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), hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), record4 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v, keys2) => record4(v) && Object.keys(v).sort().join(",") === keys2.sort().join(","), date = (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) =>
|
|
60000
|
+
}, 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), hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), record4 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v, keys2) => record4(v) && Object.keys(v).sort().join(",") === keys2.sort().join(","), date = (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) => createHash19("sha256").update(bytes).digest("hex");
|
|
59683
60001
|
var init_remote_private_publications = __esm(() => {
|
|
59684
60002
|
init_fleet_credentials();
|
|
59685
60003
|
init_remote_workspace_selection();
|
|
@@ -63218,15 +63536,15 @@ var init_schemas = __esm(() => {
|
|
|
63218
63536
|
} catch (_err) {}
|
|
63219
63537
|
}
|
|
63220
63538
|
const input = payload.value;
|
|
63221
|
-
const
|
|
63222
|
-
const isValidDate =
|
|
63539
|
+
const isDate3 = input instanceof Date;
|
|
63540
|
+
const isValidDate = isDate3 && !Number.isNaN(input.getTime());
|
|
63223
63541
|
if (isValidDate)
|
|
63224
63542
|
return payload;
|
|
63225
63543
|
payload.issues.push({
|
|
63226
63544
|
expected: "date",
|
|
63227
63545
|
code: "invalid_type",
|
|
63228
63546
|
input,
|
|
63229
|
-
...
|
|
63547
|
+
...isDate3 ? { received: "Invalid Date" } : {},
|
|
63230
63548
|
inst
|
|
63231
63549
|
});
|
|
63232
63550
|
return payload;
|
|
@@ -79068,7 +79386,7 @@ var init_v4 = __esm(() => {
|
|
|
79068
79386
|
});
|
|
79069
79387
|
|
|
79070
79388
|
// src/lib/canonical-json.ts
|
|
79071
|
-
import { createHash as
|
|
79389
|
+
import { createHash as createHash20 } from "crypto";
|
|
79072
79390
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
79073
79391
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
79074
79392
|
}
|
|
@@ -79112,7 +79430,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
79112
79430
|
return visit3(value, 0);
|
|
79113
79431
|
}
|
|
79114
79432
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
79115
|
-
return
|
|
79433
|
+
return createHash20("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
79116
79434
|
}
|
|
79117
79435
|
var init_canonical_json = () => {};
|
|
79118
79436
|
|
|
@@ -79447,7 +79765,7 @@ var init_remote_recurring = __esm(() => {
|
|
|
79447
79765
|
|
|
79448
79766
|
// src/lib/remote-run-contract.ts
|
|
79449
79767
|
function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
79450
|
-
const record8 =
|
|
79768
|
+
const record8 = isRecord4(payload) ? payload : {};
|
|
79451
79769
|
return {
|
|
79452
79770
|
contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
|
|
79453
79771
|
...pickString(record8, "id"),
|
|
@@ -79469,7 +79787,7 @@ function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
|
79469
79787
|
...hasOwn(record8, "details") ? { details: record8.details } : {}
|
|
79470
79788
|
};
|
|
79471
79789
|
}
|
|
79472
|
-
function
|
|
79790
|
+
function isRecord4(value) {
|
|
79473
79791
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
79474
79792
|
}
|
|
79475
79793
|
function hasOwn(record8, key) {
|
|
@@ -79608,7 +79926,7 @@ var init_remote_credit_checkout = __esm(() => {
|
|
|
79608
79926
|
|
|
79609
79927
|
// src/lib/remote-profile.ts
|
|
79610
79928
|
function customerNamePatch(input2, field2) {
|
|
79611
|
-
if (!
|
|
79929
|
+
if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
|
|
79612
79930
|
throw new Error("Provide only the requested name field.");
|
|
79613
79931
|
const value = input2[field2];
|
|
79614
79932
|
if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
|
|
@@ -79616,22 +79934,22 @@ function customerNamePatch(input2, field2) {
|
|
|
79616
79934
|
}
|
|
79617
79935
|
return { [field2]: value.trim() };
|
|
79618
79936
|
}
|
|
79619
|
-
function
|
|
79937
|
+
function isRecord5(value) {
|
|
79620
79938
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
79621
79939
|
}
|
|
79622
79940
|
function string4(value) {
|
|
79623
79941
|
return typeof value === "string" && value.length > 0;
|
|
79624
79942
|
}
|
|
79625
79943
|
function parseUpdatedProfile(value) {
|
|
79626
|
-
const user =
|
|
79627
|
-
if (!
|
|
79944
|
+
const user = isRecord5(value) && value.user;
|
|
79945
|
+
if (!isRecord5(user) || !string4(user.id) || !string4(user.email) || !(user.displayName === null || typeof user.displayName === "string") || typeof user.role !== "string" || !["owner", "admin", "member", "viewer"].includes(user.role)) {
|
|
79628
79946
|
throw new Error("The server returned an invalid account profile.");
|
|
79629
79947
|
}
|
|
79630
79948
|
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
79631
79949
|
}
|
|
79632
79950
|
function parseUpdatedWorkspace(value) {
|
|
79633
|
-
const organization2 =
|
|
79634
|
-
if (!
|
|
79951
|
+
const organization2 = isRecord5(value) && value.organization;
|
|
79952
|
+
if (!isRecord5(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
|
|
79635
79953
|
throw new Error("The server returned an invalid workspace.");
|
|
79636
79954
|
}
|
|
79637
79955
|
return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
|
|
@@ -80813,7 +81131,7 @@ async function requestAuthApi(instance, path, options) {
|
|
|
80813
81131
|
const text4 = await res.text();
|
|
80814
81132
|
const body = text4 ? parseJsonBody(text4) : {};
|
|
80815
81133
|
if (!res.ok) {
|
|
80816
|
-
const record9 =
|
|
81134
|
+
const record9 = isRecord6(body) ? body : {};
|
|
80817
81135
|
const detail = typeof record9.detail === "string" ? record9.detail : undefined;
|
|
80818
81136
|
const error61 = typeof record9.error === "string" ? record9.error : undefined;
|
|
80819
81137
|
const code = typeof record9.code === "string" ? record9.code : undefined;
|
|
@@ -80841,7 +81159,7 @@ function condenseErrorBody(text4) {
|
|
|
80841
81159
|
return collapsed;
|
|
80842
81160
|
return `${collapsed.slice(0, MAX_ERROR_DETAIL_LENGTH - 1).trimEnd()}\u2026`;
|
|
80843
81161
|
}
|
|
80844
|
-
function
|
|
81162
|
+
function isRecord6(value) {
|
|
80845
81163
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
80846
81164
|
}
|
|
80847
81165
|
|
|
@@ -81026,10 +81344,10 @@ var init_remote_auth = __esm(() => {
|
|
|
81026
81344
|
});
|
|
81027
81345
|
|
|
81028
81346
|
// src/lib/sign-in.ts
|
|
81029
|
-
import { createHash as
|
|
81347
|
+
import { createHash as createHash21 } from "crypto";
|
|
81030
81348
|
import { chmodSync as chmodSync3, mkdirSync as mkdirSync12, readFileSync as readFileSync21, readdirSync as readdirSync14, renameSync as renameSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync11 } from "fs";
|
|
81031
|
-
import { basename as
|
|
81032
|
-
function
|
|
81349
|
+
import { basename as basename13, dirname as dirname21, join as join37 } from "path";
|
|
81350
|
+
function isRecord7(value) {
|
|
81033
81351
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
81034
81352
|
}
|
|
81035
81353
|
function boundedText(value, max2 = 2048) {
|
|
@@ -81054,7 +81372,7 @@ function clampIntervalMs(seconds) {
|
|
|
81054
81372
|
return Math.min(MAX_INTERVAL_MS, Math.max(MIN_INTERVAL_MS, Math.round(seconds * 1000)));
|
|
81055
81373
|
}
|
|
81056
81374
|
function parseDeviceAuthorizationStart(body) {
|
|
81057
|
-
const record9 =
|
|
81375
|
+
const record9 = isRecord7(body) ? body : {};
|
|
81058
81376
|
if (typeof record9.error === "string") {
|
|
81059
81377
|
throw new Error(typeof record9.detail === "string" ? record9.detail : record9.error);
|
|
81060
81378
|
}
|
|
@@ -81088,7 +81406,7 @@ function pollDeviceToken(origin, deviceCode) {
|
|
|
81088
81406
|
});
|
|
81089
81407
|
}
|
|
81090
81408
|
function deviceErrorCode(error61) {
|
|
81091
|
-
if (!(error61 instanceof HostedApiError) && !(
|
|
81409
|
+
if (!(error61 instanceof HostedApiError) && !(isRecord7(error61) && error61.name === "HostedApiError"))
|
|
81092
81410
|
return;
|
|
81093
81411
|
const failure = error61;
|
|
81094
81412
|
const named = [failure.code, failure.detail, failure.message].find((value) => value === "slow_down" || value === "authorization_pending" || value === "expired_token" || value === "invalid_device_code" || value === "access_denied");
|
|
@@ -81149,7 +81467,7 @@ async function pollDeviceAuthorization(input2) {
|
|
|
81149
81467
|
throw error61;
|
|
81150
81468
|
}
|
|
81151
81469
|
}
|
|
81152
|
-
const record9 =
|
|
81470
|
+
const record9 = isRecord7(body) ? body : {};
|
|
81153
81471
|
const code = typeof record9.error === "string" ? record9.error : undefined;
|
|
81154
81472
|
if (code === "authorization_pending" || code === undefined && record9.status === "pending") {
|
|
81155
81473
|
await wait();
|
|
@@ -81173,11 +81491,11 @@ async function pollDeviceAuthorization(input2) {
|
|
|
81173
81491
|
}
|
|
81174
81492
|
function pendingDeviceSignInStem(env3) {
|
|
81175
81493
|
const file2 = skillsCredentialFilePath(env3);
|
|
81176
|
-
return { dir: dirname21(file2), stem:
|
|
81494
|
+
return { dir: dirname21(file2), stem: basename13(file2).replace(/^credentials/, "device-login") };
|
|
81177
81495
|
}
|
|
81178
81496
|
function pendingDeviceSignInPath(origin, env3 = process.env) {
|
|
81179
81497
|
const { dir, stem } = pendingDeviceSignInStem(env3);
|
|
81180
|
-
return join37(dir, `${stem}-${
|
|
81498
|
+
return join37(dir, `${stem}-${createHash21("sha256").update(origin).digest("hex").slice(0, 16)}.json`);
|
|
81181
81499
|
}
|
|
81182
81500
|
function savePendingDeviceSignIn(origin, start, env3 = process.env, now3 = Date.now()) {
|
|
81183
81501
|
const path = pendingDeviceSignInPath(origin, env3);
|
|
@@ -81207,7 +81525,7 @@ function loadPendingDeviceSignIn(origin, env3 = process.env, now3 = Date.now())
|
|
|
81207
81525
|
} catch {
|
|
81208
81526
|
return null;
|
|
81209
81527
|
}
|
|
81210
|
-
if (!
|
|
81528
|
+
if (!isRecord7(parsed))
|
|
81211
81529
|
return null;
|
|
81212
81530
|
const deviceCode = boundedText(parsed.deviceCode, 512);
|
|
81213
81531
|
const userCode = boundedText(parsed.userCode, 64);
|
|
@@ -81249,7 +81567,7 @@ function pendingDeviceSignInOrigins(env3 = process.env, now3 = Date.now()) {
|
|
|
81249
81567
|
continue;
|
|
81250
81568
|
try {
|
|
81251
81569
|
const parsed = JSON.parse(readFileSync21(join37(location.dir, entry), "utf8"));
|
|
81252
|
-
if (
|
|
81570
|
+
if (isRecord7(parsed) && typeof parsed.origin === "string" && Number(parsed.expiresAt) > now3 && pendingDeviceSignInPath(parsed.origin, env3) === join37(location.dir, entry))
|
|
81253
81571
|
origins.push(parsed.origin);
|
|
81254
81572
|
} catch {}
|
|
81255
81573
|
}
|
|
@@ -81291,7 +81609,7 @@ async function persistSignIn(result2, origin, env3 = process.env, issuedBy = "si
|
|
|
81291
81609
|
headers: { Authorization: `Bearer ${result2.token}` },
|
|
81292
81610
|
body: JSON.stringify({ name: "cli" })
|
|
81293
81611
|
});
|
|
81294
|
-
apiKey =
|
|
81612
|
+
apiKey = isRecord7(created) && typeof created.key === "string" && created.key.trim() ? created.key : undefined;
|
|
81295
81613
|
}
|
|
81296
81614
|
if (!apiKey)
|
|
81297
81615
|
throw new Error("Sign-in succeeded but the server returned no API key");
|
|
@@ -81330,7 +81648,7 @@ async function revokeStoredKey(origin, apiKey) {
|
|
|
81330
81648
|
}
|
|
81331
81649
|
return { outcome: "failed", detail: "the server could not be reached" };
|
|
81332
81650
|
}
|
|
81333
|
-
if (
|
|
81651
|
+
if (isRecord7(body) && body.revoked === true)
|
|
81334
81652
|
return { outcome: "revoked" };
|
|
81335
81653
|
return { outcome: "not_revoked", detail: "the server answered without confirming revocation" };
|
|
81336
81654
|
}
|
|
@@ -81454,10 +81772,10 @@ async function readSignedInAccount(env3 = process.env) {
|
|
|
81454
81772
|
const body = await new RemoteSkillsAuthClient(connection.apiOrigin).request("/api/auth/whoami", {
|
|
81455
81773
|
headers: { Authorization: `Bearer ${connection.apiKey}` }
|
|
81456
81774
|
});
|
|
81457
|
-
const record9 =
|
|
81458
|
-
const data =
|
|
81459
|
-
const user =
|
|
81460
|
-
const organization2 =
|
|
81775
|
+
const record9 = isRecord7(body) ? body : {};
|
|
81776
|
+
const data = isRecord7(record9.data) ? record9.data : {};
|
|
81777
|
+
const user = isRecord7(record9.user) ? record9.user : isRecord7(data.user) ? data.user : {};
|
|
81778
|
+
const organization2 = isRecord7(record9.organization) ? record9.organization : isRecord7(data.organization) ? data.organization : {};
|
|
81461
81779
|
const email4 = boundedText(user.email, 320);
|
|
81462
81780
|
const org = boundedText(organization2.slug, 200) ?? boundedText(organization2.name, 200);
|
|
81463
81781
|
return { ...base2, ...email4 ? { email: email4 } : {}, ...org ? { organization: org } : {} };
|
|
@@ -81872,7 +82190,7 @@ function optionPrefix(program2, args) {
|
|
|
81872
82190
|
}
|
|
81873
82191
|
|
|
81874
82192
|
// src/lib/revision.ts
|
|
81875
|
-
import { createHash as
|
|
82193
|
+
import { createHash as createHash22 } from "crypto";
|
|
81876
82194
|
function revisionIdOf(content) {
|
|
81877
82195
|
const canonicalContent = {
|
|
81878
82196
|
slug: content.slug,
|
|
@@ -81892,8 +82210,8 @@ function revisionIdOf(content) {
|
|
|
81892
82210
|
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
81893
82211
|
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
81894
82212
|
}
|
|
81895
|
-
const
|
|
81896
|
-
return
|
|
82213
|
+
const canonical6 = JSON.stringify(canonicalContent);
|
|
82214
|
+
return createHash22("sha256").update(canonical6).digest("hex");
|
|
81897
82215
|
}
|
|
81898
82216
|
var REVISION_ID_PATTERN;
|
|
81899
82217
|
var init_revision = __esm(() => {
|
|
@@ -82342,7 +82660,7 @@ var init_pull = __esm(() => {
|
|
|
82342
82660
|
});
|
|
82343
82661
|
|
|
82344
82662
|
// src/lib/skill-context.ts
|
|
82345
|
-
import { createHash as
|
|
82663
|
+
import { createHash as createHash23 } from "crypto";
|
|
82346
82664
|
import { resolve as resolve19 } from "path";
|
|
82347
82665
|
function words2(value) {
|
|
82348
82666
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
@@ -82358,9 +82676,9 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
82358
82676
|
if (prompt.length > 128 * 1024 || (input2.paths?.length ?? 0) > 100 || (input2.skills?.length ?? 0) > 100) {
|
|
82359
82677
|
throw new SkillSelectionError("CONTEXT_INPUT_TOO_LARGE", "The Skills context input exceeds its size limit.");
|
|
82360
82678
|
}
|
|
82361
|
-
const maxChars = options.maxChars
|
|
82679
|
+
const maxChars = options.maxChars;
|
|
82362
82680
|
const maxSkills = options.maxSkills ?? 3;
|
|
82363
|
-
if (!Number.isInteger(maxChars) || maxChars < 512 || maxChars > 64000 || !Number.isInteger(maxSkills) || maxSkills < 1 || maxSkills > 20) {
|
|
82681
|
+
if (maxChars !== undefined && (!Number.isInteger(maxChars) || maxChars < 512 || maxChars > 64000) || !Number.isInteger(maxSkills) || maxSkills < 1 || maxSkills > 20) {
|
|
82364
82682
|
throw new SkillSelectionError("INVALID_CONTEXT_BUDGET", "Context limits must be 512\u201364000 characters and 1\u201320 skills.");
|
|
82365
82683
|
}
|
|
82366
82684
|
const profileId = input2.profileId ?? "default";
|
|
@@ -82417,7 +82735,7 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
82417
82735
|
const { content } = readSelectedDocument(entries);
|
|
82418
82736
|
const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${reason})
|
|
82419
82737
|
${content}`;
|
|
82420
|
-
if (chars + section.length + (sections.length ? 2 : 0) > maxChars) {
|
|
82738
|
+
if (maxChars !== undefined && chars + section.length + (sections.length ? 2 : 0) > maxChars) {
|
|
82421
82739
|
omitted.push({ slug: selection.slug, version: selection.version, reason: "context-budget", loadCommand });
|
|
82422
82740
|
continue;
|
|
82423
82741
|
}
|
|
@@ -82431,7 +82749,7 @@ ${content}`;
|
|
|
82431
82749
|
`);
|
|
82432
82750
|
const receipt = {
|
|
82433
82751
|
schemaVersion: 1,
|
|
82434
|
-
id:
|
|
82752
|
+
id: createHash23("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
82435
82753
|
profileId: profile.profileId,
|
|
82436
82754
|
profileRevision: profile.profileRevision,
|
|
82437
82755
|
authority: profile.authority,
|
|
@@ -82453,8 +82771,8 @@ var init_skill_context = __esm(() => {
|
|
|
82453
82771
|
});
|
|
82454
82772
|
|
|
82455
82773
|
// src/lib/session-reconciliation.ts
|
|
82456
|
-
import { createHash as
|
|
82457
|
-
import { isDeepStrictEqual as
|
|
82774
|
+
import { createHash as createHash24 } from "crypto";
|
|
82775
|
+
import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
|
|
82458
82776
|
function exactTimestamp(value) {
|
|
82459
82777
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
82460
82778
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -82511,7 +82829,7 @@ async function reconcileSkillSessionIfSafe(sessionId, profileId, options = {}) {
|
|
|
82511
82829
|
for (const key of old.loaded) {
|
|
82512
82830
|
const previous = oldByKey.get(key);
|
|
82513
82831
|
const current = targetBySlug.get(previous.slug);
|
|
82514
|
-
if (!current || !
|
|
82832
|
+
if (!current || !isDeepStrictEqual9({ ...previous, profileRevision: target.profileRevision }, current)) {
|
|
82515
82833
|
throw new SkillSelectionError("SESSION_RECONCILIATION_REQUIRED", "A loaded Skills selection was removed or changed; review skills sessions reconcile before changing this session's pin.");
|
|
82516
82834
|
}
|
|
82517
82835
|
}
|
|
@@ -82593,7 +82911,7 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
82593
82911
|
const result2 = replaceSkillSession(skillSessionSnapshotBinding(snapshot2), replacement, { ...plan, planDigest }, options, () => assertPlanWindow(issuedAt, expiresAt, (options.now ?? Date.now)()));
|
|
82594
82912
|
return { applied: true, plan, planDigest, ...result2 };
|
|
82595
82913
|
}
|
|
82596
|
-
var SESSION_RECONCILIATION_PLAN_TTL_MS, digest3 = (value) =>
|
|
82914
|
+
var SESSION_RECONCILIATION_PLAN_TTL_MS, digest3 = (value) => createHash24("sha256").update(JSON.stringify(value)).digest("hex");
|
|
82597
82915
|
var init_session_reconciliation = __esm(() => {
|
|
82598
82916
|
init_profile_client();
|
|
82599
82917
|
init_selection_cache();
|
|
@@ -82673,7 +82991,7 @@ Preserved original receipt: ${result2.archivePath}` : `Planned one session recon
|
|
|
82673
82991
|
reportContextError(error61, options.json);
|
|
82674
82992
|
}
|
|
82675
82993
|
});
|
|
82676
|
-
parent.command("context [prompt]").description("Select bounded, versioned skill context for this prompt without running skills").option("--stdin", "Read native hook JSON or a prompt from stdin", false).option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--auto-reconcile-safe", "For managed hooks, archive and renew an expired session only when all loaded selections are unchanged", false).option("--session <id>", "Keep the same skill versions across this session").option("--restore", "Re-emit loaded session context after compaction or resume", false).option("--max-chars <count>", "
|
|
82994
|
+
parent.command("context [prompt]").description("Select bounded, versioned skill context for this prompt without running skills").option("--stdin", "Read native hook JSON or a prompt from stdin", false).option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--auto-reconcile-safe", "For managed hooks, archive and renew an expired session only when all loaded selections are unchanged", false).option("--session <id>", "Keep the same skill versions across this session").option("--restore", "Re-emit loaded session context after compaction or resume", false).option("--max-chars <count>", "Optional maximum injected characters").option("--max-skills <count>", "Maximum loaded skills", "3").option("--json", "Return context and selection receipts as JSON", false).action(async (prompt, options) => {
|
|
82677
82995
|
try {
|
|
82678
82996
|
if (options.autoReconcileSafe && !options.cached)
|
|
82679
82997
|
throw new SkillSelectionError("INVALID_CONTEXT_INPUT", "--auto-reconcile-safe requires --cached.");
|
|
@@ -82682,7 +83000,7 @@ Preserved original receipt: ${result2.archivePath}` : `Planned one session recon
|
|
|
82682
83000
|
input2.cwd ??= process.cwd();
|
|
82683
83001
|
input2.sessionId = options.session ?? input2.sessionId;
|
|
82684
83002
|
input2.restore ||= options.restore;
|
|
82685
|
-
const resolverOptions = { ...contextResolverOptions(options), maxChars: Number(options.maxChars), maxSkills: Number(options.maxSkills) };
|
|
83003
|
+
const resolverOptions = { ...contextResolverOptions(options), ...options.maxChars === undefined ? {} : { maxChars: Number(options.maxChars) }, maxSkills: Number(options.maxSkills) };
|
|
82686
83004
|
let result2;
|
|
82687
83005
|
try {
|
|
82688
83006
|
result2 = await buildSkillContext(input2, resolverOptions);
|
|
@@ -83729,7 +84047,7 @@ function handleDiff(name, options) {
|
|
|
83729
84047
|
const sourcePath = getSkillPath(bare);
|
|
83730
84048
|
const canonicalDir = join39(resolveCorpusRoot(), normalized);
|
|
83731
84049
|
const canonicalSkillMd = join39(canonicalDir, "SKILL.md");
|
|
83732
|
-
const
|
|
84050
|
+
const canonical6 = {
|
|
83733
84051
|
present: existsSync22(canonicalSkillMd),
|
|
83734
84052
|
path: canonicalDir,
|
|
83735
84053
|
...existsSync22(canonicalSkillMd) ? { hash: hashSkillMarkdownFile(canonicalSkillMd) } : {},
|
|
@@ -83761,7 +84079,7 @@ function handleDiff(name, options) {
|
|
|
83761
84079
|
stub = undefined;
|
|
83762
84080
|
}
|
|
83763
84081
|
}
|
|
83764
|
-
const diverged = present &&
|
|
84082
|
+
const diverged = present && canonical6.present && managed && hash5 !== canonical6.hash;
|
|
83765
84083
|
homes.push({
|
|
83766
84084
|
agent,
|
|
83767
84085
|
path: dir,
|
|
@@ -83776,7 +84094,7 @@ function handleDiff(name, options) {
|
|
|
83776
84094
|
if (options.json) {
|
|
83777
84095
|
console.log(JSON.stringify({
|
|
83778
84096
|
name: bare,
|
|
83779
|
-
canonical:
|
|
84097
|
+
canonical: canonical6,
|
|
83780
84098
|
pinned,
|
|
83781
84099
|
installedVersion,
|
|
83782
84100
|
registryVersion,
|
|
@@ -83784,10 +84102,10 @@ function handleDiff(name, options) {
|
|
|
83784
84102
|
homes
|
|
83785
84103
|
}, null, 2));
|
|
83786
84104
|
} else {
|
|
83787
|
-
if (!
|
|
84105
|
+
if (!canonical6.present) {
|
|
83788
84106
|
console.log(source_default.yellow(`${bare}: no canonical corpus entry`));
|
|
83789
84107
|
} else {
|
|
83790
|
-
console.log(source_default.dim(`${bare} canonical: ${
|
|
84108
|
+
console.log(source_default.dim(`${bare} canonical: ${canonical6.hash?.slice(0, 12)}\u2026`));
|
|
83791
84109
|
}
|
|
83792
84110
|
if (pinned) {
|
|
83793
84111
|
console.log(upToDate ? source_default.green(`\u2713 pin is up to date (${installedVersion})`) : source_default.yellow(`pin metadata differs: ${installedVersion} \u2192 ${registryVersion}`));
|
|
@@ -83798,14 +84116,14 @@ function handleDiff(name, options) {
|
|
|
83798
84116
|
} else if (!home.managed) {
|
|
83799
84117
|
console.log(`${source_default.dim("\u2022")} ${home.agent}: ${source_default.yellow("unmarked (adoption candidate)")}`);
|
|
83800
84118
|
} else if (home.diverged) {
|
|
83801
|
-
const stubNote = home.stub === true ? " (home is a pointer stub; canonical holds content)" :
|
|
83802
|
-
console.log(`${source_default.red("\u2717")} ${home.agent}: ${source_default.red("diverged")} ${source_default.dim(home.hash?.slice(0, 12))} \u2260 ${source_default.dim(
|
|
84119
|
+
const stubNote = home.stub === true ? " (home is a pointer stub; canonical holds content)" : canonical6.stub === true ? " (home holds content; canonical renders a pointer stub \u2014 sync refuses to replace it)" : "";
|
|
84120
|
+
console.log(`${source_default.red("\u2717")} ${home.agent}: ${source_default.red("diverged")} ${source_default.dim(home.hash?.slice(0, 12))} \u2260 ${source_default.dim(canonical6.hash?.slice(0, 12))}${stubNote}`);
|
|
83803
84121
|
} else {
|
|
83804
84122
|
console.log(`${source_default.green("\u2713")} ${home.agent}: ${source_default.green("matches canonical")}`);
|
|
83805
84123
|
}
|
|
83806
84124
|
}
|
|
83807
84125
|
}
|
|
83808
|
-
if (divergedHomes.length > 0 || homes.length > 0 && !
|
|
84126
|
+
if (divergedHomes.length > 0 || homes.length > 0 && !canonical6.present)
|
|
83809
84127
|
process.exitCode = 1;
|
|
83810
84128
|
}
|
|
83811
84129
|
var init_introspect = __esm(() => {
|
|
@@ -87111,7 +87429,7 @@ var init_execution_grants = __esm(() => {
|
|
|
87111
87429
|
});
|
|
87112
87430
|
|
|
87113
87431
|
// src/lib/execution-grant-client.ts
|
|
87114
|
-
import { isDeepStrictEqual as
|
|
87432
|
+
import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
|
|
87115
87433
|
function id(value) {
|
|
87116
87434
|
if (!grantIdentifier(value))
|
|
87117
87435
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -87163,7 +87481,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
|
|
|
87163
87481
|
headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
|
|
87164
87482
|
body: JSON.stringify({ grants: snapshot2 })
|
|
87165
87483
|
}));
|
|
87166
|
-
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !
|
|
87484
|
+
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual10(policy.grants, snapshot2)) {
|
|
87167
87485
|
throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
|
|
87168
87486
|
}
|
|
87169
87487
|
return policy;
|
|
@@ -87174,7 +87492,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
87174
87492
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
87175
87493
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
87176
87494
|
}
|
|
87177
|
-
if (!
|
|
87495
|
+
if (!isDeepStrictEqual10(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual10(value.bindings.consumer, snapshot2.consumer)) {
|
|
87178
87496
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
87179
87497
|
}
|
|
87180
87498
|
return value;
|
|
@@ -87193,7 +87511,7 @@ __export(exports_selected_run, {
|
|
|
87193
87511
|
prepareSelectedSecretBindings: () => prepareSelectedSecretBindings,
|
|
87194
87512
|
executeSelectedLocal: () => executeSelectedLocal
|
|
87195
87513
|
});
|
|
87196
|
-
import { createHash as
|
|
87514
|
+
import { createHash as createHash25 } from "crypto";
|
|
87197
87515
|
import { spawn as spawn3 } from "child_process";
|
|
87198
87516
|
import { mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync4, writeFileSync as writeFileSync15, realpathSync as realpathSync11 } from "fs";
|
|
87199
87517
|
import { dirname as dirname23, join as join45, resolve as resolve20 } from "path";
|
|
@@ -87319,7 +87637,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
87319
87637
|
selection: selection2,
|
|
87320
87638
|
target: "local",
|
|
87321
87639
|
runtime: runtimeName,
|
|
87322
|
-
inputDigest: `sha256:${
|
|
87640
|
+
inputDigest: `sha256:${createHash25("sha256").update(input2).digest("hex")}`,
|
|
87323
87641
|
runDirectory,
|
|
87324
87642
|
...executionGrant ? { executionGrant } : {},
|
|
87325
87643
|
...bindings ? { secretBinding: { schema: bindings.schema, selection: bindings.selection, consumer: bindings.consumer, secretsAuthority: bindings.secretsAuthority, bindings: bindings.bindings } } : {},
|
|
@@ -91536,9 +91854,9 @@ var init_runs = __esm(() => {
|
|
|
91536
91854
|
});
|
|
91537
91855
|
|
|
91538
91856
|
// src/lib/run-state.ts
|
|
91539
|
-
import { createHash as
|
|
91857
|
+
import { createHash as createHash26, randomBytes } from "crypto";
|
|
91540
91858
|
import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync32, readdirSync as readdirSync18, statSync as statSync16, writeFileSync as writeFileSync16 } from "fs";
|
|
91541
|
-
import { extname, join as join46, relative as
|
|
91859
|
+
import { extname, join as join46, relative as relative7 } from "path";
|
|
91542
91860
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
91543
91861
|
const now3 = new Date;
|
|
91544
91862
|
const id2 = createRunId(now3);
|
|
@@ -91673,7 +91991,7 @@ function collectRunArtifacts(context) {
|
|
|
91673
91991
|
artifacts.push({
|
|
91674
91992
|
path: toProjectRelative(context.targetDir, path),
|
|
91675
91993
|
mime: mimeForPath(path),
|
|
91676
|
-
sha256:
|
|
91994
|
+
sha256: createHash26("sha256").update(bytes).digest("hex"),
|
|
91677
91995
|
sizeBytes: stat2.size
|
|
91678
91996
|
});
|
|
91679
91997
|
}
|
|
@@ -91704,7 +92022,7 @@ function createRunId(now3) {
|
|
|
91704
92022
|
return `run_${now3.getTime().toString(36)}_${randomBytes(4).toString("hex")}`;
|
|
91705
92023
|
}
|
|
91706
92024
|
function toProjectRelative(targetDir, path) {
|
|
91707
|
-
const rel =
|
|
92025
|
+
const rel = relative7(targetDir, path).split(/[\\/]/).join("/");
|
|
91708
92026
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
91709
92027
|
}
|
|
91710
92028
|
function mimeForPath(path) {
|
|
@@ -97399,8 +97717,8 @@ var require_resolve = __commonJS((exports) => {
|
|
|
97399
97717
|
}
|
|
97400
97718
|
return count;
|
|
97401
97719
|
}
|
|
97402
|
-
function getFullPath(resolver, id2 = "",
|
|
97403
|
-
if (
|
|
97720
|
+
function getFullPath(resolver, id2 = "", normalize5) {
|
|
97721
|
+
if (normalize5 !== false)
|
|
97404
97722
|
id2 = normalizeId(id2);
|
|
97405
97723
|
const p = resolver.parse(id2);
|
|
97406
97724
|
return _getFullPath(resolver, p);
|
|
@@ -98959,7 +99277,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
98959
99277
|
}
|
|
98960
99278
|
return decodedScheme;
|
|
98961
99279
|
}
|
|
98962
|
-
function
|
|
99280
|
+
function normalize5(uri, options) {
|
|
98963
99281
|
if (typeof uri === "string") {
|
|
98964
99282
|
uri = normalizeString(uri, options);
|
|
98965
99283
|
} else if (typeof uri === "object") {
|
|
@@ -99000,49 +99318,49 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
99000
99318
|
schemelessOptions.skipEscape = true;
|
|
99001
99319
|
return serialize(resolved, schemelessOptions);
|
|
99002
99320
|
}
|
|
99003
|
-
function resolveComponent(base2,
|
|
99321
|
+
function resolveComponent(base2, relative8, options, skipNormalization) {
|
|
99004
99322
|
const target = {};
|
|
99005
99323
|
if (!skipNormalization) {
|
|
99006
99324
|
base2 = parse12(serialize(base2, options), options);
|
|
99007
|
-
|
|
99325
|
+
relative8 = parse12(serialize(relative8, options), options);
|
|
99008
99326
|
}
|
|
99009
99327
|
options = options || {};
|
|
99010
|
-
if (!options.tolerant &&
|
|
99011
|
-
target.scheme =
|
|
99012
|
-
target.userinfo =
|
|
99013
|
-
target.host =
|
|
99014
|
-
target.port =
|
|
99015
|
-
target.path = removeDotSegments(
|
|
99016
|
-
target.query =
|
|
99328
|
+
if (!options.tolerant && relative8.scheme) {
|
|
99329
|
+
target.scheme = relative8.scheme;
|
|
99330
|
+
target.userinfo = relative8.userinfo;
|
|
99331
|
+
target.host = relative8.host;
|
|
99332
|
+
target.port = relative8.port;
|
|
99333
|
+
target.path = removeDotSegments(relative8.path || "");
|
|
99334
|
+
target.query = relative8.query;
|
|
99017
99335
|
} else {
|
|
99018
|
-
if (
|
|
99019
|
-
target.userinfo =
|
|
99020
|
-
target.host =
|
|
99021
|
-
target.port =
|
|
99022
|
-
target.path = removeDotSegments(
|
|
99023
|
-
target.query =
|
|
99336
|
+
if (relative8.userinfo !== undefined || relative8.host !== undefined || relative8.port !== undefined) {
|
|
99337
|
+
target.userinfo = relative8.userinfo;
|
|
99338
|
+
target.host = relative8.host;
|
|
99339
|
+
target.port = relative8.port;
|
|
99340
|
+
target.path = removeDotSegments(relative8.path || "");
|
|
99341
|
+
target.query = relative8.query;
|
|
99024
99342
|
} else {
|
|
99025
|
-
if (!
|
|
99343
|
+
if (!relative8.path) {
|
|
99026
99344
|
target.path = base2.path;
|
|
99027
|
-
if (
|
|
99028
|
-
target.query =
|
|
99345
|
+
if (relative8.query !== undefined) {
|
|
99346
|
+
target.query = relative8.query;
|
|
99029
99347
|
} else {
|
|
99030
99348
|
target.query = base2.query;
|
|
99031
99349
|
}
|
|
99032
99350
|
} else {
|
|
99033
|
-
if (
|
|
99034
|
-
target.path = removeDotSegments(
|
|
99351
|
+
if (relative8.path[0] === "/") {
|
|
99352
|
+
target.path = removeDotSegments(relative8.path);
|
|
99035
99353
|
} else {
|
|
99036
99354
|
if ((base2.userinfo !== undefined || base2.host !== undefined || base2.port !== undefined) && !base2.path) {
|
|
99037
|
-
target.path = "/" +
|
|
99355
|
+
target.path = "/" + relative8.path;
|
|
99038
99356
|
} else if (!base2.path) {
|
|
99039
|
-
target.path =
|
|
99357
|
+
target.path = relative8.path;
|
|
99040
99358
|
} else {
|
|
99041
|
-
target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) +
|
|
99359
|
+
target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative8.path;
|
|
99042
99360
|
}
|
|
99043
99361
|
target.path = removeDotSegments(target.path);
|
|
99044
99362
|
}
|
|
99045
|
-
target.query =
|
|
99363
|
+
target.query = relative8.query;
|
|
99046
99364
|
}
|
|
99047
99365
|
target.userinfo = base2.userinfo;
|
|
99048
99366
|
target.host = base2.host;
|
|
@@ -99050,7 +99368,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
99050
99368
|
}
|
|
99051
99369
|
target.scheme = base2.scheme;
|
|
99052
99370
|
}
|
|
99053
|
-
target.fragment =
|
|
99371
|
+
target.fragment = relative8.fragment;
|
|
99054
99372
|
return target;
|
|
99055
99373
|
}
|
|
99056
99374
|
function equal(uriA, uriB, options) {
|
|
@@ -99336,7 +99654,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
99336
99654
|
}
|
|
99337
99655
|
var fastUri = {
|
|
99338
99656
|
SCHEMES,
|
|
99339
|
-
normalize:
|
|
99657
|
+
normalize: normalize5,
|
|
99340
99658
|
resolve: resolve21,
|
|
99341
99659
|
resolveComponent,
|
|
99342
99660
|
equal,
|
|
@@ -104346,7 +104664,7 @@ async function continueRecurringRecovery(customer, directory, options) {
|
|
|
104346
104664
|
throw error61;
|
|
104347
104665
|
}
|
|
104348
104666
|
}
|
|
104349
|
-
var uuid11,
|
|
104667
|
+
var uuid11, sha5, common2, intentSchema, stateSchema, invalid6 = () => {
|
|
104350
104668
|
throw new RecurringCustomerError("RECURRING_RECOVERY_INVALID", "The original recovery directory or record is invalid or changed. Preserve it; do not create a replacement activation automatically.");
|
|
104351
104669
|
};
|
|
104352
104670
|
var init_recurring_recovery = __esm(() => {
|
|
@@ -104356,7 +104674,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
104356
104674
|
init_remote_recurring();
|
|
104357
104675
|
init_recurring_customer();
|
|
104358
104676
|
uuid11 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
104359
|
-
|
|
104677
|
+
sha5 = exports_external.string().regex(/^[a-f0-9]{64}$/);
|
|
104360
104678
|
common2 = exports_external.object({
|
|
104361
104679
|
contractVersion: exports_external.literal(1),
|
|
104362
104680
|
apiOrigin: exports_external.string().min(1).max(4096),
|
|
@@ -104369,7 +104687,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
104369
104687
|
common2.extend({
|
|
104370
104688
|
operation: exports_external.literal("activate"),
|
|
104371
104689
|
draftId: uuid11,
|
|
104372
|
-
termsSha256:
|
|
104690
|
+
termsSha256: sha5,
|
|
104373
104691
|
approvalDeadline: exports_external.string().datetime(),
|
|
104374
104692
|
approval: recurringActivationSchema
|
|
104375
104693
|
}).strict(),
|
|
@@ -104377,7 +104695,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
104377
104695
|
]);
|
|
104378
104696
|
stateSchema = exports_external.object({
|
|
104379
104697
|
contractVersion: exports_external.literal(1),
|
|
104380
|
-
intentSha256:
|
|
104698
|
+
intentSha256: sha5,
|
|
104381
104699
|
phase: exports_external.enum(["prepared", "pending", "observed"]),
|
|
104382
104700
|
consentId: uuid11.nullable()
|
|
104383
104701
|
}).strict();
|
|
@@ -106921,7 +107239,7 @@ var init_schedule_tools = __esm(() => {
|
|
|
106921
107239
|
});
|
|
106922
107240
|
|
|
106923
107241
|
// src/lib/native-storage.ts
|
|
106924
|
-
import { createHash as
|
|
107242
|
+
import { createHash as createHash27, createHmac as createHmac3 } from "crypto";
|
|
106925
107243
|
import {
|
|
106926
107244
|
existsSync as existsSync31,
|
|
106927
107245
|
mkdirSync as mkdirSync21,
|
|
@@ -106930,7 +107248,7 @@ import {
|
|
|
106930
107248
|
statSync as statSync18,
|
|
106931
107249
|
writeFileSync as writeFileSync20
|
|
106932
107250
|
} from "fs";
|
|
106933
|
-
import { dirname as dirname27, join as join52, normalize as
|
|
107251
|
+
import { dirname as dirname27, join as join52, normalize as normalize5, relative as relative8, sep as sep9 } from "path";
|
|
106934
107252
|
function resolveSkillsNativeStorageConfig(env3 = process.env) {
|
|
106935
107253
|
assertNoRetiredModeEnvVars(env3, {
|
|
106936
107254
|
app: SKILLS_ENV_NAMESPACE,
|
|
@@ -106994,11 +107312,11 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
106994
107312
|
if (existsSync31(projectStateDir)) {
|
|
106995
107313
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
106996
107314
|
const bytes = readFileSync35(filePath);
|
|
106997
|
-
const relativePath = toPosix(
|
|
107315
|
+
const relativePath = toPosix(relative8(targetDir, filePath));
|
|
106998
107316
|
files.push({
|
|
106999
107317
|
path: relativePath,
|
|
107000
107318
|
sizeBytes: bytes.byteLength,
|
|
107001
|
-
sha256:
|
|
107319
|
+
sha256: createHash27("sha256").update(bytes).digest("hex"),
|
|
107002
107320
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
107003
107321
|
});
|
|
107004
107322
|
}
|
|
@@ -108180,7 +108498,7 @@ var init_remote_account2 = __esm(() => {
|
|
|
108180
108498
|
});
|
|
108181
108499
|
|
|
108182
108500
|
// src/cli/commands/runtime-copyfile.ts
|
|
108183
|
-
import { createHash as
|
|
108501
|
+
import { createHash as createHash28, randomUUID as randomUUID9 } from "crypto";
|
|
108184
108502
|
import { Readable } from "stream";
|
|
108185
108503
|
import { createGunzip as createGunzip2 } from "zlib";
|
|
108186
108504
|
import {
|
|
@@ -108197,17 +108515,17 @@ import {
|
|
|
108197
108515
|
symlinkSync,
|
|
108198
108516
|
writeFileSync as writeFileSync23
|
|
108199
108517
|
} from "fs";
|
|
108200
|
-
import { basename as
|
|
108518
|
+
import { basename as basename14, delimiter, dirname as dirname30, isAbsolute as isAbsolute24, join as join55, relative as relative9, resolve as resolve23, sep as sep10 } from "path";
|
|
108201
108519
|
function object9(value, code2) {
|
|
108202
108520
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
108203
108521
|
throw new Error(code2);
|
|
108204
108522
|
return value;
|
|
108205
108523
|
}
|
|
108206
108524
|
function hash5(bytes) {
|
|
108207
|
-
return
|
|
108525
|
+
return createHash28("sha256").update(bytes).digest("hex");
|
|
108208
108526
|
}
|
|
108209
108527
|
function isWithin(parent, child) {
|
|
108210
|
-
const rel =
|
|
108528
|
+
const rel = relative9(parent, child);
|
|
108211
108529
|
return rel === "" || !rel.startsWith(`..${sep10}`) && rel !== ".." && !isAbsolute24(rel);
|
|
108212
108530
|
}
|
|
108213
108531
|
function entryExists(path) {
|
|
@@ -108302,7 +108620,7 @@ function resolveRuntimeLayout(homeInput, pathInput) {
|
|
|
108302
108620
|
assertNoSymlinkAncestors(currentPackageRoot);
|
|
108303
108621
|
if (!isWithin(runtimeRoot, currentPackageRoot))
|
|
108304
108622
|
throw new Error("ACTIVE_RUNTIME_OUTSIDE_COPYFILE_ROOT");
|
|
108305
|
-
const relativePackage =
|
|
108623
|
+
const relativePackage = relative9(runtimeRoot, currentPackageRoot).split(sep10);
|
|
108306
108624
|
if (relativePackage.length !== 4 || relativePackage[1] !== "node_modules" || relativePackage[2] !== "@hasna" || relativePackage[3] !== "skills") {
|
|
108307
108625
|
throw new Error("ACTIVE_RUNTIME_LAYOUT_UNSUPPORTED");
|
|
108308
108626
|
}
|
|
@@ -108355,7 +108673,7 @@ function verifyIntegrity(bytes, integrity) {
|
|
|
108355
108673
|
const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(integrity);
|
|
108356
108674
|
if (!match)
|
|
108357
108675
|
throw new Error("REGISTRY_INTEGRITY_UNSUPPORTED");
|
|
108358
|
-
const actual =
|
|
108676
|
+
const actual = createHash28("sha512").update(bytes).digest("base64");
|
|
108359
108677
|
if (actual !== match[1])
|
|
108360
108678
|
throw new Error("TARBALL_INTEGRITY_MISMATCH");
|
|
108361
108679
|
}
|
|
@@ -108856,7 +109174,7 @@ function inspectAliasPackageChain(home, aliasPath, oldLinkTarget, oldTarget, bin
|
|
|
108856
109174
|
function validateAliasSource(home, name, actual) {
|
|
108857
109175
|
const packageRoot = dirname30(dirname30(actual));
|
|
108858
109176
|
const roots = [join55(home, ".hasna", "skills", "runtime"), join55(home, ".bun", "install"), join55(home, ".local", "lib", "node_modules")];
|
|
108859
|
-
if (
|
|
109177
|
+
if (basename14(packageRoot) !== "skills" || basename14(dirname30(packageRoot)) !== "@hasna" || !roots.some((root2) => isWithin(root2, packageRoot))) {
|
|
108860
109178
|
throw new Error("ALIAS_SOURCE_ROOT_UNSUPPORTED");
|
|
108861
109179
|
}
|
|
108862
109180
|
assertOwnedSafePath(packageRoot, home);
|
|
@@ -108934,8 +109252,8 @@ function adoptCopyfileAliases(options = {}) {
|
|
|
108934
109252
|
for (const item of aliases) {
|
|
108935
109253
|
if (readlinkSync5(item.path) !== item.oldLinkTarget || realpathSync14(item.path) !== item.oldTarget)
|
|
108936
109254
|
throw new Error("ALIAS_CHANGED_BEFORE_SWITCH");
|
|
108937
|
-
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[
|
|
108938
|
-
const source = validateAliasSource(home,
|
|
109255
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename14(item.path)], item.chain, true);
|
|
109256
|
+
const source = validateAliasSource(home, basename14(item.path), item.oldTarget);
|
|
108939
109257
|
if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
|
|
108940
109258
|
throw new Error("ALIAS_SOURCE_DRIFT");
|
|
108941
109259
|
receipt.state = "switching";
|
|
@@ -108944,11 +109262,11 @@ function adoptCopyfileAliases(options = {}) {
|
|
|
108944
109262
|
replaceLink(item.path, item.newTarget, item.backupPath, item.oldLinkTarget, item.oldTarget);
|
|
108945
109263
|
if (realpathSync14(item.path) !== item.newTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
108946
109264
|
throw new Error("ALIAS_SWITCH_READBACK_MISMATCH");
|
|
108947
|
-
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[
|
|
109265
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename14(item.path)], item.chain, true);
|
|
108948
109266
|
options.onAliasSwitched?.(item.path);
|
|
108949
109267
|
}
|
|
108950
109268
|
for (const item of aliases) {
|
|
108951
|
-
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[
|
|
109269
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename14(item.path)], item.chain, true);
|
|
108952
109270
|
if (readlinkSync5(item.backupPath) !== item.oldLinkTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
108953
109271
|
throw new Error("ALIAS_BACKUP_DRIFT");
|
|
108954
109272
|
}
|
|
@@ -108961,16 +109279,16 @@ function adoptCopyfileAliases(options = {}) {
|
|
|
108961
109279
|
for (const item of [...aliases].reverse()) {
|
|
108962
109280
|
try {
|
|
108963
109281
|
if (!entryExists(item.backupPath)) {
|
|
108964
|
-
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[
|
|
108965
|
-
const source2 = validateAliasSource(home,
|
|
109282
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename14(item.path)], item.chain, true);
|
|
109283
|
+
const source2 = validateAliasSource(home, basename14(item.path), item.oldTarget);
|
|
108966
109284
|
if (!entryExists(item.path) || readlinkSync5(item.path) !== item.oldLinkTarget || realpathSync14(item.path) !== item.oldTarget || source2.version !== item.oldVersion || source2.binarySha256 !== item.oldBinarySha256)
|
|
108967
109285
|
failed = true;
|
|
108968
109286
|
continue;
|
|
108969
109287
|
}
|
|
108970
109288
|
if (readlinkSync5(item.backupPath) !== item.oldLinkTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
108971
109289
|
throw new Error("ALIAS_BACKUP_DRIFT");
|
|
108972
|
-
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[
|
|
108973
|
-
const source = validateAliasSource(home,
|
|
109290
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename14(item.path)], item.chain, true);
|
|
109291
|
+
const source = validateAliasSource(home, basename14(item.path), item.oldTarget);
|
|
108974
109292
|
if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
|
|
108975
109293
|
throw new Error("ALIAS_SOURCE_DRIFT");
|
|
108976
109294
|
if (realpathSync14(item.path) === item.oldTarget && readlinkSync5(item.path) === item.oldLinkTarget)
|
|
@@ -109023,7 +109341,7 @@ function rollbackCopyfileAliases(receiptId, options = {}) {
|
|
|
109023
109341
|
throw new Error("ALIAS_RECEIPT_BINDING_INVALID");
|
|
109024
109342
|
const seen = new Set;
|
|
109025
109343
|
for (const item of receipt.aliases) {
|
|
109026
|
-
const name =
|
|
109344
|
+
const name = basename14(item.path), target = layout.bin[name];
|
|
109027
109345
|
if (!target || seen.has(item.path) || !aliasDirs(home).includes(dirname30(item.path)) || item.newTarget !== join55(layout.currentPackageRoot, target) || item.backupPath !== `${item.path}.skills-alias-prev-${receiptId}` || item.chain !== undefined && (!item.chain || typeof item.chain !== "object" || typeof item.chain.packageLinkPath !== "string" || typeof item.chain.packageLinkTarget !== "string")) {
|
|
109028
109346
|
throw new Error("ALIAS_RECEIPT_LAUNCHER_INVALID");
|
|
109029
109347
|
}
|
|
@@ -109072,7 +109390,7 @@ function rollbackCopyfileAliases(receiptId, options = {}) {
|
|
|
109072
109390
|
restored++;
|
|
109073
109391
|
}
|
|
109074
109392
|
for (const item of receipt.aliases) {
|
|
109075
|
-
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[
|
|
109393
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename14(item.path)], item.chain, true);
|
|
109076
109394
|
}
|
|
109077
109395
|
receipt.state = "rolled-back";
|
|
109078
109396
|
persistAliasReceipt(receiptPath, receipt);
|
|
@@ -109105,7 +109423,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
109105
109423
|
const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
|
|
109106
109424
|
const stagePath = join55(layout.runtimeRoot, `.stage-${version2}-${id2}`);
|
|
109107
109425
|
const finalPath = join55(layout.runtimeRoot, `${version2}-copyfile`);
|
|
109108
|
-
const launchers = layout.launchers.map((item) => ({ ...item, newTarget: join55(finalPath, "node_modules", "@hasna", "skills", layout.bin[
|
|
109426
|
+
const launchers = layout.launchers.map((item) => ({ ...item, newTarget: join55(finalPath, "node_modules", "@hasna", "skills", layout.bin[basename14(item.path)]) }));
|
|
109109
109427
|
let switched = [];
|
|
109110
109428
|
let moved = false;
|
|
109111
109429
|
try {
|
|
@@ -109288,7 +109606,7 @@ function rollbackCopyfileRuntime(receiptId, options = {}) {
|
|
|
109288
109606
|
const switched = [];
|
|
109289
109607
|
const seen = new Set;
|
|
109290
109608
|
for (const item of receipt.launchers) {
|
|
109291
|
-
const bin = oldBins[
|
|
109609
|
+
const bin = oldBins[basename14(item.path)];
|
|
109292
109610
|
if (!bin || !isAbsolute24(item.path) || seen.has(item.path) || item.oldTarget !== join55(receipt.currentPackageRoot, bin) || item.newTarget !== join55(receipt.targetPackageRoot, bin) || !/^.+\.skills-prev-[0-9a-f-]{36}$/.test(item.backupPath) || !item.backupPath.startsWith(`${item.path}.skills-prev-`) || typeof item.oldLinkTarget !== "string" || resolve23(dirname30(item.path), item.oldLinkTarget) !== item.oldTarget)
|
|
109293
109611
|
throw new Error("RECEIPT_LAUNCHER_BINDING_INVALID");
|
|
109294
109612
|
assertNoSymlinkAncestors(dirname30(item.path));
|
|
@@ -109356,9 +109674,9 @@ var exports_runtime = {};
|
|
|
109356
109674
|
__export(exports_runtime, {
|
|
109357
109675
|
registerRuntime: () => registerRuntime
|
|
109358
109676
|
});
|
|
109359
|
-
import { createHash as
|
|
109677
|
+
import { createHash as createHash29, randomUUID as randomUUID10 } from "crypto";
|
|
109360
109678
|
import { lstatSync as lstatSync29, mkdirSync as mkdirSync25, readFileSync as readFileSync38, realpathSync as realpathSync15, writeFileSync as writeFileSync24 } from "fs";
|
|
109361
|
-
import { basename as
|
|
109679
|
+
import { basename as basename15, isAbsolute as isAbsolute25, join as join56 } from "path";
|
|
109362
109680
|
import { createInterface } from "readline";
|
|
109363
109681
|
function registerRuntime(parent) {
|
|
109364
109682
|
parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill; use -- to end Skills options").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--remote", "Run on the configured server, using its catalog and quote", false).option("--target <target>", "Execution target: local or cloud").option("--secret-bindings <path>", "Reviewed, exact-selection local execution vault bindings (JSON file)").option("--secret-bindings-template", "Print a scoped bindings template without resolving secrets or running the skill", false).option("--input <json>", "Structured cloud execution input").option("--skill-version <version>", "Exact selected skill version").option("--selection-profile <id>", "Selected Skills profile").option("--session <id>", "Pinned Skills session").option("--cached", "Use the explicit verified selected cache", false).option("--yes", "Approve the server's quoted credit cost for this run", false).option("--idempotency-key <key>", "Reuse this key when retrying the same remote submission").option("--file <path>", "Attach a local input file to a remote run (repeatable)", (value, prior) => [...prior, value], []).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options, command) => {
|
|
@@ -109397,7 +109715,7 @@ function registerRuntime(parent) {
|
|
|
109397
109715
|
if (!expected)
|
|
109398
109716
|
throw new Error("Cloud artifact not found");
|
|
109399
109717
|
const bytes = await client2.download(id2, name);
|
|
109400
|
-
if (bytes.length !== expected.byteSize ||
|
|
109718
|
+
if (bytes.length !== expected.byteSize || createHash29("sha256").update(bytes).digest("hex") !== expected.sha256)
|
|
109401
109719
|
throw new Error("Cloud artifact integrity verification failed");
|
|
109402
109720
|
writeFileSync24(options.output, bytes, { flag: "wx", mode: 384 });
|
|
109403
109721
|
console.log(JSON.stringify({ path: options.output, sha256: expected.sha256, byteSize: bytes.length }));
|
|
@@ -109871,7 +110189,7 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
|
|
|
109871
110189
|
const info = lstatSync29(path);
|
|
109872
110190
|
if (!info.isFile() || info.size > 20 * 1024 * 1024)
|
|
109873
110191
|
throw new Error("Input must be a regular file no larger than 20 MiB");
|
|
109874
|
-
return { name:
|
|
110192
|
+
return { name: basename15(path), bytes: new Uint8Array(readFileSync38(path)) };
|
|
109875
110193
|
});
|
|
109876
110194
|
describeRemoteFiles(inputFiles);
|
|
109877
110195
|
client2 = new RemoteSkillsClient(routing.apiKey, routing.apiOrigin);
|
|
@@ -111040,7 +111358,7 @@ var init_portable_snapshot_filter = __esm(() => {
|
|
|
111040
111358
|
});
|
|
111041
111359
|
|
|
111042
111360
|
// src/lib/station-snapshot.ts
|
|
111043
|
-
import { createHash as
|
|
111361
|
+
import { createHash as createHash30 } from "crypto";
|
|
111044
111362
|
import {
|
|
111045
111363
|
copyFileSync as copyFileSync2,
|
|
111046
111364
|
mkdirSync as mkdirSync26,
|
|
@@ -111048,14 +111366,14 @@ import {
|
|
|
111048
111366
|
statSync as statSync20,
|
|
111049
111367
|
writeFileSync as writeFileSync25
|
|
111050
111368
|
} from "fs";
|
|
111051
|
-
import { dirname as dirname32, isAbsolute as isAbsolute26, relative as
|
|
111369
|
+
import { dirname as dirname32, isAbsolute as isAbsolute26, relative as relative10, resolve as resolve24, sep as sep12 } from "path";
|
|
111052
111370
|
function validateStationId(stationId) {
|
|
111053
111371
|
if (!/^[a-z0-9-]+$/.test(stationId)) {
|
|
111054
111372
|
throw new StationSnapshotError("INVALID_STATION", `station id must be a slug, got: ${stationId}`);
|
|
111055
111373
|
}
|
|
111056
111374
|
}
|
|
111057
111375
|
function sha256File(filePath) {
|
|
111058
|
-
return
|
|
111376
|
+
return createHash30("sha256").update(readFileSync39(filePath)).digest("hex");
|
|
111059
111377
|
}
|
|
111060
111378
|
function scanHome(definition, homesRoot) {
|
|
111061
111379
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -111153,7 +111471,7 @@ function writeStationSnapshot(options) {
|
|
|
111153
111471
|
const untouched = [];
|
|
111154
111472
|
for (const plan of plans) {
|
|
111155
111473
|
const destination = resolve24(repoRoot, plan.destination);
|
|
111156
|
-
const destinationRelative =
|
|
111474
|
+
const destinationRelative = relative10(repoRoot, destination);
|
|
111157
111475
|
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep12) || isAbsolute26(destinationRelative)) {
|
|
111158
111476
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
111159
111477
|
}
|
|
@@ -111627,8 +111945,8 @@ var init_create_sync_config = __esm(() => {
|
|
|
111627
111945
|
});
|
|
111628
111946
|
|
|
111629
111947
|
// src/lib/codex-native-config.ts
|
|
111630
|
-
import { isDeepStrictEqual as
|
|
111631
|
-
function
|
|
111948
|
+
import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
|
|
111949
|
+
function isRecord8(value) {
|
|
111632
111950
|
if (!value || typeof value !== "object")
|
|
111633
111951
|
return false;
|
|
111634
111952
|
const prototype = Object.getPrototypeOf(value);
|
|
@@ -111649,10 +111967,10 @@ function decimalIdentity(text4) {
|
|
|
111649
111967
|
return `${match[1]}${coefficient}e${exponent}`;
|
|
111650
111968
|
}
|
|
111651
111969
|
function codexNativeConfigEqual(native, expected) {
|
|
111652
|
-
if (
|
|
111970
|
+
if (isDeepStrictEqual11(native, expected))
|
|
111653
111971
|
return true;
|
|
111654
111972
|
if (typeof expected === "number") {
|
|
111655
|
-
if (!Number.isFinite(expected) || !
|
|
111973
|
+
if (!Number.isFinite(expected) || !isRecord8(native))
|
|
111656
111974
|
return false;
|
|
111657
111975
|
const keys3 = Object.keys(native);
|
|
111658
111976
|
const value = native[NUMBER_TAG];
|
|
@@ -111664,7 +111982,7 @@ function codexNativeConfigEqual(native, expected) {
|
|
|
111664
111982
|
if (Array.isArray(native) || Array.isArray(expected)) {
|
|
111665
111983
|
return Array.isArray(native) && Array.isArray(expected) && native.length === expected.length && native.every((value, index) => codexNativeConfigEqual(value, expected[index]));
|
|
111666
111984
|
}
|
|
111667
|
-
if (!
|
|
111985
|
+
if (!isRecord8(native) || !isRecord8(expected))
|
|
111668
111986
|
return false;
|
|
111669
111987
|
const actual = native, wanted = expected;
|
|
111670
111988
|
return Object.keys(actual).length === Object.keys(wanted).length && Object.keys(wanted).every((key) => Object.hasOwn(actual, key) && codexNativeConfigEqual(actual[key], wanted[key]));
|
|
@@ -111675,42 +111993,42 @@ var init_codex_native_config = () => {};
|
|
|
111675
111993
|
// src/lib/codex-hook-trust-identity.ts
|
|
111676
111994
|
import { realpathSync as realpathSync16 } from "fs";
|
|
111677
111995
|
import { dirname as dirname33, isAbsolute as isAbsolute27, join as join59 } from "path";
|
|
111678
|
-
import { isDeepStrictEqual as
|
|
111996
|
+
import { isDeepStrictEqual as isDeepStrictEqual12 } from "util";
|
|
111679
111997
|
function inspectRecordedSkillsCli(command, expected) {
|
|
111680
|
-
|
|
111998
|
+
need6(isAbsolute27(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
|
|
111681
111999
|
const resolved = realpathSync16(command);
|
|
111682
112000
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
111683
112001
|
const manifest = snapshot(join59(dirname33(dirname33(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
111684
112002
|
const pkg = JSON.parse(manifest.text);
|
|
111685
|
-
|
|
111686
|
-
|
|
112003
|
+
need6(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join59(dirname33(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
112004
|
+
need6((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
111687
112005
|
const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
|
|
111688
|
-
|
|
112006
|
+
need6(isDeepStrictEqual12(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
111689
112007
|
return { receipt, recheck() {
|
|
111690
|
-
|
|
112008
|
+
need6(realpathSync16(command) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
111691
112009
|
unchanged2(cli);
|
|
111692
112010
|
unchanged2(manifest);
|
|
111693
112011
|
} };
|
|
111694
112012
|
}
|
|
111695
112013
|
function bindSkillsCli(command, reviewed) {
|
|
111696
112014
|
const normal = Bun.which("skills", { PATH: process.env.PATH });
|
|
111697
|
-
|
|
112015
|
+
need6(normal, "SKILLS_COMMAND_UNAVAILABLE");
|
|
111698
112016
|
const resolved = realpathSync16(normal);
|
|
111699
112017
|
const bound = isAbsolute27(command) ? command : command === "skills" ? normal : undefined;
|
|
111700
|
-
|
|
112018
|
+
need6(bound && realpathSync16(bound) === resolved, "SKILLS_COMMAND_MISMATCH");
|
|
111701
112019
|
const entrypoint = reviewed?.path ?? process.argv[1];
|
|
111702
|
-
|
|
112020
|
+
need6(entrypoint && realpathSync16(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
|
|
111703
112021
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
111704
112022
|
const manifest = snapshot(join59(dirname33(dirname33(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
111705
112023
|
const pkg = JSON.parse(manifest.text);
|
|
111706
|
-
|
|
112024
|
+
need6(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join59(dirname33(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
111707
112025
|
if (reviewed)
|
|
111708
|
-
|
|
111709
|
-
|
|
112026
|
+
need6(/^[a-f0-9]{64}$/.test(reviewed.sha256) && reviewed.sha256 === cli.sha256 && reviewed.version === pkg.version, "SKILLS_RELEASE_MISMATCH");
|
|
112027
|
+
need6((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
111710
112028
|
return {
|
|
111711
112029
|
receipt: { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 },
|
|
111712
112030
|
recheck() {
|
|
111713
|
-
|
|
112031
|
+
need6(Bun.which("skills", { PATH: process.env.PATH }) === normal && realpathSync16(normal) === resolved && realpathSync16(bound) === resolved && realpathSync16(entrypoint) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
111714
112032
|
unchanged2(cli);
|
|
111715
112033
|
unchanged2(manifest);
|
|
111716
112034
|
}
|
|
@@ -111721,19 +112039,19 @@ var init_codex_hook_trust_identity = __esm(() => {
|
|
|
111721
112039
|
});
|
|
111722
112040
|
|
|
111723
112041
|
// src/lib/codex-hook-policy-recovery.ts
|
|
111724
|
-
import { isDeepStrictEqual as
|
|
112042
|
+
import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
|
|
111725
112043
|
function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
|
|
111726
|
-
|
|
111727
|
-
|
|
112044
|
+
need6(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
|
|
112045
|
+
need6(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
111728
112046
|
const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
|
|
111729
112047
|
const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
|
|
111730
|
-
|
|
111731
|
-
const changes = ["roots", "sources"].filter((key) => !
|
|
111732
|
-
|
|
112048
|
+
need6(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
|
|
112049
|
+
const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual13(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
|
|
112050
|
+
need6(changes.length > 0, "RECONCILE_POLICY_CHANGED");
|
|
111733
112051
|
const compared = structuredClone(current);
|
|
111734
112052
|
compared.bridge.discovery.claude.roots = oldDiscovery.roots;
|
|
111735
112053
|
compared.bridge.discovery.claude.sources = oldDiscovery.sources;
|
|
111736
|
-
|
|
112054
|
+
need6(isDeepStrictEqual13(before2, compared), "RECONCILE_POLICY_CHANGED");
|
|
111737
112055
|
verifyAgentDiscovery(newDiscovery);
|
|
111738
112056
|
return { reason: options.reason, changes };
|
|
111739
112057
|
}
|
|
@@ -111745,21 +112063,21 @@ var init_codex_hook_policy_recovery = __esm(() => {
|
|
|
111745
112063
|
|
|
111746
112064
|
// src/lib/agent-codex-trust.ts
|
|
111747
112065
|
import { lstatSync as lstatSync31, mkdirSync as mkdirSync27, readdirSync as readdirSync23, readFileSync as readFileSync40, realpathSync as realpathSync17, statSync as statSync21 } from "fs";
|
|
111748
|
-
import { createHash as
|
|
112066
|
+
import { createHash as createHash31, randomUUID as randomUUID11 } from "crypto";
|
|
111749
112067
|
import { execFileSync } from "child_process";
|
|
111750
112068
|
import { dirname as dirname34, isAbsolute as isAbsolute28, join as join60, resolve as resolve25 } from "path";
|
|
111751
112069
|
import { homedir as homedir12 } from "os";
|
|
111752
|
-
import { isDeepStrictEqual as
|
|
112070
|
+
import { isDeepStrictEqual as isDeepStrictEqual14 } from "util";
|
|
111753
112071
|
function verifyReleaseArtifact(proof, skillsCli) {
|
|
111754
|
-
|
|
112072
|
+
need6(isAbsolute28(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
111755
112073
|
const tarPath = realpathSync17(proof.packageTarPath);
|
|
111756
|
-
|
|
112074
|
+
need6(statSync21(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
111757
112075
|
const tarBytes = readFileSync40(tarPath);
|
|
111758
|
-
|
|
112076
|
+
need6(tarBytes.byteLength === proof.packageTarBytes && createHash31("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
|
|
111759
112077
|
const packageRoot = dirname34(dirname34(realpathSync17(skillsCli.receipt.path)));
|
|
111760
112078
|
const manifest = readFileSync40(join60(packageRoot, "package.json"));
|
|
111761
|
-
|
|
111762
|
-
|
|
112079
|
+
need6(createHash31("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
|
|
112080
|
+
need6(createHash31("sha256").update(readFileSync40(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
|
|
111763
112081
|
const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync40(skillsCli.receipt.path)]];
|
|
111764
112082
|
for (const [member, expected] of members2) {
|
|
111765
112083
|
let actual;
|
|
@@ -111768,7 +112086,7 @@ function verifyReleaseArtifact(proof, skillsCli) {
|
|
|
111768
112086
|
} catch {
|
|
111769
112087
|
throw new Error("RECONCILE_RELEASE_ARTIFACT_MEMBER_MISSING");
|
|
111770
112088
|
}
|
|
111771
|
-
|
|
112089
|
+
need6(Buffer.compare(actual, expected) === 0, "RECONCILE_RELEASE_ARTIFACT_MEMBER_CHANGED");
|
|
111772
112090
|
}
|
|
111773
112091
|
}
|
|
111774
112092
|
async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc) {
|
|
@@ -111776,48 +112094,48 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111776
112094
|
try {
|
|
111777
112095
|
const home = resolve25(options.home ?? homedir12()), dataDir3 = resolve25(options.dataDir ?? getDataDirReadOnly());
|
|
111778
112096
|
const parent = resolve25(join60(dataDir3, "native-hook-trust")), journal = resolve25(options.journal);
|
|
111779
|
-
|
|
112097
|
+
need6(journal.startsWith(parent + "/") && /^[a-f0-9-]{36}$/.test(journal.slice(parent.length + 1)), "RECONCILE_JOURNAL_PATH");
|
|
111780
112098
|
const dir = lstatSync31(journal, { throwIfNoEntry: false });
|
|
111781
|
-
|
|
111782
|
-
|
|
111783
|
-
|
|
112099
|
+
need6(!!dir && dir.isDirectory() && !dir.isSymbolicLink() && dir.uid === process.getuid() && (dir.mode & 511) === 448, "UNSAFE_JOURNAL");
|
|
112100
|
+
need6(!lstatSync31(join60(journal, "receipt.json"), { throwIfNoEntry: false }), "RECONCILE_ALREADY_COMPLETE");
|
|
112101
|
+
need6(lstatSync31(join60(journal, "stopped.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_JOURNAL_INCOMPLETE");
|
|
111784
112102
|
const stoppedFile = snapshot(join60(journal, "stopped.json"), true);
|
|
111785
112103
|
const intentFile = snapshot(join60(journal, "intent.json"), true), intent = JSON.parse(intentFile.text);
|
|
111786
112104
|
const configBefore = snapshot(join60(journal, "config.before.toml"), true), hooksBefore = snapshot(join60(journal, "hooks.before.json"), true), policyBefore = snapshot(join60(journal, "policy.before.json"), true);
|
|
111787
112105
|
const legacyIntent = intent.home === undefined && intent.configSha256 === undefined && intent.configVersion === undefined && intent.declarations === undefined && intent.admitted === undefined && intent.nativeHooks === undefined;
|
|
111788
|
-
|
|
112106
|
+
need6(intent.version === 1 && /^[a-f0-9]{64}$/.test(intent.planDigest) && typeof intent.configPath === "string" && /^[a-f0-9]{64}$/.test(intent.policySha256) && /^[a-f0-9]{64}$/.test(intent.hooksSha256) && Array.isArray(intent.hooks) && intent.hooks.length > 0 && intent.hooks.length <= 100 && (legacyIntent || typeof intent.home === "string" && intent.home === home && /^[a-f0-9]{64}$/.test(intent.configSha256) && typeof intent.configVersion === "string" && Array.isArray(intent.declarations) && Array.isArray(intent.admitted) && Array.isArray(intent.nativeHooks)), "RECONCILE_INTENT_INVALID");
|
|
111789
112107
|
const declarations = intent.declarations ?? intent.hooks.map((hook) => ({ event: hook.event, key: `${join60(home, ".codex/hooks.json")}${hook.key.slice(hook.key.indexOf(".codex/hooks.json") + ".codex/hooks.json".length)}`, command: hook.command }));
|
|
111790
112108
|
const admitted = intent.admitted ?? intent.hooks;
|
|
111791
112109
|
const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
|
|
111792
112110
|
const digestInput = { version: 1, home: intent.home ?? home, configPath: intent.configPath, ...intent.nativeConfigPath !== undefined ? { nativeConfigPath: intent.nativeConfigPath } : {}, configSha256, configVersion, policySha256: intent.policySha256, hooksSha256: intent.hooksSha256, skillsCli: intent.skillsCli, nativeVersion: intent.nativeVersion, declarations, admitted };
|
|
111793
|
-
|
|
111794
|
-
|
|
111795
|
-
|
|
112111
|
+
need6(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
|
|
112112
|
+
need6(isDeepStrictEqual14(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
|
|
112113
|
+
need6(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
|
|
111796
112114
|
const policyCurrent = snapshot(join60(dataDir3, "agent-policy.json"), true);
|
|
111797
112115
|
const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
|
|
111798
112116
|
const recovery = claudeRecovery ?? additionsRecovery;
|
|
111799
|
-
|
|
111800
|
-
|
|
111801
|
-
|
|
112117
|
+
need6(!recovery || !options.supersedeBinding, "RECONCILE_RECOVERY_MODE_CONFLICT");
|
|
112118
|
+
need6(!claudeRecovery || !additionsRecovery || claudeRecovery.reviewedPlanDigest === additionsRecovery.reviewedPlanDigest, "RECONCILE_RECOVERY_MODE_CONFLICT");
|
|
112119
|
+
need6(recovery?.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(recovery.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
111802
112120
|
const policyChange = claudeRecovery ? reviewedClaudeDiscoveryChange(policyBefore.text, policyCurrent.text, claudeRecovery) : undefined;
|
|
111803
112121
|
let supersededBinding = false;
|
|
111804
112122
|
if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
|
|
111805
|
-
|
|
112123
|
+
need6(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
|
|
111806
112124
|
const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
|
|
111807
|
-
|
|
112125
|
+
need6(isDeepStrictEqual14(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
|
|
111808
112126
|
supersededBinding = true;
|
|
111809
112127
|
}
|
|
111810
112128
|
if (supersededBinding || recovery) {
|
|
111811
|
-
|
|
112129
|
+
need6(Array.isArray(intent.nativeHooks) && intent.nativeHooks.length > 0, "RECONCILE_NATIVE_INVENTORY_REQUIRED");
|
|
111812
112130
|
const proof = options.releaseProof;
|
|
111813
|
-
|
|
112131
|
+
need6(!!proof && isAbsolute28(proof.packageTarPath) && /^[a-f0-9]{64}$/.test(proof.packageTarSha256) && Number.isInteger(proof.packageTarBytes) && proof.packageTarBytes > 0 && /^[a-f0-9]{64}$/.test(proof.manifestSha256) && /^[a-f0-9]{64}$/.test(proof.executableSha256), "RECONCILE_RELEASE_PROVENANCE_REQUIRED");
|
|
111814
112132
|
}
|
|
111815
112133
|
const currentConfig = snapshot(intent.configPath, true);
|
|
111816
112134
|
const policy = parseManagedSkillPolicy(policyCurrent.text), binding = policy.bridge;
|
|
111817
112135
|
const alias = binding.rootAliases?.find((item) => item.agent === "codex"), physicalRoot = alias?.target ?? join60(home, ".codex"), hooksPath = join60(physicalRoot, "hooks.json");
|
|
111818
|
-
|
|
112136
|
+
need6(resolve25(intent.configPath) === resolve25(join60(physicalRoot, "config.toml")), "RECONCILE_CONFIG_PATH_CHANGED");
|
|
111819
112137
|
const nativeConfigPath = intent.nativeConfigPath ?? intent.configPath;
|
|
111820
|
-
|
|
112138
|
+
need6([join60(home, ".codex/config.toml"), join60(physicalRoot, "config.toml")].includes(nativeConfigPath), "RECONCILE_NATIVE_CONFIG_PATH_CHANGED");
|
|
111821
112139
|
const codexHome = nativeConfigPath !== intent.configPath ? undefined : dirname34(nativeConfigPath);
|
|
111822
112140
|
const hooksCurrent = snapshot(hooksPath);
|
|
111823
112141
|
const additions = additionsRecovery ? reviewNativeHookAdditions({ path: additionsRecovery.path, intent: intentFile, policy: policyCurrent, configBefore, configCurrent: currentConfig, hooksBefore, hooksCurrent, nativeHooksPath: join60(dirname34(nativeConfigPath), "hooks.json"), journalHooks: intent.hooks, recordedNativeHooks: intent.nativeHooks }) : undefined;
|
|
@@ -111825,7 +112143,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111825
112143
|
const managedKeys = managedHooks.map((h) => h.key);
|
|
111826
112144
|
const witnessSame = codexTrustReconcileWitness(configBefore.text, managedKeys) === codexTrustReconcileWitness(currentConfig.text, managedKeys);
|
|
111827
112145
|
const unmanagedSame = codexTrustUnmanagedSemantic(configBefore.text, managedKeys) === codexTrustUnmanagedSemantic(currentConfig.text, managedKeys);
|
|
111828
|
-
|
|
112146
|
+
need6(unmanagedSame && (witnessSame || supersededBinding), "RECONCILE_UNRELATED_CONFIG_CHANGED");
|
|
111829
112147
|
const before2 = structure3(configBefore.text), current = structure3(currentConfig.text), expectedConfig = structuredClone(before2);
|
|
111830
112148
|
expectedConfig.hooks ??= {};
|
|
111831
112149
|
expectedConfig.hooks.state ??= {};
|
|
@@ -111837,10 +112155,10 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111837
112155
|
if (supersededBinding) {
|
|
111838
112156
|
for (const hook of intent.hooks) {
|
|
111839
112157
|
const state = current.hooks?.state?.[hook.key];
|
|
111840
|
-
|
|
112158
|
+
need6(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
|
|
111841
112159
|
}
|
|
111842
112160
|
} else
|
|
111843
|
-
|
|
112161
|
+
need6(isDeepStrictEqual14(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
|
|
111844
112162
|
const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
|
|
111845
112163
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
|
|
111846
112164
|
const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
|
|
@@ -111849,29 +112167,29 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111849
112167
|
const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
111850
112168
|
if (supersededBinding)
|
|
111851
112169
|
verifyReleaseArtifact(options.releaseProof, skillsCli);
|
|
111852
|
-
const skillsBindingChanged = !
|
|
111853
|
-
|
|
112170
|
+
const skillsBindingChanged = !isDeepStrictEqual14(skillsCli.receipt, intent.skillsCli);
|
|
112171
|
+
need6(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
111854
112172
|
if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
|
|
111855
|
-
|
|
112173
|
+
need6(supersededBinding, "RECONCILE_HOOKS_CHANGED");
|
|
111856
112174
|
const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
|
|
111857
112175
|
for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
|
|
111858
112176
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
111859
|
-
|
|
112177
|
+
need6(isDeepStrictEqual14(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
111860
112178
|
}
|
|
111861
112179
|
}
|
|
111862
112180
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
111863
|
-
|
|
112181
|
+
need6(rpc.version === intent.nativeVersion, "RECONCILE_NATIVE_VERSION_CHANGED");
|
|
111864
112182
|
const discovered = await rpc.request("hooks/list", { cwds: [home] });
|
|
111865
|
-
|
|
112183
|
+
need6(Array.isArray(discovered?.data) && discovered.data.length === 1 && discovered.data[0].cwd === home && !discovered.data[0].errors?.length && !discovered.data[0].warnings?.length, "RECONCILE_NATIVE_DISCOVERY_REFUSED");
|
|
111866
112184
|
const nativeHooks = discovered.data[0].hooks;
|
|
111867
112185
|
const recordedNativeHooks = intent.nativeHooks;
|
|
111868
112186
|
if (recordedNativeHooks)
|
|
111869
|
-
|
|
112187
|
+
need6(nativeHooks.length === recordedNativeHooks.length + (additions?.hooks.length ?? 0), "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
|
|
111870
112188
|
const plannedKeys = new Set(intent.hooks.map((hook) => hook.key));
|
|
111871
112189
|
let nativeTrustPending = false;
|
|
111872
112190
|
for (const expected of [...recordedNativeHooks ?? [], ...additions?.hooks ?? []]) {
|
|
111873
112191
|
const matches2 = nativeHooks.filter((hook) => hook.key === expected.key);
|
|
111874
|
-
|
|
112192
|
+
need6(matches2.length === 1, "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
|
|
111875
112193
|
const actual = safeNativeHook(matches2[0]);
|
|
111876
112194
|
const stable = (hook) => {
|
|
111877
112195
|
const { enabled, trustStatus, ...identity5 } = hook;
|
|
@@ -111879,35 +112197,35 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111879
112197
|
delete identity5.command;
|
|
111880
112198
|
return identity5;
|
|
111881
112199
|
};
|
|
111882
|
-
|
|
112200
|
+
need6(isDeepStrictEqual14(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
111883
112201
|
if (!plannedKeys.has(expected.key))
|
|
111884
|
-
|
|
112202
|
+
need6(isDeepStrictEqual14(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
|
|
111885
112203
|
}
|
|
111886
112204
|
for (const wanted of intent.hooks) {
|
|
111887
112205
|
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join60(home, ".codex/hooks.json"), hooksPath));
|
|
111888
|
-
|
|
112206
|
+
need6(matches2.length === 1, "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
111889
112207
|
const h = matches2[0];
|
|
111890
112208
|
const trusted = h.trustStatus === "trusted";
|
|
111891
|
-
|
|
112209
|
+
need6(h.enabled === true && (trusted || supersededBinding && h.trustStatus === "modified") && (h.currentHash === wanted.currentHash || supersededBinding && /^sha256:[a-f0-9]{64}$/.test(h.currentHash)), "RECONCILE_NATIVE_STATE_INCOMPLETE");
|
|
111892
112210
|
if (!trusted)
|
|
111893
112211
|
nativeTrustPending = true;
|
|
111894
112212
|
if (supersededBinding) {
|
|
111895
112213
|
const nativeToCliEvent = { userPromptSubmit: "UserPromptSubmit", sessionStart: "SessionStart", subagentStart: "SubagentStart" };
|
|
111896
|
-
|
|
112214
|
+
need6(h.command === renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, nativeToCliEvent[wanted.event] ?? ""), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
111897
112215
|
}
|
|
111898
112216
|
}
|
|
111899
112217
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
111900
112218
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
111901
|
-
|
|
112219
|
+
need6(layers?.length === 1 && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
|
|
111902
112220
|
const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
|
|
111903
|
-
|
|
112221
|
+
need6(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual14(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
|
|
111904
112222
|
const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
111905
112223
|
const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
111906
|
-
|
|
112224
|
+
need6(finalLayers?.length === 1 && !finalLayers[0].disabledReason && codexNativeConfigEqual(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
|
|
111907
112225
|
if (recovery) {
|
|
111908
112226
|
for (const response of [config2, finalConfig]) {
|
|
111909
112227
|
const layerIndex = response.layers.findIndex((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
111910
|
-
|
|
112228
|
+
need6(response.config && response.config.features?.hooks !== false && response.layers.slice(0, layerIndex).every((layer) => layer.config?.hooks === undefined && layer.config?.features?.hooks === undefined), "RECONCILE_NATIVE_CONFIG_OVERRIDDEN");
|
|
111911
112229
|
}
|
|
111912
112230
|
recoveryCli.recheck();
|
|
111913
112231
|
verifyReleaseArtifact(options.releaseProof, recoveryCli);
|
|
@@ -111928,7 +112246,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111928
112246
|
const recoveryPlan = recovery ? { version: 1, kind: additions ? claudeRecovery ? "claude-discovery-and-native-hook-additions" : "native-hook-additions" : "claude-discovery-only", journal, journalIntentSha256: intentFile.sha256, stoppedSha256: stoppedFile.sha256, originalPlanDigest: intent.planDigest, policyBeforeSha256: policyBefore.sha256, policyCurrentSha256: policyCurrent.sha256, configBeforeSha256: configBefore.sha256, configCurrentSha256: currentConfig.sha256, ...configDiscoveryRecovery ? { configDiscoveryRecovery } : {}, ...additions ? { nativeHookAdditions: additions.receipt } : {}, hooksSha256: hooksCurrent.sha256, nativeConfigPath, nativeVersion: rpc.version, nativeInventorySha256: makePlanDigest(nativeHooks.map(safeNativeHook)), recordedNativeInventorySha256: makePlanDigest(recordedNativeHooks), boundSkillsCli: skillsCli.receipt, recoveryCli: recoveryCli.receipt, releaseProof: options.releaseProof, ...policyChange } : undefined;
|
|
111929
112247
|
const recoveryPlanDigest = recoveryPlan ? makePlanDigest(recoveryPlan) : undefined;
|
|
111930
112248
|
if (recovery?.reviewedPlanDigest !== undefined)
|
|
111931
|
-
|
|
112249
|
+
need6(recovery.reviewedPlanDigest === recoveryPlanDigest, "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
111932
112250
|
const preview2 = !!recovery && recovery.reviewedPlanDigest === undefined;
|
|
111933
112251
|
const receipt = { version: 1, status: preview2 ? "recovery-review-required" : supersededBinding ? "superseded" : "reconciled", reconciled: !preview2, supersededBinding, automaticRollback: false, journal, planDigest: intent.planDigest, nativeVersion: rpc.version, nativeStateVerified: !nativeTrustPending, nativeTrustPending, bindingRefreshRequired: !!configDiscoveryRecovery, nativeExecutionVerified: false, configSha256: currentConfig.sha256, hooksSha256: hooksCurrent.sha256, policySha256: policyBefore.sha256, policyBeforeSha256: policyBefore.sha256, policyCurrentSha256: policyCurrent.sha256, previousSkillsCli: intent.skillsCli, skillsCli: skillsCli.receipt, releaseProof: options.releaseProof, nativeInventoryScope: recordedNativeHooks ? "complete" : "declared-managed-hooks-only", unrelatedNativeHooksPreserved: !!recordedNativeHooks, unrelatedSettingsAndCommentsPreserved: true, ...recoveryPlan ? { recoveryPlan, recoveryPlanDigest } : {} };
|
|
111934
112252
|
if (preview2)
|
|
@@ -111951,7 +112269,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
111951
112269
|
const binding = policy.bridge, alias = binding.rootAliases?.find((item) => item.agent === "codex");
|
|
111952
112270
|
const skillsCli = bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
111953
112271
|
const physicalRoot = alias?.target ?? join60(home, ".codex"), hooksPath = join60(physicalRoot, "hooks.json"), configPath = join60(physicalRoot, "config.toml");
|
|
111954
|
-
|
|
112272
|
+
need6(options.codexHome === undefined || isAbsolute28(options.codexHome) && [join60(home, ".codex"), physicalRoot].includes(options.codexHome), "NATIVE_HOME_NOT_ADMITTED");
|
|
111955
112273
|
const codexHome = options.codexHome === undefined ? undefined : physicalRoot;
|
|
111956
112274
|
const nativeConfigPath = join60(codexHome ?? join60(home, ".codex"), "config.toml");
|
|
111957
112275
|
const hooksFile = snapshot(hooksPath), configFile = snapshot(configPath, true), original = structure3(configFile.text);
|
|
@@ -111959,38 +112277,38 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
111959
112277
|
for (const [event, [nativeEvent, label]] of Object.entries(eventNames)) {
|
|
111960
112278
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
111961
112279
|
const groups = declarations[event];
|
|
111962
|
-
|
|
112280
|
+
need6(Array.isArray(groups), "DECLARATION_CHANGED");
|
|
111963
112281
|
const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
|
|
111964
|
-
|
|
112282
|
+
need6(matches2.length === 1 && isDeepStrictEqual14(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
|
|
111965
112283
|
expected.push({ event: nativeEvent, key: `${join60(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
|
|
111966
112284
|
}
|
|
111967
112285
|
codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
|
|
111968
112286
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
111969
|
-
|
|
112287
|
+
need6(SUPPORTED_CODEX_HOOK_VERSIONS.includes(rpc.version), "NATIVE_UNSUPPORTED_VERSION");
|
|
111970
112288
|
const list2 = async () => {
|
|
111971
112289
|
const response = await rpc.request("hooks/list", { cwds: [home] });
|
|
111972
112290
|
const nativeRoots = [...new Set([physicalRoot, join60(home, ".codex")])];
|
|
111973
112291
|
const coexistenceWarnings = nativeRoots.flatMap((hooksRoot) => nativeRoots.map((configRoot) => `loading hooks from both ${join60(hooksRoot, "hooks.json")} and ${join60(configRoot, "config.toml")}; prefer a single representation for this layer`));
|
|
111974
112292
|
const entry = response?.data?.[0];
|
|
111975
|
-
|
|
112293
|
+
need6(Array.isArray(response?.data) && response.data.length === 1 && entry.cwd === home && Array.isArray(entry.hooks) && entry.hooks.length <= 1e4 && Array.isArray(entry.errors) && entry.errors.length === 0 && Array.isArray(entry.warnings) && entry.warnings.length <= 1 && entry.warnings.every((warning) => typeof warning === "string" && coexistenceWarnings.includes(warning)), "NATIVE_DISCOVERY_REFUSED");
|
|
111976
112294
|
return response.data[0].hooks;
|
|
111977
112295
|
};
|
|
111978
112296
|
const admit = (hooks) => expected.map((wanted) => {
|
|
111979
112297
|
const canonicalKey = wanted.key.replace(join60(home, ".codex/hooks.json"), hooksPath);
|
|
111980
112298
|
const matches2 = hooks.filter((h2) => h2.key === wanted.key || h2.key === canonicalKey);
|
|
111981
|
-
|
|
112299
|
+
need6(matches2.length === 1, "AMBIGUOUS_IDENTITY");
|
|
111982
112300
|
const h = matches2[0];
|
|
111983
|
-
|
|
111984
|
-
|
|
111985
|
-
|
|
112301
|
+
need6(h.eventName === wanted.event && h.handlerType === "command" && h.command === wanted.command && h.matcher === null && h.timeoutSec === 15 && h.async === false && h.statusMessage === null && h.additionalContextLimit === null && [hooksPath, join60(home, ".codex/hooks.json")].includes(h.sourcePath) && h.source === "user" && h.pluginId === null && h.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(h.currentHash) && typeof h.enabled === "boolean", "NATIVE_IDENTITY_CHANGED");
|
|
112302
|
+
need6(hooks.filter((other) => other.command === wanted.command).length === 1, "AMBIGUOUS_IDENTITY");
|
|
112303
|
+
need6(["trusted", "untrusted", "modified"].includes(h.trustStatus), "UNKNOWN_TRUST_STATUS");
|
|
111986
112304
|
return { key: h.key, event: wanted.event, command: wanted.command, handlerType: h.handlerType, timeoutSec: h.timeoutSec, sourcePath: h.sourcePath, currentHash: h.currentHash, enabled: h.enabled, trustStatus: h.trustStatus };
|
|
111987
112305
|
});
|
|
111988
112306
|
const discovered = await list2(), admitted = admit(discovered);
|
|
111989
112307
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
111990
112308
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
111991
|
-
|
|
111992
|
-
|
|
111993
|
-
|
|
112309
|
+
need6(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, original), "NATIVE_CONFIG_MISMATCH");
|
|
112310
|
+
need6(config2.config && config2.config.features?.hooks !== false, "NATIVE_HOOKS_DISABLED");
|
|
112311
|
+
need6(config2.layers.slice(0, config2.layers.indexOf(layers[0])).every((layer) => layer.config?.hooks === undefined && layer.config?.features?.hooks === undefined), "NATIVE_CONFIG_OVERRIDDEN");
|
|
111994
112312
|
const keys3 = admitted.map((h) => h.key), planned = admitted.filter((h) => !h.enabled || h.trustStatus !== "trusted");
|
|
111995
112313
|
const preservationText = codexTrustTextWitness(configFile.text, keys3);
|
|
111996
112314
|
const checkInputs = () => {
|
|
@@ -112005,16 +112323,16 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112005
112323
|
const planDigest = makePlanDigest(digestInput);
|
|
112006
112324
|
const baseReceipt = { planDigest, skillsCli: skillsCli.receipt, nativeVersion: rpc.version, nativeConfigPath, ownedNativeProcessId: rpc.processId, agent: "codex", applied: false, planned, nativeEligible: planned.length === 0, bindingRefreshRequired: false, transport: "owned-stdio-process", existingSessionsReloaded: false, nativeExecutionVerified: false };
|
|
112007
112325
|
if (options.apply)
|
|
112008
|
-
|
|
112326
|
+
need6(options.reviewedPlanDigest === planDigest, "PLAN_CHANGED: run the dry-run again and pass its reviewed --plan-digest with --apply");
|
|
112009
112327
|
const parent = join60(dataDir3, "native-hook-trust");
|
|
112010
112328
|
const st = lstatSync31(parent, { throwIfNoEntry: false });
|
|
112011
112329
|
if (st) {
|
|
112012
|
-
|
|
112330
|
+
need6(st.isDirectory() && !st.isSymbolicLink() && st.uid === process.getuid() && (st.mode & 511) === 448, "UNSAFE_JOURNAL");
|
|
112013
112331
|
const priorJournals = readdirSync23(parent);
|
|
112014
|
-
|
|
112332
|
+
need6(priorJournals.length < 1000, "JOURNAL_BOUND");
|
|
112015
112333
|
for (const name of priorJournals) {
|
|
112016
|
-
|
|
112017
|
-
|
|
112334
|
+
need6(/^[a-f0-9-]{36}$/.test(name), "UNSAFE_JOURNAL");
|
|
112335
|
+
need6(lstatSync31(join60(parent, name, "receipt.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_REQUIRED: inspect the incomplete private journal before applying again");
|
|
112018
112336
|
}
|
|
112019
112337
|
}
|
|
112020
112338
|
if (!options.apply || !planned.length)
|
|
@@ -112027,7 +112345,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112027
112345
|
save(join60(journal, "hooks.before.json"), hooksFile.bytes);
|
|
112028
112346
|
save(join60(journal, "policy.before.json"), policyFile.bytes);
|
|
112029
112347
|
save(join60(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
|
|
112030
|
-
|
|
112348
|
+
need6(isDeepStrictEqual14(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
|
|
112031
112349
|
checkInputs();
|
|
112032
112350
|
let result2;
|
|
112033
112351
|
try {
|
|
@@ -112035,22 +112353,22 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112035
112353
|
} catch {
|
|
112036
112354
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_WRITE_FAILED: reconcile the private journal before retrying");
|
|
112037
112355
|
}
|
|
112038
|
-
|
|
112356
|
+
need6(result2?.status === "ok" && result2.filePath === configPath && /^sha256:[a-f0-9]{64}$/.test(result2.version), "NATIVE_WRITE_OVERRIDDEN");
|
|
112039
112357
|
const after2 = snapshot(configPath, true), expectedConfig = structuredClone(original);
|
|
112040
112358
|
expectedConfig.hooks ??= {};
|
|
112041
112359
|
expectedConfig.hooks.state ??= {};
|
|
112042
112360
|
for (const h of planned)
|
|
112043
112361
|
expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
|
|
112044
|
-
|
|
112362
|
+
need6(isDeepStrictEqual14(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
|
|
112045
112363
|
const savedConfig = snapshot(join60(journal, "config.before.toml"), true);
|
|
112046
|
-
|
|
112364
|
+
need6(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
|
|
112047
112365
|
const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
|
|
112048
112366
|
unchanged2(policyFile);
|
|
112049
112367
|
unchanged2(hooksFile);
|
|
112050
112368
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
|
|
112051
112369
|
const current = await list2(), confirmed = admit(current);
|
|
112052
|
-
|
|
112053
|
-
|
|
112370
|
+
need6(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual14(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
|
|
112371
|
+
need6(isDeepStrictEqual14(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
|
|
112054
112372
|
unchanged2(after2);
|
|
112055
112373
|
unchanged2(savedConfig);
|
|
112056
112374
|
unchanged2(hooksFile);
|
|
@@ -112073,7 +112391,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112073
112391
|
}
|
|
112074
112392
|
}
|
|
112075
112393
|
var json2 = (value) => JSON.stringify(value, null, 2) + `
|
|
112076
|
-
`, structure3 = (text4) => JSON.parse(JSON.stringify(Bun.TOML.parse(text4))), eventNames, makePlanDigest = (value) =>
|
|
112394
|
+
`, structure3 = (text4) => JSON.parse(JSON.stringify(Bun.TOML.parse(text4))), eventNames, makePlanDigest = (value) => createHash31("sha256").update(json2(value)).digest("hex"), safeNativeHook = (hook) => ({ key: hook.key, eventName: hook.eventName, handlerType: hook.handlerType, command: hook.command, matcher: hook.matcher, timeoutSec: hook.timeoutSec, async: hook.async, statusMessage: hook.statusMessage, additionalContextLimit: hook.additionalContextLimit, sourcePath: hook.sourcePath, source: hook.source, pluginId: hook.pluginId, isManaged: hook.isManaged, currentHash: hook.currentHash, enabled: hook.enabled, trustStatus: hook.trustStatus }), withoutCodexCommand = (value) => {
|
|
112077
112395
|
const copy = structuredClone(value);
|
|
112078
112396
|
if (copy?.bridge?.commands && Object.hasOwn(copy.bridge.commands, "codex"))
|
|
112079
112397
|
copy.bridge.commands.codex = "<reviewed-codex-binding>";
|
|
@@ -112213,7 +112531,7 @@ __export(exports_agent_integration, {
|
|
|
112213
112531
|
registerAgentIntegration: () => registerAgentIntegration
|
|
112214
112532
|
});
|
|
112215
112533
|
import { existsSync as existsSync35, lstatSync as lstatSync32, readFileSync as readFileSync41, writeFileSync as writeFileSync26 } from "fs";
|
|
112216
|
-
import { createHash as
|
|
112534
|
+
import { createHash as createHash32 } from "crypto";
|
|
112217
112535
|
import { homedir as homedir13 } from "os";
|
|
112218
112536
|
import { isAbsolute as isAbsolute29 } from "path";
|
|
112219
112537
|
function pinnedHookProfile(input3, configuredProfile) {
|
|
@@ -112311,7 +112629,7 @@ function registerAgentIntegration(parent) {
|
|
|
112311
112629
|
skillCount: catalog.skills.length,
|
|
112312
112630
|
output: options.output,
|
|
112313
112631
|
bytes: Buffer.byteLength(bytes),
|
|
112314
|
-
sha256:
|
|
112632
|
+
sha256: createHash32("sha256").update(bytes).digest("hex")
|
|
112315
112633
|
};
|
|
112316
112634
|
await writeCliOutput(options.json ? JSON.stringify(receipt) : `Captured ${receipt.skillCount} native Codex skill(s) in ${receipt.output}.`);
|
|
112317
112635
|
} catch (error61) {
|
|
@@ -112320,10 +112638,10 @@ function registerAgentIntegration(parent) {
|
|
|
112320
112638
|
process.exitCode = 1;
|
|
112321
112639
|
}
|
|
112322
112640
|
});
|
|
112323
|
-
hook.command("witness").requiredOption("--kind <kind>", "claude-settings-v3, claude-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2").requiredOption("--path <path>", "Canonical absolute path to the reviewed settings or registry file").option("--json", "Output the discovery witness as JSON", false).description("Capture an explicit versioned review witness without installing it").action(async (options) => {
|
|
112324
|
-
const capture2 = options.kind === "codex-settings-v1" ? captureCodexSettings : options.kind === "claude-settings-v3" ? captureClaudeSettingsV3 : options.kind === "claude-settings-v2" ? captureClaudeSettingsV2 : options.kind === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : null;
|
|
112641
|
+
hook.command("witness").requiredOption("--kind <kind>", "claude-settings-v3, claude-settings-v2, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2").requiredOption("--path <path>", "Canonical absolute path to the reviewed settings or registry file").option("--json", "Output the discovery witness as JSON", false).description("Capture an explicit versioned review witness without installing it").action(async (options) => {
|
|
112642
|
+
const capture2 = options.kind === "codex-settings-v2" ? captureCodexSettingsV2 : options.kind === "codex-settings-v1" ? captureCodexSettings : options.kind === "claude-settings-v3" ? captureClaudeSettingsV3 : options.kind === "claude-settings-v2" ? captureClaudeSettingsV2 : options.kind === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : null;
|
|
112325
112643
|
if (!capture2)
|
|
112326
|
-
throw new Error("Unsupported witness kind; select claude-settings-v3, claude-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2");
|
|
112644
|
+
throw new Error("Unsupported witness kind; select claude-settings-v3, claude-settings-v2, codex-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2");
|
|
112327
112645
|
await writeCliOutput(JSON.stringify(capture2(options.path), null, 2));
|
|
112328
112646
|
});
|
|
112329
112647
|
hook.command("rebind-settings").requiredOption("--agent <agent>", "claude or codex").requiredOption("--reviewed-preimage <path>", "Exact preserved legacy settings.json or config.toml").requiredOption("--expected-policy-sha256 <sha256>", "Exact current managed policy bytes").requiredOption("--expected-settings-sha256 <sha256>", "Exact current native settings bytes").option("--apply", "Apply the explicit semantic witness upgrade with preservation and readback", false).option("--json", "Return the metadata-only plan or receipt", false).description("Explicitly migrate a legacy native settings witness after proving only known preferences changed").action(async (options) => {
|
|
@@ -112342,7 +112660,7 @@ function registerAgentIntegration(parent) {
|
|
|
112342
112660
|
hook.command("agents").option("--json", "Output the adapter capability inventory", false).description("Show maintained native adapters and explicit coverage limits").action(async () => {
|
|
112343
112661
|
await writeCliOutput(JSON.stringify({ agents: INTEGRATION_AGENTS.map((agent) => ({ agent, bridge: true, ...AGENT_ADAPTERS[agent] })), inventoryOnly: ["codewith", "windsurf", "pi", "amp", "cline", "roo", "copilot"], limitations: ["Cursor prompt hooks gate submission; selected context is injected at session start only.", "Native discovery checks cover known home roots and current project ancestors. External plugin hook injection and arbitrary added directories require separate review.", "Hermes injects selected prompt context, but native pre_llm_call fails open. Exact native hook trust, bundled reseeding opt-out, native payload retirement and a supervised pre-tool guard are required. Child failures block explicitly; native host/supervisor death is not a universal fail-closed guarantee.", "Restart agents and use their normal hook trust controls after installation."] }, null, 2));
|
|
112344
112662
|
});
|
|
112345
|
-
hook.command("install").option("--agent <agent>", `Agent to configure: ${INTEGRATION_AGENTS.join(", ")}, all`, "all").option("--command <path>", "Skills executable used by the hook (preserves existing binding; new agents use skills)").option("--selection-profile <id>", "Selection profile (preserves existing binding; new agents use default)").option("--include-vendor", "Retained for compatibility; vendor system skills are always inventoried and disabled", false).option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes for unsupported registrations").option("--codex-native-catalog <file>", "Reviewed
|
|
112663
|
+
hook.command("install").option("--agent <agent>", `Agent to configure: ${INTEGRATION_AGENTS.join(", ")}, all`, "all").option("--command <path>", "Skills executable used by the hook (preserves existing binding; new agents use skills)").option("--selection-profile <id>", "Selection profile (preserves existing binding; new agents use default)").option("--include-vendor", "Retained for compatibility; vendor system skills are always inventoried and disabled", false).option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes for unsupported registrations").option("--codex-native-catalog <file>", "Reviewed supported Codex skill and installed-plugin receipt for exact qualified-name disables").option("--reviewed-cache-alias <path>", "Exact skill-containing vendor cache alias reviewed for this hook plan").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Apply the plan, preserving prior configuration in private backups", false).option("--json", "Output a receipt as JSON", false).description("Plan or install one Skills CLI bridge plus native prompt hooks").action(async (options) => {
|
|
112346
112664
|
try {
|
|
112347
112665
|
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync41(options.discoveryInputs, "utf8")) : undefined;
|
|
112348
112666
|
const plan = planAgentIntegration({ projectDir: process.cwd(), agents: agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, codexNativeCatalog: options.codexNativeCatalog ? JSON.parse(readFileSync41(options.codexNativeCatalog, "utf8")) : undefined });
|
|
@@ -112825,7 +113143,7 @@ var init_grants = __esm(() => {
|
|
|
112825
113143
|
});
|
|
112826
113144
|
|
|
112827
113145
|
// src/lib/station-hydrate.ts
|
|
112828
|
-
import { createHash as
|
|
113146
|
+
import { createHash as createHash33 } from "crypto";
|
|
112829
113147
|
import {
|
|
112830
113148
|
copyFileSync as copyFileSync3,
|
|
112831
113149
|
mkdirSync as mkdirSync28,
|
|
@@ -113028,7 +113346,7 @@ function skillSha256(skill) {
|
|
|
113028
113346
|
return sha256File(skill.files[0].winner.fullPath);
|
|
113029
113347
|
}
|
|
113030
113348
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
113031
|
-
return
|
|
113349
|
+
return createHash33("sha256").update(joined.sort().join(`
|
|
113032
113350
|
`)).digest("hex");
|
|
113033
113351
|
}
|
|
113034
113352
|
function writeStationHydration(options) {
|
|
@@ -113185,10 +113503,10 @@ var init_hydrate = __esm(() => {
|
|
|
113185
113503
|
});
|
|
113186
113504
|
|
|
113187
113505
|
// src/lib/prepare-skill.ts
|
|
113188
|
-
import { createHash as
|
|
113506
|
+
import { createHash as createHash34, randomUUID as randomUUID12 } from "crypto";
|
|
113189
113507
|
import { chmodSync as chmodSync5, existsSync as existsSync36, lstatSync as lstatSync33, mkdirSync as mkdirSync29, mkdtempSync as mkdtempSync6, readFileSync as readFileSync43, readdirSync as readdirSync25, renameSync as renameSync14, rmSync as rmSync9, writeFileSync as writeFileSync30 } from "fs";
|
|
113190
113508
|
import { tmpdir as tmpdir2 } from "os";
|
|
113191
|
-
import { dirname as dirname36, isAbsolute as isAbsolute30, join as join62, relative as
|
|
113509
|
+
import { dirname as dirname36, isAbsolute as isAbsolute30, join as join62, relative as relative11, resolve as resolve27, sep as sep14 } from "path";
|
|
113192
113510
|
function prepareSkill(name, options) {
|
|
113193
113511
|
const normalized = normalizePortableSkillName(name);
|
|
113194
113512
|
if (!new RegExp(SEMVER_PATTERN).test(options.version))
|
|
@@ -113202,7 +113520,7 @@ function prepareSkill(name, options) {
|
|
|
113202
113520
|
let manifest;
|
|
113203
113521
|
try {
|
|
113204
113522
|
const parsed = JSON.parse(manifestFile.bytes.toString("utf8"));
|
|
113205
|
-
if (!
|
|
113523
|
+
if (!isRecord9(parsed))
|
|
113206
113524
|
throw new Error("not an object");
|
|
113207
113525
|
manifest = parsed;
|
|
113208
113526
|
} catch {
|
|
@@ -113224,7 +113542,7 @@ function prepareSkill(name, options) {
|
|
|
113224
113542
|
if (documentKind !== undefined && documentKind !== kind) {
|
|
113225
113543
|
throw new Error("SKILL.md kind conflicts with the candidate skill.json kind. Review and align both declarations (or remove the optional frontmatter kind), then prepare again; no files were changed.");
|
|
113226
113544
|
}
|
|
113227
|
-
if (manifest.provenance !== undefined && !
|
|
113545
|
+
if (manifest.provenance !== undefined && !isRecord9(manifest.provenance))
|
|
113228
113546
|
throw new Error("skill.json provenance must be an object.");
|
|
113229
113547
|
const provenance = manifest.provenance;
|
|
113230
113548
|
const candidate2 = { ...manifest, version: options.version, kind, provenance: { ...provenance } };
|
|
@@ -113289,17 +113607,17 @@ function escapedRelativePath(path) {
|
|
|
113289
113607
|
return JSON.stringify(bounded);
|
|
113290
113608
|
}
|
|
113291
113609
|
function validateRuntimeEntrypoint(candidate2, root2) {
|
|
113292
|
-
if (candidate2.kind !== "executable" || !
|
|
113610
|
+
if (candidate2.kind !== "executable" || !isRecord9(candidate2.runtime))
|
|
113293
113611
|
return;
|
|
113294
113612
|
const entry = candidate2.runtime.entrypoint;
|
|
113295
113613
|
if (typeof entry !== "string")
|
|
113296
113614
|
return;
|
|
113297
|
-
const target = resolve27(root2, entry), rel =
|
|
113615
|
+
const target = resolve27(root2, entry), rel = relative11(root2, target);
|
|
113298
113616
|
if (isAbsolute30(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep14}`) || isAbsolute30(rel) || !existsSync36(target) || !lstatSync33(target).isFile()) {
|
|
113299
113617
|
throw new Error("Executable runtime.entrypoint must name an existing regular file inside the skill directory.");
|
|
113300
113618
|
}
|
|
113301
113619
|
}
|
|
113302
|
-
function
|
|
113620
|
+
function isRecord9(value) {
|
|
113303
113621
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
113304
113622
|
}
|
|
113305
113623
|
function validateRawCandidate(candidate2) {
|
|
@@ -113324,7 +113642,7 @@ function validateRawCandidate(candidate2) {
|
|
|
113324
113642
|
if (!Array.isArray(items))
|
|
113325
113643
|
throw new Error(`skill.json ${field2} must be an array.`);
|
|
113326
113644
|
for (const item of items) {
|
|
113327
|
-
if (!
|
|
113645
|
+
if (!isRecord9(item) || typeof item.name !== "string" || !item.name.trim())
|
|
113328
113646
|
throw new Error(`Invalid skill.json ${field2} name.`);
|
|
113329
113647
|
if (field2 === "inputs" && (typeof item.type !== "string" || !item.type.trim() || item.required !== undefined && typeof item.required !== "boolean"))
|
|
113330
113648
|
throw new Error("Invalid skill.json input type or required flag.");
|
|
@@ -113343,7 +113661,7 @@ function validateRawCandidate(candidate2) {
|
|
|
113343
113661
|
}
|
|
113344
113662
|
}
|
|
113345
113663
|
function validateRuntimeEntrypointCompatibility(candidate2) {
|
|
113346
|
-
if (candidate2.kind !== "executable" || !
|
|
113664
|
+
if (candidate2.kind !== "executable" || !isRecord9(candidate2.runtime))
|
|
113347
113665
|
return;
|
|
113348
113666
|
const runtime = candidate2.runtime.runtime;
|
|
113349
113667
|
const entrypoint = candidate2.runtime.entrypoint;
|
|
@@ -113384,13 +113702,13 @@ function snapshot2(root2) {
|
|
|
113384
113702
|
if (rootStat.isSymbolicLink() || !rootStat.isDirectory())
|
|
113385
113703
|
throw new Error("Skill root must be a real directory, not a symlink.");
|
|
113386
113704
|
const files = [], directories = [];
|
|
113387
|
-
const hash6 =
|
|
113705
|
+
const hash6 = createHash34("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
|
|
113388
113706
|
let count = 0, bytes = 0;
|
|
113389
|
-
const walk2 = (
|
|
113390
|
-
for (const name of readdirSync25(join62(root2,
|
|
113391
|
-
if (IGNORED.has(name.toLowerCase()) || !
|
|
113707
|
+
const walk2 = (relative12) => {
|
|
113708
|
+
for (const name of readdirSync25(join62(root2, relative12)).sort()) {
|
|
113709
|
+
if (IGNORED.has(name.toLowerCase()) || !relative12 && ROOT_IGNORED.has(name.toLowerCase()))
|
|
113392
113710
|
continue;
|
|
113393
|
-
const path =
|
|
113711
|
+
const path = relative12 ? `${relative12}/${name}` : name;
|
|
113394
113712
|
if (++count > 20000)
|
|
113395
113713
|
throw new Error("Skill exceeds the preparation entry limit (20000).");
|
|
113396
113714
|
const absolute = join62(root2, path), stat3 = lstatSync33(absolute);
|
|
@@ -113809,7 +114127,7 @@ var init_schedule = __esm(() => {
|
|
|
113809
114127
|
|
|
113810
114128
|
// src/lib/registry-sync.ts
|
|
113811
114129
|
import { mkdirSync as mkdirSync30, writeFileSync as writeFileSync31 } from "fs";
|
|
113812
|
-
import { dirname as dirname37, relative as
|
|
114130
|
+
import { dirname as dirname37, relative as relative12 } from "path";
|
|
113813
114131
|
function createRegistrySyncArtifact(options = {}) {
|
|
113814
114132
|
const profile = options.profile ?? "all";
|
|
113815
114133
|
const includeDocs = options.includeDocs ?? true;
|
|
@@ -113821,7 +114139,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
113821
114139
|
const registry2 = [...loadRegistryProfile(profile)].sort((a, b) => a.name.localeCompare(b.name));
|
|
113822
114140
|
const skills = registry2.map((skill) => {
|
|
113823
114141
|
const skillPath = getSkillPath(skill.name);
|
|
113824
|
-
const directory2 =
|
|
114142
|
+
const directory2 = relative12(process.cwd(), skillPath) || skillPath;
|
|
113825
114143
|
const validation = includeValidation ? validateSkillDirectory(skill.name, skillPath, skill) : undefined;
|
|
113826
114144
|
const docs = includeDocs ? buildDocs(skill.name) : undefined;
|
|
113827
114145
|
return {
|
|
@@ -113887,7 +114205,7 @@ var init_registry_sync = __esm(() => {
|
|
|
113887
114205
|
|
|
113888
114206
|
// src/lib/review-export.ts
|
|
113889
114207
|
import { constants as constants22, closeSync as closeSync25, existsSync as existsSync37, fstatSync as fstatSync25, fsyncSync as fsyncSync7, lstatSync as lstatSync34, openSync as openSync25, realpathSync as realpathSync18, statSync as statSync23, writeFileSync as writeFileSync32 } from "fs";
|
|
113890
|
-
import { basename as
|
|
114208
|
+
import { basename as basename16, dirname as dirname38, isAbsolute as isAbsolute31, join as join63, parse as parse12, resolve as resolve28, sep as sep15 } from "path";
|
|
113891
114209
|
function exactVersion(spec) {
|
|
113892
114210
|
const at2 = spec.indexOf("@");
|
|
113893
114211
|
const slug2 = spec.slice(0, at2);
|
|
@@ -113898,7 +114216,7 @@ function exactVersion(spec) {
|
|
|
113898
114216
|
return { slug: slug2, version: version2 };
|
|
113899
114217
|
}
|
|
113900
114218
|
function assertPrivateDestination(output2) {
|
|
113901
|
-
if (!isAbsolute31(output2) || resolve28(output2) !== output2 || !
|
|
114219
|
+
if (!isAbsolute31(output2) || resolve28(output2) !== output2 || !basename16(output2).endsWith(".tar.gz")) {
|
|
113902
114220
|
throw new Error("Review output must be an absolute, normalized .tar.gz path.");
|
|
113903
114221
|
}
|
|
113904
114222
|
const parent = dirname38(output2);
|
|
@@ -114670,7 +114988,7 @@ async function apiRequest(path, options, instance) {
|
|
|
114670
114988
|
const origin = instance ?? getApiUrl(`${(options?.method || "GET").toUpperCase()} ${path}`);
|
|
114671
114989
|
return new RemoteSkillsAuthClient(origin).request(path, options);
|
|
114672
114990
|
}
|
|
114673
|
-
function
|
|
114991
|
+
function isRecord10(value) {
|
|
114674
114992
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
114675
114993
|
}
|
|
114676
114994
|
function commandErrorPayload(err, fallback) {
|
|
@@ -114684,11 +115002,11 @@ function commandErrorPayload(err, fallback) {
|
|
|
114684
115002
|
...err.apiUrl ? { apiUrl: err.apiUrl } : {}
|
|
114685
115003
|
};
|
|
114686
115004
|
}
|
|
114687
|
-
const code2 =
|
|
115005
|
+
const code2 = isRecord10(err) && typeof err.code === "string" ? err.code : undefined;
|
|
114688
115006
|
return {
|
|
114689
115007
|
error: err?.message || fallback,
|
|
114690
115008
|
...code2 ? { code: code2 } : {},
|
|
114691
|
-
...
|
|
115009
|
+
...isRecord10(err) && Array.isArray(err.next) ? { next: err.next } : {}
|
|
114692
115010
|
};
|
|
114693
115011
|
}
|
|
114694
115012
|
function writeCommandError(err, fallback, json3) {
|
|
@@ -114713,7 +115031,7 @@ function stringField2(value) {
|
|
|
114713
115031
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
114714
115032
|
}
|
|
114715
115033
|
function recordField(value) {
|
|
114716
|
-
return
|
|
115034
|
+
return isRecord10(value) ? value : undefined;
|
|
114717
115035
|
}
|
|
114718
115036
|
function identityApiKey(live) {
|
|
114719
115037
|
const root2 = recordField(live);
|
|
@@ -115186,7 +115504,7 @@ async function runWhoami(options) {
|
|
|
115186
115504
|
} catch {}
|
|
115187
115505
|
payload.permissions = { publish: access?.permissions?.publish ?? null, profilesWrite: access?.permissions?.profilesWrite ?? null };
|
|
115188
115506
|
const apiKey = identityApiKey(res);
|
|
115189
|
-
if (access?.scopes && (apiKey === undefined ||
|
|
115507
|
+
if (access?.scopes && (apiKey === undefined || isRecord10(apiKey) && !Object.hasOwn(apiKey, "scopes"))) {
|
|
115190
115508
|
payload.scopes = access.scopes;
|
|
115191
115509
|
}
|
|
115192
115510
|
if (options.json) {
|