@hasna/skills 0.10.20 → 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 +39 -8
- package/bin/index.js +603 -349
- 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 +1 -1
- package/dist/index.js +417 -172
- 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 +4 -0
- package/dist/lib/codex-plugin-skill-controls.d.ts +1 -0
- 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 +488 -244
- 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
|
|
@@ -51062,7 +51086,11 @@ function projectCodexInstalledPlugins(response) {
|
|
|
51062
51086
|
for (const value of marketplace.plugins) {
|
|
51063
51087
|
if (!object4(value) || !scalar(value.id, MAX_PLUGIN_ID_BYTES) || !scalar(value.name, MAX_NAME_BYTES) || value.id !== `${value.name}@${marketplace.name}`)
|
|
51064
51088
|
refuse();
|
|
51065
|
-
plugins.push({
|
|
51089
|
+
plugins.push({
|
|
51090
|
+
...value,
|
|
51091
|
+
marketplace: marketplace.name,
|
|
51092
|
+
sourceType: object4(value.source) && value.source.type === "remote" ? "remote" : undefined
|
|
51093
|
+
});
|
|
51066
51094
|
}
|
|
51067
51095
|
}
|
|
51068
51096
|
return projectCodexInstalledPluginEntries(plugins);
|
|
@@ -51072,13 +51100,21 @@ function projectCodexInstalledPluginEntries(value) {
|
|
|
51072
51100
|
refuse();
|
|
51073
51101
|
const ids = new Set;
|
|
51074
51102
|
return value.map((item) => {
|
|
51075
|
-
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))
|
|
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")
|
|
51076
51104
|
refuse();
|
|
51077
51105
|
const at2 = item.id.lastIndexOf("@");
|
|
51078
51106
|
if (at2 < 1 || item.id.slice(0, at2) !== item.name || at2 === item.id.length - 1 || ids.has(item.id))
|
|
51079
51107
|
refuse();
|
|
51080
51108
|
ids.add(item.id);
|
|
51081
|
-
return {
|
|
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
|
+
};
|
|
51082
51118
|
});
|
|
51083
51119
|
}
|
|
51084
51120
|
async function captureCodexNativeSkillCatalog(options, connect = connectCodexHookRpc) {
|
|
@@ -51091,7 +51127,7 @@ async function captureCodexNativeSkillCatalog(options, connect = connectCodexHoo
|
|
|
51091
51127
|
...options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }
|
|
51092
51128
|
});
|
|
51093
51129
|
try {
|
|
51094
|
-
if (!
|
|
51130
|
+
if (!supportsCodexNativeCapability(rpc.version, "qualified-skill-catalog"))
|
|
51095
51131
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_UNSUPPORTED_VERSION");
|
|
51096
51132
|
const response = await rpc.request("skills/list", { cwds: [options.cwd], forceReload: true });
|
|
51097
51133
|
const installed = await rpc.request("plugin/installed", { cwds: [options.cwd] });
|
|
@@ -51142,10 +51178,10 @@ function isCodexNativeSkillDisabled(skill, rules) {
|
|
|
51142
51178
|
}
|
|
51143
51179
|
return disabled;
|
|
51144
51180
|
}
|
|
51145
|
-
var MAX_SKILLS = 4096, MAX_NAME_BYTES = 1024, MAX_PATH_BYTES = 16384, MAX_PLUGIN_ID_BYTES = 1024,
|
|
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);
|
|
51146
51182
|
var init_codex_native_skill_catalog = __esm(() => {
|
|
51147
51183
|
init_codex_hook_rpc();
|
|
51148
|
-
|
|
51184
|
+
init_codex_native_compatibility();
|
|
51149
51185
|
});
|
|
51150
51186
|
|
|
51151
51187
|
// src/lib/codex-plugin-skill-controls.ts
|
|
@@ -51154,6 +51190,21 @@ import { lstatSync as lstatSync12, readdirSync as readdirSync6, readlinkSync as
|
|
|
51154
51190
|
function refuse2() {
|
|
51155
51191
|
throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
|
|
51156
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
|
+
}
|
|
51157
51208
|
function appControls(path, read2) {
|
|
51158
51209
|
const text2 = read2(path);
|
|
51159
51210
|
let value, sha2562;
|
|
@@ -51243,6 +51294,10 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
|
|
|
51243
51294
|
for (const parent of new Set(controls.map((control) => control.pluginParent))) {
|
|
51244
51295
|
if (!parent.startsWith(cache3 + sep4) || !lstatSync12(parent, { throwIfNoEntry: false })?.isDirectory())
|
|
51245
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
|
+
}
|
|
51246
51301
|
const names = readdirSync6(parent);
|
|
51247
51302
|
entries += names.length;
|
|
51248
51303
|
if (entries > 4096)
|
|
@@ -51269,7 +51324,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
|
|
|
51269
51324
|
}
|
|
51270
51325
|
}
|
|
51271
51326
|
function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2, rules) {
|
|
51272
|
-
if (catalog?.version
|
|
51327
|
+
if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd2)
|
|
51273
51328
|
refuse2();
|
|
51274
51329
|
const skills = projectCodexNativeSkillCatalog({ data: [{ cwd: cwd2, errors: [], skills: catalog.skills }] }, cwd2);
|
|
51275
51330
|
const allowed = new Set(documents), result2 = [];
|
|
@@ -51296,20 +51351,28 @@ function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2,
|
|
|
51296
51351
|
const listed = skills.filter((skill) => skill.path === document3);
|
|
51297
51352
|
if (listed.length > 1)
|
|
51298
51353
|
refuse2();
|
|
51354
|
+
let remotePluginId;
|
|
51299
51355
|
if (listed.length === 1) {
|
|
51300
51356
|
if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
|
|
51301
51357
|
refuse2();
|
|
51302
51358
|
} else {
|
|
51303
|
-
const installed = installedPlugins.filter((
|
|
51304
|
-
if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled
|
|
51359
|
+
const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
|
|
51360
|
+
if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
|
|
51305
51361
|
refuse2();
|
|
51306
|
-
|
|
51307
|
-
|
|
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
|
+
}
|
|
51308
51371
|
const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document3 && rule?.enabled === false);
|
|
51309
51372
|
if (!exactPathDeny || !isCodexNativeSkillDisabled({ name: parsed.name, path: document3, pluginId: expectedPluginId, enabled: true }, rules))
|
|
51310
51373
|
refuse2();
|
|
51311
51374
|
}
|
|
51312
|
-
result2.push({ ...parsed, pluginId: expectedPluginId });
|
|
51375
|
+
result2.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
|
|
51313
51376
|
}
|
|
51314
51377
|
if (!result2.length || new Set(result2.map((item) => item.name)).size !== result2.length)
|
|
51315
51378
|
refuse2();
|
|
@@ -51325,7 +51388,7 @@ function isReviewedCodexPluginSkillDisabled(document3, cache3, controls, rules,
|
|
|
51325
51388
|
return false;
|
|
51326
51389
|
}
|
|
51327
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);
|
|
51328
|
-
if (!reviewed)
|
|
51391
|
+
if (!reviewed || reviewed.remotePluginId !== undefined && !remoteInstallationMatches(reviewed.pluginParent, reviewed.remotePluginId, read2))
|
|
51329
51392
|
return false;
|
|
51330
51393
|
return isCodexNativeSkillDisabled({ name: parsed.name, path: document3, pluginId: reviewed.pluginId, enabled: true }, rules);
|
|
51331
51394
|
}
|
|
@@ -51346,10 +51409,11 @@ enabled = false
|
|
|
51346
51409
|
var identifier2 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v), CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
|
|
51347
51410
|
var init_codex_plugin_skill_controls = __esm(() => {
|
|
51348
51411
|
init_claude_settings_witness();
|
|
51412
|
+
init_codex_native_compatibility();
|
|
51349
51413
|
init_codex_native_skill_catalog();
|
|
51350
51414
|
});
|
|
51351
51415
|
|
|
51352
|
-
// src/lib/codex-settings-witness.ts
|
|
51416
|
+
// src/lib/codex-settings-witness-v1.ts
|
|
51353
51417
|
import { createHash as createHash9 } from "crypto";
|
|
51354
51418
|
function need3(value) {
|
|
51355
51419
|
if (!value)
|
|
@@ -51440,21 +51504,173 @@ function captureCodexSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
|
51440
51504
|
const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
51441
51505
|
return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
|
|
51442
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
|
+
}
|
|
51443
51654
|
function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
|
|
51444
|
-
|
|
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));
|
|
51445
51656
|
const budget = { remaining: 2 * 1024 * 1024 };
|
|
51446
51657
|
const before2 = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
|
|
51447
|
-
|
|
51448
|
-
const sha2562 =
|
|
51449
|
-
if (
|
|
51658
|
+
need4((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before2) : sha2(before2)) === previous.sha256);
|
|
51659
|
+
const sha2562 = settingsDigest2(current);
|
|
51660
|
+
if (settingsDigest2(before2) !== sha2562)
|
|
51450
51661
|
throw new Error("Codex non-preference settings changed; explicit discovery review required");
|
|
51451
|
-
return { path: previous.path, hashMode: "codex-settings-
|
|
51662
|
+
return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
|
|
51452
51663
|
}
|
|
51453
|
-
var
|
|
51664
|
+
var sha2 = (text2) => createHash10("sha256").update(text2).digest("hex"), EFFORT2, VERBOSITY2, MODEL, CODEX_DISCOVERY_PROJECTION_FIELDS, INFERENCE;
|
|
51454
51665
|
var init_codex_settings_witness = __esm(() => {
|
|
51666
|
+
init_codex_settings_witness_v1();
|
|
51667
|
+
init_codex_settings_witness_v1();
|
|
51455
51668
|
init_claude_settings_witness();
|
|
51456
|
-
|
|
51457
|
-
|
|
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"];
|
|
51458
51674
|
});
|
|
51459
51675
|
|
|
51460
51676
|
// src/lib/managed-policy.ts
|
|
@@ -51543,7 +51759,7 @@ var init_managed_policy = __esm(() => {
|
|
|
51543
51759
|
});
|
|
51544
51760
|
|
|
51545
51761
|
// src/lib/agent-bridge.ts
|
|
51546
|
-
import { createHash as
|
|
51762
|
+
import { createHash as createHash11 } from "crypto";
|
|
51547
51763
|
import { lstatSync as lstatSync14, readFileSync as readFileSync12, readdirSync as readdirSync7 } from "fs";
|
|
51548
51764
|
import { join as join20 } from "path";
|
|
51549
51765
|
function isOwnedCliBridge(path, expectedPaths) {
|
|
@@ -51577,7 +51793,7 @@ Use the Skills CLI as the skill authority for this station.
|
|
|
51577
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.
|
|
51578
51794
|
`, CLI_BRIDGE_DIGEST, CLI_BRIDGE_FILES;
|
|
51579
51795
|
var init_agent_bridge = __esm(() => {
|
|
51580
|
-
CLI_BRIDGE_DIGEST =
|
|
51796
|
+
CLI_BRIDGE_DIGEST = createHash11("sha256").update(markdown).digest("hex");
|
|
51581
51797
|
CLI_BRIDGE_FILES = Object.freeze({
|
|
51582
51798
|
"SKILL.md": markdown,
|
|
51583
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)}
|
|
@@ -51856,7 +52072,8 @@ var init_agent_codex = __esm(() => {
|
|
|
51856
52072
|
});
|
|
51857
52073
|
|
|
51858
52074
|
// src/lib/agent-integration.ts
|
|
51859
|
-
import { createHash as
|
|
52075
|
+
import { createHash as createHash12, randomUUID as randomUUID5 } from "crypto";
|
|
52076
|
+
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
51860
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";
|
|
51861
52078
|
import { dirname as dirname11, join as join21, resolve as resolve11, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
|
|
51862
52079
|
import { homedir as homedir3 } from "os";
|
|
@@ -51965,7 +52182,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
51965
52182
|
}
|
|
51966
52183
|
}
|
|
51967
52184
|
function treeHash(root) {
|
|
51968
|
-
const hash2 =
|
|
52185
|
+
const hash2 = createHash12("sha256");
|
|
51969
52186
|
let bytes = 0, files = 0;
|
|
51970
52187
|
function visit2(path) {
|
|
51971
52188
|
const stat = lstatSync15(path);
|
|
@@ -51992,7 +52209,7 @@ function treeHash(root) {
|
|
|
51992
52209
|
return hash2.digest("hex");
|
|
51993
52210
|
}
|
|
51994
52211
|
function parseNativeMigrationTargetManifest(input) {
|
|
51995
|
-
const digest =
|
|
52212
|
+
const digest = sha3(input);
|
|
51996
52213
|
let value;
|
|
51997
52214
|
try {
|
|
51998
52215
|
value = JSON.parse(input.toString());
|
|
@@ -52397,7 +52614,7 @@ function planAgentIntegration(options) {
|
|
|
52397
52614
|
const observedNativeSources = [];
|
|
52398
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) => {
|
|
52399
52616
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
52400
|
-
observedNativeSources.push({ path, sha256:
|
|
52617
|
+
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
52401
52618
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
52402
52619
|
}, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
|
|
52403
52620
|
const changes = [];
|
|
@@ -52425,7 +52642,7 @@ function planAgentIntegration(options) {
|
|
|
52425
52642
|
throw new Error("Refusing to overwrite an unrecognized Hermes supervisor");
|
|
52426
52643
|
if (supervisorBefore !== supervisorAfter)
|
|
52427
52644
|
changes.push({ path: supervisorPath, before: supervisorBefore, after: supervisorAfter });
|
|
52428
|
-
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256:
|
|
52645
|
+
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256: sha3(supervisorAfter) };
|
|
52429
52646
|
const after3 = configureHermesHooks(before2, supervisor, previous);
|
|
52430
52647
|
if (before2 !== after3)
|
|
52431
52648
|
changes.push({ path, before: before2, after: after3 });
|
|
@@ -52472,12 +52689,12 @@ function planAgentIntegration(options) {
|
|
|
52472
52689
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
52473
52690
|
home: resolve11(home),
|
|
52474
52691
|
includeVendor: true,
|
|
52475
|
-
...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)) } } } : {},
|
|
52476
52693
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
52477
52694
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
52478
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 ?? [],
|
|
52479
52696
|
rootAliases: aliases,
|
|
52480
|
-
...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 } : {},
|
|
52481
52698
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
52482
52699
|
} };
|
|
52483
52700
|
const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
|
|
@@ -52515,7 +52732,7 @@ function atomicWrite2(path, content) {
|
|
|
52515
52732
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
52516
52733
|
const home = resolve11(options.home ?? homedir3()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
|
|
52517
52734
|
const snapshot = readManagedSkillPolicySnapshot(dataDir3);
|
|
52518
|
-
if (!snapshot ||
|
|
52735
|
+
if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
|
|
52519
52736
|
throw new Error("Managed policy preimage changed");
|
|
52520
52737
|
const policy = snapshot.value, aliases = policy.bridge?.rootAliases ?? [];
|
|
52521
52738
|
recheckRootAliases(aliases);
|
|
@@ -52524,7 +52741,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
52524
52741
|
throw new Error("Settings witness upgrade requires an existing reviewed native binding");
|
|
52525
52742
|
const configPath = canonicalAgentPath(join21(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
|
|
52526
52743
|
const settings = readOptional(configPath);
|
|
52527
|
-
if (settings === null ||
|
|
52744
|
+
if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
|
|
52528
52745
|
throw new Error("Native settings preimage changed");
|
|
52529
52746
|
const sources = binding.sources.filter((source) => source.path === configPath && source.format === undefined && source.fields === undefined);
|
|
52530
52747
|
if (sources.length !== 1)
|
|
@@ -52532,15 +52749,40 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
52532
52749
|
const previous = sources[0];
|
|
52533
52750
|
if (typeof previous.sha256 !== "string")
|
|
52534
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
|
+
}
|
|
52535
52768
|
const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
52536
|
-
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 };
|
|
52537
52779
|
verifyAgentDiscovery(replacement);
|
|
52538
52780
|
const after2 = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
52539
|
-
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 })) } };
|
|
52540
52782
|
}
|
|
52541
52783
|
function applyAgentIntegration(plan) {
|
|
52542
52784
|
for (const source of plan.observedNativeSources ?? [])
|
|
52543
|
-
if (
|
|
52785
|
+
if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
|
|
52544
52786
|
throw new Error("Native identity source changed after planning");
|
|
52545
52787
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
52546
52788
|
throw new Error("Native settings changed after witness planning");
|
|
@@ -52560,7 +52802,7 @@ function applyAgentIntegration(plan) {
|
|
|
52560
52802
|
const upgrade = plan.settingsWitnessUpgrade;
|
|
52561
52803
|
if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
|
|
52562
52804
|
throw new Error("Invalid settings witness upgrade plan");
|
|
52563
|
-
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 });
|
|
52564
52806
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
|
|
52565
52807
|
throw new Error("Settings witness upgrade plan changed");
|
|
52566
52808
|
provenDiscovery = verified.discoveryBefore[0];
|
|
@@ -52588,7 +52830,7 @@ function applyAgentIntegration(plan) {
|
|
|
52588
52830
|
chmodSync(backupRoot, 448);
|
|
52589
52831
|
for (const [index, change] of plan.changes.entries())
|
|
52590
52832
|
if (change.before !== null) {
|
|
52591
|
-
const backup = join21(backupRoot, `${index}-${
|
|
52833
|
+
const backup = join21(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
|
|
52592
52834
|
writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
|
|
52593
52835
|
if (readFileSync13(backup, "utf8") !== change.before)
|
|
52594
52836
|
throw new Error("Native configuration preservation readback failed");
|
|
@@ -52607,7 +52849,7 @@ function applyAgentIntegration(plan) {
|
|
|
52607
52849
|
}
|
|
52608
52850
|
recheckRootAliases(aliases);
|
|
52609
52851
|
for (const source of plan.observedNativeSources ?? [])
|
|
52610
|
-
if (
|
|
52852
|
+
if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
|
|
52611
52853
|
throw new Error("Native identity source changed during application");
|
|
52612
52854
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
52613
52855
|
throw new Error("Native settings changed during witness application");
|
|
@@ -52619,7 +52861,7 @@ function applyAgentIntegration(plan) {
|
|
|
52619
52861
|
}
|
|
52620
52862
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
52621
52863
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
52622
|
-
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 } : {} }) + `
|
|
52623
52865
|
`);
|
|
52624
52866
|
} catch (error) {
|
|
52625
52867
|
for (const change of written.reverse()) {
|
|
@@ -52698,8 +52940,8 @@ function archiveNativeSkills(inventory, options) {
|
|
|
52698
52940
|
const operationId = randomUUID5(), archiveRoot = join21(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join21(archiveRoot, "receipt.json");
|
|
52699
52941
|
const entries = selected.map((entry, index) => ({
|
|
52700
52942
|
source: entry.vendor ? join21(entry.path, "SKILL.md") : entry.path,
|
|
52701
|
-
archive: join21(archiveRoot, `${index}-${
|
|
52702
|
-
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,
|
|
52703
52945
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
52704
52946
|
status: "planned"
|
|
52705
52947
|
}));
|
|
@@ -52710,7 +52952,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
52710
52952
|
const stat = lstatSync15(entry.archive);
|
|
52711
52953
|
if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
|
|
52712
52954
|
throw new Error("Archive type changed");
|
|
52713
|
-
if ((entry.discoveryOnly ?
|
|
52955
|
+
if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
52714
52956
|
throw new Error("Archive bytes changed");
|
|
52715
52957
|
};
|
|
52716
52958
|
assertSafePath(archiveRoot);
|
|
@@ -52830,7 +53072,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
52830
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");
|
|
52831
53073
|
if (agent === "hermes") {
|
|
52832
53074
|
const supervisor = binding.supervisors?.hermes;
|
|
52833
|
-
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)))
|
|
52834
53076
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
52835
53077
|
assertHermesProtection(home, config, command, profile, supervisor);
|
|
52836
53078
|
} else if (agent === "opencode") {
|
|
@@ -52946,7 +53188,7 @@ function assertNativeExportAllowed(dataDir3 = getDataDirReadOnly()) {
|
|
|
52946
53188
|
if (requiresCliSkillLoading(dataDir3))
|
|
52947
53189
|
throw new Error("NATIVE_SKILL_EXPORT_DISABLED: this station loads skills through the Skills CLI; use skills sync --selection-profile <id>");
|
|
52948
53190
|
}
|
|
52949
|
-
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.";
|
|
52950
53192
|
var init_agent_integration = __esm(() => {
|
|
52951
53193
|
init_codex_plugin_skill_controls();
|
|
52952
53194
|
init_codex_native_skill_catalog();
|
|
@@ -54528,7 +54770,7 @@ var init_selection_resolver = __esm(() => {
|
|
|
54528
54770
|
});
|
|
54529
54771
|
|
|
54530
54772
|
// src/lib/plugin-projection.ts
|
|
54531
|
-
import { createHash as
|
|
54773
|
+
import { createHash as createHash13 } from "crypto";
|
|
54532
54774
|
import { posix } from "path";
|
|
54533
54775
|
function pluginRefusal(message) {
|
|
54534
54776
|
throw new SkillSelectionError("PLUGIN_ADMISSION_REFUSED", message);
|
|
@@ -54721,7 +54963,7 @@ function buildPluginProjection(entries) {
|
|
|
54721
54963
|
files.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
54722
54964
|
return { manifest, payloads: manifest.payloads, files, witnesses: pluginFileWitnesses(files), originalTreeDigest, projectionTreeDigest: pluginTreeDigest(files), removed: removed.length ? pluginFileWitnesses(removed) : [] };
|
|
54723
54965
|
}
|
|
54724
|
-
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;
|
|
54725
54967
|
var init_plugin_projection = __esm(() => {
|
|
54726
54968
|
init_selection_cache();
|
|
54727
54969
|
PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -54729,7 +54971,7 @@ var init_plugin_projection = __esm(() => {
|
|
|
54729
54971
|
});
|
|
54730
54972
|
|
|
54731
54973
|
// src/lib/plugin-projection-store.ts
|
|
54732
|
-
import { createHash as
|
|
54974
|
+
import { createHash as createHash14, randomUUID as randomUUID6 } from "crypto";
|
|
54733
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";
|
|
54734
54976
|
import { dirname as dirname13, isAbsolute as isAbsolute12, join as join29, parse as parse5, resolve as resolve12 } from "path";
|
|
54735
54977
|
function stat(path) {
|
|
@@ -54741,7 +54983,7 @@ function stat(path) {
|
|
|
54741
54983
|
return pluginRefusal("Cannot inspect plugin filesystem entry");
|
|
54742
54984
|
}
|
|
54743
54985
|
}
|
|
54744
|
-
function
|
|
54986
|
+
function canonical5(path) {
|
|
54745
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");
|
|
54746
54988
|
}
|
|
54747
54989
|
function owner(stats, system = false) {
|
|
@@ -54752,7 +54994,7 @@ function same3(left, right, identityOnly = false) {
|
|
|
54752
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");
|
|
54753
54995
|
}
|
|
54754
54996
|
function ancestors(path, create3 = false) {
|
|
54755
|
-
|
|
54997
|
+
canonical5(path);
|
|
54756
54998
|
const result2 = [], parent = dirname13(path), filesystemRoot = parse5(parent).root;
|
|
54757
54999
|
let current = filesystemRoot;
|
|
54758
55000
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
@@ -55067,7 +55309,7 @@ function pluginExecutableDigest(path) {
|
|
|
55067
55309
|
const opened = fstatSync9(fd, { bigint: true });
|
|
55068
55310
|
same3(before2, opened);
|
|
55069
55311
|
fileShape(opened, maximum, "executable");
|
|
55070
|
-
const hash2 =
|
|
55312
|
+
const hash2 = createHash14("sha256"), buffer = new Uint8Array(64 * 1024);
|
|
55071
55313
|
let remaining = Number(opened.size);
|
|
55072
55314
|
while (remaining > 0) {
|
|
55073
55315
|
const count = readSync7(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
|
|
@@ -55650,21 +55892,21 @@ var init_plugin_discovery = __esm(() => {
|
|
|
55650
55892
|
});
|
|
55651
55893
|
|
|
55652
55894
|
// src/lib/claude-marketplace-registry.ts
|
|
55653
|
-
import { createHash as
|
|
55895
|
+
import { createHash as createHash15 } from "crypto";
|
|
55654
55896
|
import { closeSync as closeSync11, constants as constants11, fstatSync as fstatSync11, lstatSync as lstatSync19, openSync as openSync11, readSync as readSync9 } from "fs";
|
|
55655
55897
|
import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute15, resolve as resolve15 } from "path";
|
|
55656
|
-
function
|
|
55898
|
+
function need5(value, reason) {
|
|
55657
55899
|
if (!value)
|
|
55658
55900
|
throw new Error(`Claude marketplace registry ${reason}`);
|
|
55659
55901
|
}
|
|
55660
55902
|
function absolutePath3(value) {
|
|
55661
|
-
|
|
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");
|
|
55662
55904
|
}
|
|
55663
55905
|
function parents2(path) {
|
|
55664
55906
|
const result2 = [];
|
|
55665
55907
|
for (let at2 = dirname15(path);; at2 = dirname15(at2)) {
|
|
55666
55908
|
const stat2 = lstatSync19(at2, { bigint: true });
|
|
55667
|
-
|
|
55909
|
+
need5(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
55668
55910
|
result2.push([at2, stat2]);
|
|
55669
55911
|
if (at2 === dirname15(at2))
|
|
55670
55912
|
return result2;
|
|
@@ -55672,15 +55914,15 @@ function parents2(path) {
|
|
|
55672
55914
|
}
|
|
55673
55915
|
function readRegistry2(path, budget) {
|
|
55674
55916
|
absolutePath3(path);
|
|
55675
|
-
|
|
55676
|
-
|
|
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");
|
|
55677
55919
|
const ancestors2 = parents2(path), initial2 = lstatSync19(path, { bigint: true });
|
|
55678
|
-
|
|
55679
|
-
|
|
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");
|
|
55680
55922
|
const fd = openSync11(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
|
|
55681
55923
|
try {
|
|
55682
55924
|
const opened = fstatSync11(fd, { bigint: true });
|
|
55683
|
-
|
|
55925
|
+
need5(opened.isFile() && sameFile2(initial2, opened), "changed during open");
|
|
55684
55926
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
55685
55927
|
let length = 0;
|
|
55686
55928
|
while (length < bytes.length) {
|
|
@@ -55690,10 +55932,10 @@ function readRegistry2(path, budget) {
|
|
|
55690
55932
|
length += count;
|
|
55691
55933
|
}
|
|
55692
55934
|
const after2 = fstatSync11(fd, { bigint: true }), current = lstatSync19(path, { bigint: true });
|
|
55693
|
-
|
|
55935
|
+
need5(current.isFile() && sameFile2(opened, after2) && sameFile2(after2, current) && BigInt(length) === opened.size, "changed during read");
|
|
55694
55936
|
for (const [ancestor, before2] of ancestors2) {
|
|
55695
55937
|
const now3 = lstatSync19(ancestor, { bigint: true });
|
|
55696
|
-
|
|
55938
|
+
need5(now3.isDirectory() && before2.dev === now3.dev && before2.ino === now3.ino && before2.mode === now3.mode, "parent changed during read");
|
|
55697
55939
|
}
|
|
55698
55940
|
budget.remaining -= length;
|
|
55699
55941
|
try {
|
|
@@ -55713,7 +55955,7 @@ function parse6(text2) {
|
|
|
55713
55955
|
at2++;
|
|
55714
55956
|
};
|
|
55715
55957
|
function string() {
|
|
55716
|
-
|
|
55958
|
+
need5(text2[at2] === '"', "contains invalid JSON");
|
|
55717
55959
|
const start = at2++;
|
|
55718
55960
|
while (at2 < text2.length) {
|
|
55719
55961
|
const character = text2[at2++];
|
|
@@ -55724,7 +55966,7 @@ function parse6(text2) {
|
|
|
55724
55966
|
} catch {
|
|
55725
55967
|
throw new Error("Claude marketplace registry contains invalid JSON string");
|
|
55726
55968
|
}
|
|
55727
|
-
|
|
55969
|
+
need5(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
|
|
55728
55970
|
return value2;
|
|
55729
55971
|
}
|
|
55730
55972
|
if (character === "\\")
|
|
@@ -55733,7 +55975,7 @@ function parse6(text2) {
|
|
|
55733
55975
|
throw new Error("Claude marketplace registry contains an unterminated JSON string");
|
|
55734
55976
|
}
|
|
55735
55977
|
function value(depth) {
|
|
55736
|
-
|
|
55978
|
+
need5(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
55737
55979
|
space();
|
|
55738
55980
|
const character = text2[at2];
|
|
55739
55981
|
if (character === '"')
|
|
@@ -55747,17 +55989,17 @@ function parse6(text2) {
|
|
|
55747
55989
|
if (object5) {
|
|
55748
55990
|
space();
|
|
55749
55991
|
const key = string();
|
|
55750
|
-
|
|
55992
|
+
need5(!keys2.has(key), "contains a duplicate JSON key");
|
|
55751
55993
|
keys2.add(key);
|
|
55752
55994
|
space();
|
|
55753
|
-
|
|
55995
|
+
need5(text2[at2++] === ":", "contains invalid JSON");
|
|
55754
55996
|
entries.push([key, value(depth + 1)]);
|
|
55755
55997
|
} else
|
|
55756
55998
|
items.push(value(depth + 1));
|
|
55757
55999
|
space();
|
|
55758
56000
|
if (text2[at2] === end)
|
|
55759
56001
|
break;
|
|
55760
|
-
|
|
56002
|
+
need5(text2[at2++] === ",", "contains invalid JSON");
|
|
55761
56003
|
}
|
|
55762
56004
|
at2++;
|
|
55763
56005
|
return object5 ? { kind: "object", entries } : { kind: "array", items };
|
|
@@ -55769,21 +56011,21 @@ function parse6(text2) {
|
|
|
55769
56011
|
}
|
|
55770
56012
|
numberPattern.lastIndex = at2;
|
|
55771
56013
|
const number = numberPattern.exec(text2);
|
|
55772
|
-
|
|
56014
|
+
need5(number, "contains invalid JSON");
|
|
55773
56015
|
at2 += number[0].length;
|
|
55774
56016
|
return { kind: "number", value: number[0] };
|
|
55775
56017
|
}
|
|
55776
56018
|
const root2 = value(0);
|
|
55777
56019
|
space();
|
|
55778
|
-
|
|
55779
|
-
|
|
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");
|
|
55780
56022
|
return root2;
|
|
55781
56023
|
}
|
|
55782
56024
|
function exactKeys(value, keys2) {
|
|
55783
56025
|
return value?.kind === "object" && value.entries.length === keys2.length && value.entries.every(([key]) => keys2.includes(key));
|
|
55784
56026
|
}
|
|
55785
56027
|
function textField(value) {
|
|
55786
|
-
|
|
56028
|
+
need5(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
|
|
55787
56029
|
return value.value;
|
|
55788
56030
|
}
|
|
55789
56031
|
function normalizeRow(row, version = 1) {
|
|
@@ -55791,7 +56033,7 @@ function normalizeRow(row, version = 1) {
|
|
|
55791
56033
|
if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
55792
56034
|
return;
|
|
55793
56035
|
const autoUpdate = field(row, "autoUpdate");
|
|
55794
|
-
|
|
56036
|
+
need5(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
55795
56037
|
}
|
|
55796
56038
|
const source = field(row, "source");
|
|
55797
56039
|
if (source?.kind !== "object")
|
|
@@ -55807,23 +56049,23 @@ function normalizeRow(row, version = 1) {
|
|
|
55807
56049
|
absolutePath3(sourceLocation);
|
|
55808
56050
|
absolutePath3(textField(field(row, "installLocation")));
|
|
55809
56051
|
const updated = textField(field(row, "lastUpdated"));
|
|
55810
|
-
|
|
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");
|
|
55811
56053
|
const millis = Date.parse(updated);
|
|
55812
|
-
|
|
56054
|
+
need5(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
55813
56055
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
55814
56056
|
}
|
|
55815
56057
|
function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
55816
56058
|
const value = parse6(readRegistry2(path, budget));
|
|
55817
56059
|
for (const [, row] of value.entries)
|
|
55818
56060
|
normalizeRow(row);
|
|
55819
|
-
const sha2562 =
|
|
56061
|
+
const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
55820
56062
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
55821
56063
|
}
|
|
55822
56064
|
function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
55823
56065
|
const value = parse6(readRegistry2(path, budget));
|
|
55824
56066
|
for (const [, row] of value.entries)
|
|
55825
56067
|
normalizeRow(row, 2);
|
|
55826
|
-
const sha2562 =
|
|
56068
|
+
const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
55827
56069
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
55828
56070
|
}
|
|
55829
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];
|
|
@@ -55833,17 +56075,17 @@ var init_claude_marketplace_registry = __esm(() => {
|
|
|
55833
56075
|
|
|
55834
56076
|
// src/lib/codex-hook-trust-files.ts
|
|
55835
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";
|
|
55836
|
-
import { createHash as
|
|
56078
|
+
import { createHash as createHash16 } from "crypto";
|
|
55837
56079
|
import { dirname as dirname16 } from "path";
|
|
55838
|
-
function
|
|
56080
|
+
function need6(value, code) {
|
|
55839
56081
|
if (!value)
|
|
55840
56082
|
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
55841
56083
|
}
|
|
55842
56084
|
function safeParents(file) {
|
|
55843
56085
|
for (let p = dirname16(file);; p = dirname16(p)) {
|
|
55844
56086
|
const s = lstatSync20(p);
|
|
55845
|
-
|
|
55846
|
-
|
|
56087
|
+
need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
|
|
56088
|
+
need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
55847
56089
|
if (dirname16(p) === p)
|
|
55848
56090
|
break;
|
|
55849
56091
|
}
|
|
@@ -55852,12 +56094,12 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
55852
56094
|
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
55853
56095
|
safeParents(file);
|
|
55854
56096
|
const initial2 = lstatSync20(file, { bigint: true });
|
|
55855
|
-
|
|
55856
|
-
|
|
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");
|
|
55857
56099
|
const fd = openSync12(file, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
|
|
55858
56100
|
try {
|
|
55859
56101
|
const before2 = fstatSync12(fd, { bigint: true });
|
|
55860
|
-
|
|
56102
|
+
need6(sameStat(initial2, before2), "INPUT_CHANGED");
|
|
55861
56103
|
const bytes = Buffer.alloc(Number(before2.size) + 1);
|
|
55862
56104
|
let length = 0;
|
|
55863
56105
|
while (length < bytes.length) {
|
|
@@ -55866,16 +56108,16 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
55866
56108
|
break;
|
|
55867
56109
|
length += n2;
|
|
55868
56110
|
}
|
|
55869
|
-
|
|
56111
|
+
need6(length === Number(before2.size) && sameStat(before2, fstatSync12(fd, { bigint: true })) && sameStat(before2, lstatSync20(file, { bigint: true })), "INPUT_CHANGED");
|
|
55870
56112
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
55871
|
-
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") };
|
|
55872
56114
|
} finally {
|
|
55873
56115
|
closeSync12(fd);
|
|
55874
56116
|
}
|
|
55875
56117
|
}
|
|
55876
56118
|
function unchanged2(before2) {
|
|
55877
56119
|
const after2 = snapshot(before2.file, before2.privateFile, before2.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
55878
|
-
|
|
56120
|
+
need6(sameStat(before2.stat, after2.stat) && after2.sha256 === before2.sha256, "INPUT_CHANGED");
|
|
55879
56121
|
}
|
|
55880
56122
|
function save(file, content) {
|
|
55881
56123
|
safeParents(file);
|
|
@@ -55891,7 +56133,7 @@ var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs
|
|
|
55891
56133
|
var init_codex_hook_trust_files = () => {};
|
|
55892
56134
|
|
|
55893
56135
|
// src/lib/codex-hook-trust-layout.ts
|
|
55894
|
-
import { isDeepStrictEqual as
|
|
56136
|
+
import { isDeepStrictEqual as isDeepStrictEqual5 } from "util";
|
|
55895
56137
|
function codexTrustTextWitness(text2, keys2, canonicalize = true) {
|
|
55896
56138
|
const parsed = Bun.TOML.parse(text2), wanted = new Set(keys2);
|
|
55897
56139
|
const headers = [...text2.matchAll(/^[ \t]*(\[[^\r\n]+\])[ \t]*(#[^\r\n]*)?(?:\r?\n|$)/gm)];
|
|
@@ -55956,7 +56198,7 @@ function codexTrustTextWitness(text2, keys2, canonicalize = true) {
|
|
|
55956
56198
|
if (expected.hooks)
|
|
55957
56199
|
delete expected.hooks.state;
|
|
55958
56200
|
try {
|
|
55959
|
-
if (!
|
|
56201
|
+
if (!isDeepStrictEqual5(prune(Bun.TOML.parse(noState)), prune(expected)))
|
|
55960
56202
|
throw fail2();
|
|
55961
56203
|
} catch {
|
|
55962
56204
|
throw fail2();
|
|
@@ -56026,55 +56268,55 @@ var init_codex_hook_trust_layout = () => {};
|
|
|
56026
56268
|
|
|
56027
56269
|
// src/lib/codex-hook-additions-review.ts
|
|
56028
56270
|
import { isAbsolute as isAbsolute16 } from "path";
|
|
56029
|
-
import { isDeepStrictEqual as
|
|
56271
|
+
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
56030
56272
|
function exactKeys2(value, keys2) {
|
|
56031
|
-
|
|
56273
|
+
need6(value && typeof value === "object" && !Array.isArray(value) && isDeepStrictEqual6(Object.keys(value).sort(), [...keys2].sort()), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
56032
56274
|
}
|
|
56033
56275
|
function readReviewedAdditionTrustStates(proof, before2, current, journalHooks) {
|
|
56034
56276
|
const captured = proofs.get(proof);
|
|
56035
|
-
|
|
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");
|
|
56036
56278
|
captured.recheck();
|
|
56037
56279
|
unchanged2(before2);
|
|
56038
56280
|
unchanged2(current);
|
|
56039
56281
|
return captured.states;
|
|
56040
56282
|
}
|
|
56041
56283
|
function reviewNativeHookAdditions(options) {
|
|
56042
|
-
|
|
56284
|
+
need6(isAbsolute16(options.path), "RECONCILE_ADDITIONS_REVIEW_PATH");
|
|
56043
56285
|
const reviewFile = snapshot(options.path, true), review = JSON.parse(reviewFile.text);
|
|
56044
56286
|
exactKeys2(review, ["version", "kind", "reason", "producerEvidence", "journalIntentSha256", "policySha256", "configCurrentSha256", "hooksCurrentSha256", "producerBefore", "additions"]);
|
|
56045
|
-
|
|
56287
|
+
need6((review.version === 1 || review.version === 2) && review.kind === "native-hook-additions" && text2(review.reason) && text2(review.producerEvidence), "RECONCILE_ADDITIONS_REVIEW_SCHEMA");
|
|
56046
56288
|
for (const [name, expected] of [["journalIntentSha256", options.intent.sha256], ["policySha256", options.policy.sha256], ["configCurrentSha256", options.configCurrent.sha256], ["hooksCurrentSha256", options.hooksCurrent.sha256]])
|
|
56047
|
-
|
|
56289
|
+
need6(hash2(review[name]) && review[name] === expected, "RECONCILE_ADDITIONS_REVIEW_CHANGED");
|
|
56048
56290
|
exactKeys2(review.producerBefore, ["configPath", "configSha256", "hooksPath", "hooksSha256"]);
|
|
56049
56291
|
const before2 = review.producerBefore;
|
|
56050
|
-
|
|
56292
|
+
need6(isAbsolute16(before2.configPath) && isAbsolute16(before2.hooksPath) && hash2(before2.configSha256) && hash2(before2.hooksSha256), "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
56051
56293
|
const producerConfig = snapshot(before2.configPath, true), producerHooks = snapshot(before2.hooksPath, true);
|
|
56052
|
-
|
|
56294
|
+
need6(producerConfig.sha256 === before2.configSha256 && producerHooks.sha256 === before2.hooksSha256 && producerHooks.sha256 === options.hooksBefore.sha256, "RECONCILE_ADDITIONS_PRODUCER_PREIMAGE");
|
|
56053
56295
|
const original = structure(options.configBefore.text), producer = structure(producerConfig.text), expectedProducer = structuredClone(original), originalKeys = options.journalHooks.map((hook) => hook.key);
|
|
56054
56296
|
expectedProducer.hooks ??= {};
|
|
56055
56297
|
expectedProducer.hooks.state ??= {};
|
|
56056
56298
|
for (const hook of options.journalHooks)
|
|
56057
56299
|
expectedProducer.hooks.state[hook.key] = { ...expectedProducer.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
56058
|
-
|
|
56059
|
-
|
|
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");
|
|
56060
56302
|
const expectedHooks = JSON.parse(options.hooksBefore.text), currentHooks = JSON.parse(options.hooksCurrent.text), expectedConfig = structuredClone(producer);
|
|
56061
|
-
|
|
56303
|
+
need6(expectedHooks.hooks && typeof expectedHooks.hooks === "object" && !Array.isArray(expectedHooks.hooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56062
56304
|
const added = [], states = [], seen = new Set, commands = new Set(options.recordedNativeHooks.map((hook) => hook.command));
|
|
56063
56305
|
for (const addition of review.additions) {
|
|
56064
56306
|
exactKeys2(addition, review.version === 1 ? ["event", "nativeHook"] : ["event", "nativeHook", "trustState"]);
|
|
56065
|
-
|
|
56307
|
+
need6(Object.hasOwn(review.version === 1 ? events : matchedEvents, addition.event), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56066
56308
|
const { event, nativeHook: hook } = addition;
|
|
56067
56309
|
exactKeys2(hook, nativeKeys);
|
|
56068
56310
|
const groups = expectedHooks.hooks[event] ?? [];
|
|
56069
|
-
|
|
56311
|
+
need6(Array.isArray(groups), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56070
56312
|
const key = `${options.nativeHooksPath}:${matchedEvents[event][1]}:${groups.length}:0`;
|
|
56071
|
-
|
|
56072
|
-
|
|
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");
|
|
56073
56315
|
let trustState = { enabled: true, trusted_hash: hook.currentHash };
|
|
56074
56316
|
if (review.version === 2) {
|
|
56075
56317
|
const state = addition.trustState;
|
|
56076
56318
|
exactKeys2(state, state && Object.hasOwn(state, "enabled") ? ["enabled", "trusted_hash"] : ["trusted_hash"]);
|
|
56077
|
-
|
|
56319
|
+
need6(state.trusted_hash === hook.currentHash && (!Object.hasOwn(state, "enabled") || state.enabled === true), "RECONCILE_ADDITIONS_TRUST_STATE");
|
|
56078
56320
|
trustState = { ...state };
|
|
56079
56321
|
}
|
|
56080
56322
|
seen.add(key);
|
|
@@ -56084,9 +56326,9 @@ function reviewNativeHookAdditions(options) {
|
|
|
56084
56326
|
expectedHooks.hooks[event] = [...groups, { ...event === "PreToolUse" ? { matcher: hook.matcher } : {}, hooks: [{ type: "command", command: hook.command, timeout: hook.timeoutSec }] }];
|
|
56085
56327
|
expectedConfig.hooks.state[key] = trustState;
|
|
56086
56328
|
}
|
|
56087
|
-
|
|
56329
|
+
need6(isDeepStrictEqual6(expectedHooks, currentHooks), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
56088
56330
|
const keys2 = added.map((hook) => hook.key);
|
|
56089
|
-
|
|
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");
|
|
56090
56332
|
const inputs = [reviewFile, producerConfig, producerHooks, options.intent, options.policy, options.configBefore, options.configCurrent, options.hooksBefore, options.hooksCurrent];
|
|
56091
56333
|
const recheck = () => {
|
|
56092
56334
|
for (const input of inputs)
|
|
@@ -56108,8 +56350,8 @@ var init_codex_hook_additions_review = __esm(() => {
|
|
|
56108
56350
|
});
|
|
56109
56351
|
|
|
56110
56352
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
56111
|
-
import { createHash as
|
|
56112
|
-
import { isDeepStrictEqual as
|
|
56353
|
+
import { createHash as createHash17 } from "crypto";
|
|
56354
|
+
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
56113
56355
|
function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, additions) {
|
|
56114
56356
|
const journalHooks = hooks.map(({ key, currentHash }) => ({ key, currentHash }));
|
|
56115
56357
|
const additionStates = additions ? readReviewedAdditionTrustStates(additions, before2, current, journalHooks) : [];
|
|
@@ -56120,16 +56362,16 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
|
|
|
56120
56362
|
const raw = binding.sources.filter((source) => source.path === current.file && source.hashMode === "bytes");
|
|
56121
56363
|
if (!raw.some((source) => source.sha256 !== current.sha256))
|
|
56122
56364
|
return;
|
|
56123
|
-
|
|
56124
|
-
|
|
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");
|
|
56125
56367
|
const typed = binding.sources.filter((source) => source.path === current.file && source.format === "toml");
|
|
56126
|
-
|
|
56368
|
+
need6(typed.length === 1 && typed[0].hashMode === undefined && isDeepStrictEqual7(typed[0].fields, fields), "RECONCILE_DISCOVERY_TYPED_WITNESS");
|
|
56127
56369
|
const original = structure2(before2.text), actual = structure2(current.text);
|
|
56128
56370
|
const project = (value) => Object.fromEntries(fields.map((field2) => [field2, value[field2] ?? null]));
|
|
56129
|
-
const projection =
|
|
56130
|
-
|
|
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");
|
|
56131
56373
|
const keys2 = [...journalHooks.map((hook) => hook.key), ...additionStates.map((hook) => hook.key)];
|
|
56132
|
-
|
|
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");
|
|
56133
56375
|
const expected = structuredClone(original);
|
|
56134
56376
|
expected.hooks ??= {};
|
|
56135
56377
|
expected.hooks.state ??= {};
|
|
@@ -56137,16 +56379,16 @@ function createCodexHookDiscoveryRecovery(binding, before2, current, hooks, addi
|
|
|
56137
56379
|
expected.hooks.state[hook.key] = { ...expected.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
56138
56380
|
for (const { key, state } of additionStates)
|
|
56139
56381
|
expected.hooks.state[key] = { ...state };
|
|
56140
|
-
|
|
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");
|
|
56141
56383
|
unchanged2(before2);
|
|
56142
56384
|
unchanged2(current);
|
|
56143
|
-
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 });
|
|
56144
56386
|
proofs2.set(witness, { before: before2, current, recheckAdditions });
|
|
56145
56387
|
return witness;
|
|
56146
56388
|
}
|
|
56147
56389
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
56148
56390
|
const proof = proofs2.get(witness);
|
|
56149
|
-
|
|
56391
|
+
need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
56150
56392
|
unchanged2(proof.before);
|
|
56151
56393
|
unchanged2(proof.current);
|
|
56152
56394
|
proof.recheckAdditions();
|
|
@@ -56155,23 +56397,24 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
56155
56397
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
56156
56398
|
return false;
|
|
56157
56399
|
assertCodexHookDiscoveryRecovery(binding, witness);
|
|
56158
|
-
|
|
56400
|
+
need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
56159
56401
|
return true;
|
|
56160
56402
|
}
|
|
56161
|
-
var
|
|
56403
|
+
var sha4 = (text3) => createHash17("sha256").update(text3).digest("hex"), fields, structure2 = (text3) => JSON.parse(JSON.stringify(Bun.TOML.parse(text3))), proofs2;
|
|
56162
56404
|
var init_codex_hook_discovery_recovery = __esm(() => {
|
|
56163
56405
|
init_codex_hook_trust_files();
|
|
56164
56406
|
init_codex_hook_trust_layout();
|
|
56165
56407
|
init_codex_hook_additions_review();
|
|
56166
|
-
|
|
56408
|
+
init_codex_settings_witness();
|
|
56409
|
+
fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
|
|
56167
56410
|
proofs2 = new WeakMap;
|
|
56168
56411
|
});
|
|
56169
56412
|
|
|
56170
56413
|
// src/lib/agent-discovery.ts
|
|
56171
|
-
import { createHash as
|
|
56414
|
+
import { createHash as createHash18 } from "crypto";
|
|
56172
56415
|
import { lstatSync as lstatSync21, readFileSync as readFileSync18, readdirSync as readdirSync11, realpathSync as realpathSync7 } from "fs";
|
|
56173
56416
|
import { basename as basename9, dirname as dirname17, isAbsolute as isAbsolute17, join as join32, resolve as resolve16, sep as sep6 } from "path";
|
|
56174
|
-
import { isDeepStrictEqual as
|
|
56417
|
+
import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
|
|
56175
56418
|
function parseConfig(text3, path, toml = false) {
|
|
56176
56419
|
try {
|
|
56177
56420
|
const value = toml ? Bun.TOML.parse(text3) : JSON.parse(text3);
|
|
@@ -56203,14 +56446,26 @@ function read2(path, changes) {
|
|
|
56203
56446
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
56204
56447
|
return readFileSync18(path, "utf8");
|
|
56205
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
|
+
}
|
|
56206
56461
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
56207
|
-
if (source.hashMode === "codex-settings-v1") {
|
|
56462
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") {
|
|
56208
56463
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
56209
56464
|
throw new Error("Codex settings witnesses require exact typed metadata");
|
|
56210
|
-
const current = captureCodexSettings(source.path, budget).sha256;
|
|
56465
|
+
const current = (source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
|
|
56211
56466
|
if (current !== source.sha256)
|
|
56212
56467
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
56213
|
-
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;
|
|
56214
56469
|
}
|
|
56215
56470
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
56216
56471
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
@@ -56249,10 +56504,7 @@ function projected(source, changes, budget = discoveryByteBudget()) {
|
|
|
56249
56504
|
return null;
|
|
56250
56505
|
if (!source.format)
|
|
56251
56506
|
return digest(text3);
|
|
56252
|
-
|
|
56253
|
-
if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
|
|
56254
|
-
throw new Error("Expected native discovery configuration object");
|
|
56255
|
-
return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field2) => [field2, object5[field2] ?? null]))));
|
|
56507
|
+
return projectNativeDiscoveryFields(text3, source.format, source.fields ?? [], source.path);
|
|
56256
56508
|
}
|
|
56257
56509
|
function verifyAgentDiscovery(binding, codexRecovery) {
|
|
56258
56510
|
if (codexRecovery)
|
|
@@ -56280,7 +56532,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
56280
56532
|
safe4(root2);
|
|
56281
56533
|
}
|
|
56282
56534
|
function assertMarketplaceBinding(binding, source) {
|
|
56283
|
-
if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
56535
|
+
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
56284
56536
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
56285
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"))
|
|
56286
56538
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
@@ -56297,8 +56549,8 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
56297
56549
|
return { ...source, sha256: sha2562 };
|
|
56298
56550
|
}) };
|
|
56299
56551
|
}
|
|
56300
|
-
function assertProjectDiscovery(agent, directories, home,
|
|
56301
|
-
const homeConfigPath =
|
|
56552
|
+
function assertProjectDiscovery(agent, directories, home, canonical6 = resolve16, reviewed) {
|
|
56553
|
+
const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
|
|
56302
56554
|
for (const directory of directories) {
|
|
56303
56555
|
const isHome = resolve16(directory) === resolve16(home);
|
|
56304
56556
|
if (agent === "hermes") {
|
|
@@ -56309,7 +56561,7 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve16
|
|
|
56309
56561
|
continue;
|
|
56310
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"];
|
|
56311
56563
|
for (const suffix of paths) {
|
|
56312
|
-
const path =
|
|
56564
|
+
const path = canonical6(join32(directory, suffix)), raw = read2(path);
|
|
56313
56565
|
if (raw === null)
|
|
56314
56566
|
continue;
|
|
56315
56567
|
const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
|
|
@@ -56325,14 +56577,14 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve16
|
|
|
56325
56577
|
const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
|
|
56326
56578
|
if (exact && projected(exact) === exact.sha256 && managed.length) {
|
|
56327
56579
|
const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
|
|
56328
|
-
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))));
|
|
56329
56581
|
}
|
|
56330
56582
|
}
|
|
56331
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)))
|
|
56332
56584
|
throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
|
|
56333
56585
|
}
|
|
56334
56586
|
if (agent === "claude") {
|
|
56335
|
-
const commands =
|
|
56587
|
+
const commands = canonical6(join32(directory, ".claude/commands"));
|
|
56336
56588
|
safe4(commands);
|
|
56337
56589
|
if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync11(commands).length)
|
|
56338
56590
|
throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
|
|
@@ -56343,10 +56595,10 @@ function agentDiscoveryConfigPath(home, agent) {
|
|
|
56343
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"}`);
|
|
56344
56596
|
}
|
|
56345
56597
|
function resolveAgentDiscovery(options) {
|
|
56346
|
-
const
|
|
56598
|
+
const canonical6 = options.canonical ?? resolve16, home = resolve16(options.home), agent = options.agent;
|
|
56347
56599
|
const sources = [], roots = new Set, builtinNames = [];
|
|
56348
56600
|
const witness = (path, format, fields2) => {
|
|
56349
|
-
const source = { path:
|
|
56601
|
+
const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
|
|
56350
56602
|
source.sha256 = projected(source);
|
|
56351
56603
|
sources.push(source);
|
|
56352
56604
|
return read2(source.path);
|
|
@@ -56354,7 +56606,7 @@ function resolveAgentDiscovery(options) {
|
|
|
56354
56606
|
if (agent === "hermes")
|
|
56355
56607
|
assertHermesEnvironment(home);
|
|
56356
56608
|
const configPath = agentDiscoveryConfigPath(home, agent);
|
|
56357
|
-
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"]);
|
|
56358
56610
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
56359
56611
|
const unresolved = (detail) => {
|
|
56360
56612
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
@@ -56413,11 +56665,12 @@ function resolveAgentDiscovery(options) {
|
|
|
56413
56665
|
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
56414
56666
|
unresolved("invalid retained review");
|
|
56415
56667
|
verifyAgentDiscovery(retained);
|
|
56416
|
-
if (!retained.sources.some((source) => source.path ===
|
|
56668
|
+
if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
|
|
56417
56669
|
unresolved("retained review is missing the full agent configuration source");
|
|
56418
|
-
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)))
|
|
56419
56671
|
unresolved("retained settings witness names another configuration source");
|
|
56420
|
-
|
|
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))
|
|
56421
56674
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
56422
56675
|
return retained;
|
|
56423
56676
|
}
|
|
@@ -56431,14 +56684,15 @@ function resolveAgentDiscovery(options) {
|
|
|
56431
56684
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
56432
56685
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
56433
56686
|
verifyAgentDiscovery(supplied);
|
|
56434
|
-
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)))
|
|
56435
56688
|
throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
|
|
56436
|
-
if (!review.sources.some((source) => source.path ===
|
|
56689
|
+
if (!review.sources.some((source) => source.path === canonical6(configPath)))
|
|
56437
56690
|
throw new Error("Discovery review must include the agent configuration source");
|
|
56438
|
-
|
|
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 } : {} };
|
|
56439
56693
|
}
|
|
56440
56694
|
function plugin(root2) {
|
|
56441
|
-
root2 =
|
|
56695
|
+
root2 = canonical6(root2);
|
|
56442
56696
|
safe4(root2);
|
|
56443
56697
|
const manifestPath = join32(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
56444
56698
|
const raw = witness(manifestPath);
|
|
@@ -56470,7 +56724,7 @@ function resolveAgentDiscovery(options) {
|
|
|
56470
56724
|
if (agent === "claude") {
|
|
56471
56725
|
const registrations = witness(join32(home, ".claude/plugins/installed_plugins.json"));
|
|
56472
56726
|
const installed = registrations === null ? {} : parseConfig(registrations, join32(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
56473
|
-
const commands =
|
|
56727
|
+
const commands = canonical6(join32(home, ".claude/commands"));
|
|
56474
56728
|
safe4(commands);
|
|
56475
56729
|
if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync11(commands).length)
|
|
56476
56730
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
@@ -56544,9 +56798,9 @@ function resolveAgentDiscovery(options) {
|
|
|
56544
56798
|
roots.add(join32(root2, "skills"));
|
|
56545
56799
|
}
|
|
56546
56800
|
}
|
|
56547
|
-
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))) } : {} };
|
|
56548
56802
|
}
|
|
56549
|
-
var digest = (text3) =>
|
|
56803
|
+
var digest = (text3) => createHash18("sha256").update(text3).digest("hex");
|
|
56550
56804
|
var init_agent_discovery = __esm(() => {
|
|
56551
56805
|
init_agent_policy_limits();
|
|
56552
56806
|
init_agent_hermes();
|
|
@@ -56679,7 +56933,7 @@ import {
|
|
|
56679
56933
|
statSync as statSync11,
|
|
56680
56934
|
writeFileSync as writeFileSync9
|
|
56681
56935
|
} from "fs";
|
|
56682
|
-
import { basename as basename11, dirname as dirname19, isAbsolute as isAbsolute18, join as join34, normalize 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";
|
|
56683
56937
|
function getPortableSkillsRoot(options = {}) {
|
|
56684
56938
|
return resolvePortableSkillsRoot(options, true);
|
|
56685
56939
|
}
|
|
@@ -56777,7 +57031,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
56777
57031
|
return { name: skillName, path: skillPath, manifest, created: true };
|
|
56778
57032
|
}
|
|
56779
57033
|
function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
56780
|
-
const absoluteSource =
|
|
57034
|
+
const absoluteSource = normalize4(sourceDir);
|
|
56781
57035
|
if (!existsSync18(absoluteSource) || !statSync11(absoluteSource).isDirectory()) {
|
|
56782
57036
|
throw new Error(`Import directory not found: ${sourceDir}`);
|
|
56783
57037
|
}
|
|
@@ -56844,7 +57098,7 @@ function assertDisjointImportPaths(source, destination) {
|
|
|
56844
57098
|
}
|
|
56845
57099
|
}
|
|
56846
57100
|
function portPortableSkill(sourcePath, options = {}) {
|
|
56847
|
-
const absoluteSource =
|
|
57101
|
+
const absoluteSource = normalize4(sourcePath);
|
|
56848
57102
|
if (!existsSync18(absoluteSource) || !statSync11(absoluteSource).isDirectory()) {
|
|
56849
57103
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
56850
57104
|
}
|
|
@@ -57056,7 +57310,7 @@ function safeIsDirectory(path) {
|
|
|
57056
57310
|
function isSafeRelativePath2(value) {
|
|
57057
57311
|
if (!value.trim() || isAbsolute18(value))
|
|
57058
57312
|
return false;
|
|
57059
|
-
const normalized =
|
|
57313
|
+
const normalized = normalize4(value).replace(/\\/g, "/");
|
|
57060
57314
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
57061
57315
|
}
|
|
57062
57316
|
function add4(target, code, message) {
|
|
@@ -59482,7 +59736,7 @@ var init_remote_workspace_selection = __esm(() => {
|
|
|
59482
59736
|
});
|
|
59483
59737
|
|
|
59484
59738
|
// src/lib/remote-private-publications.ts
|
|
59485
|
-
import { createHash as
|
|
59739
|
+
import { createHash as createHash19 } from "crypto";
|
|
59486
59740
|
function checkedPublicationDeclaration(value) {
|
|
59487
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)
|
|
59488
59742
|
return bad();
|
|
@@ -59743,7 +59997,7 @@ var PRIVATE_PUBLICATION_MAX_BYTES, failures, PrivatePublicationError, bad = () =
|
|
|
59743
59997
|
throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
|
|
59744
59998
|
}, invalid3 = () => {
|
|
59745
59999
|
throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
|
|
59746
|
-
}, 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");
|
|
59747
60001
|
var init_remote_private_publications = __esm(() => {
|
|
59748
60002
|
init_fleet_credentials();
|
|
59749
60003
|
init_remote_workspace_selection();
|
|
@@ -63282,15 +63536,15 @@ var init_schemas = __esm(() => {
|
|
|
63282
63536
|
} catch (_err) {}
|
|
63283
63537
|
}
|
|
63284
63538
|
const input = payload.value;
|
|
63285
|
-
const
|
|
63286
|
-
const isValidDate =
|
|
63539
|
+
const isDate3 = input instanceof Date;
|
|
63540
|
+
const isValidDate = isDate3 && !Number.isNaN(input.getTime());
|
|
63287
63541
|
if (isValidDate)
|
|
63288
63542
|
return payload;
|
|
63289
63543
|
payload.issues.push({
|
|
63290
63544
|
expected: "date",
|
|
63291
63545
|
code: "invalid_type",
|
|
63292
63546
|
input,
|
|
63293
|
-
...
|
|
63547
|
+
...isDate3 ? { received: "Invalid Date" } : {},
|
|
63294
63548
|
inst
|
|
63295
63549
|
});
|
|
63296
63550
|
return payload;
|
|
@@ -79132,7 +79386,7 @@ var init_v4 = __esm(() => {
|
|
|
79132
79386
|
});
|
|
79133
79387
|
|
|
79134
79388
|
// src/lib/canonical-json.ts
|
|
79135
|
-
import { createHash as
|
|
79389
|
+
import { createHash as createHash20 } from "crypto";
|
|
79136
79390
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
79137
79391
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
79138
79392
|
}
|
|
@@ -79176,7 +79430,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
79176
79430
|
return visit3(value, 0);
|
|
79177
79431
|
}
|
|
79178
79432
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
79179
|
-
return
|
|
79433
|
+
return createHash20("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
79180
79434
|
}
|
|
79181
79435
|
var init_canonical_json = () => {};
|
|
79182
79436
|
|
|
@@ -79511,7 +79765,7 @@ var init_remote_recurring = __esm(() => {
|
|
|
79511
79765
|
|
|
79512
79766
|
// src/lib/remote-run-contract.ts
|
|
79513
79767
|
function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
79514
|
-
const record8 =
|
|
79768
|
+
const record8 = isRecord4(payload) ? payload : {};
|
|
79515
79769
|
return {
|
|
79516
79770
|
contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
|
|
79517
79771
|
...pickString(record8, "id"),
|
|
@@ -79533,7 +79787,7 @@ function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
|
79533
79787
|
...hasOwn(record8, "details") ? { details: record8.details } : {}
|
|
79534
79788
|
};
|
|
79535
79789
|
}
|
|
79536
|
-
function
|
|
79790
|
+
function isRecord4(value) {
|
|
79537
79791
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
79538
79792
|
}
|
|
79539
79793
|
function hasOwn(record8, key) {
|
|
@@ -79672,7 +79926,7 @@ var init_remote_credit_checkout = __esm(() => {
|
|
|
79672
79926
|
|
|
79673
79927
|
// src/lib/remote-profile.ts
|
|
79674
79928
|
function customerNamePatch(input2, field2) {
|
|
79675
|
-
if (!
|
|
79929
|
+
if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
|
|
79676
79930
|
throw new Error("Provide only the requested name field.");
|
|
79677
79931
|
const value = input2[field2];
|
|
79678
79932
|
if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
|
|
@@ -79680,22 +79934,22 @@ function customerNamePatch(input2, field2) {
|
|
|
79680
79934
|
}
|
|
79681
79935
|
return { [field2]: value.trim() };
|
|
79682
79936
|
}
|
|
79683
|
-
function
|
|
79937
|
+
function isRecord5(value) {
|
|
79684
79938
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
79685
79939
|
}
|
|
79686
79940
|
function string4(value) {
|
|
79687
79941
|
return typeof value === "string" && value.length > 0;
|
|
79688
79942
|
}
|
|
79689
79943
|
function parseUpdatedProfile(value) {
|
|
79690
|
-
const user =
|
|
79691
|
-
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)) {
|
|
79692
79946
|
throw new Error("The server returned an invalid account profile.");
|
|
79693
79947
|
}
|
|
79694
79948
|
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
79695
79949
|
}
|
|
79696
79950
|
function parseUpdatedWorkspace(value) {
|
|
79697
|
-
const organization2 =
|
|
79698
|
-
if (!
|
|
79951
|
+
const organization2 = isRecord5(value) && value.organization;
|
|
79952
|
+
if (!isRecord5(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
|
|
79699
79953
|
throw new Error("The server returned an invalid workspace.");
|
|
79700
79954
|
}
|
|
79701
79955
|
return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
|
|
@@ -80877,7 +81131,7 @@ async function requestAuthApi(instance, path, options) {
|
|
|
80877
81131
|
const text4 = await res.text();
|
|
80878
81132
|
const body = text4 ? parseJsonBody(text4) : {};
|
|
80879
81133
|
if (!res.ok) {
|
|
80880
|
-
const record9 =
|
|
81134
|
+
const record9 = isRecord6(body) ? body : {};
|
|
80881
81135
|
const detail = typeof record9.detail === "string" ? record9.detail : undefined;
|
|
80882
81136
|
const error61 = typeof record9.error === "string" ? record9.error : undefined;
|
|
80883
81137
|
const code = typeof record9.code === "string" ? record9.code : undefined;
|
|
@@ -80905,7 +81159,7 @@ function condenseErrorBody(text4) {
|
|
|
80905
81159
|
return collapsed;
|
|
80906
81160
|
return `${collapsed.slice(0, MAX_ERROR_DETAIL_LENGTH - 1).trimEnd()}\u2026`;
|
|
80907
81161
|
}
|
|
80908
|
-
function
|
|
81162
|
+
function isRecord6(value) {
|
|
80909
81163
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
80910
81164
|
}
|
|
80911
81165
|
|
|
@@ -81090,10 +81344,10 @@ var init_remote_auth = __esm(() => {
|
|
|
81090
81344
|
});
|
|
81091
81345
|
|
|
81092
81346
|
// src/lib/sign-in.ts
|
|
81093
|
-
import { createHash as
|
|
81347
|
+
import { createHash as createHash21 } from "crypto";
|
|
81094
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";
|
|
81095
81349
|
import { basename as basename13, dirname as dirname21, join as join37 } from "path";
|
|
81096
|
-
function
|
|
81350
|
+
function isRecord7(value) {
|
|
81097
81351
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
81098
81352
|
}
|
|
81099
81353
|
function boundedText(value, max2 = 2048) {
|
|
@@ -81118,7 +81372,7 @@ function clampIntervalMs(seconds) {
|
|
|
81118
81372
|
return Math.min(MAX_INTERVAL_MS, Math.max(MIN_INTERVAL_MS, Math.round(seconds * 1000)));
|
|
81119
81373
|
}
|
|
81120
81374
|
function parseDeviceAuthorizationStart(body) {
|
|
81121
|
-
const record9 =
|
|
81375
|
+
const record9 = isRecord7(body) ? body : {};
|
|
81122
81376
|
if (typeof record9.error === "string") {
|
|
81123
81377
|
throw new Error(typeof record9.detail === "string" ? record9.detail : record9.error);
|
|
81124
81378
|
}
|
|
@@ -81152,7 +81406,7 @@ function pollDeviceToken(origin, deviceCode) {
|
|
|
81152
81406
|
});
|
|
81153
81407
|
}
|
|
81154
81408
|
function deviceErrorCode(error61) {
|
|
81155
|
-
if (!(error61 instanceof HostedApiError) && !(
|
|
81409
|
+
if (!(error61 instanceof HostedApiError) && !(isRecord7(error61) && error61.name === "HostedApiError"))
|
|
81156
81410
|
return;
|
|
81157
81411
|
const failure = error61;
|
|
81158
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");
|
|
@@ -81213,7 +81467,7 @@ async function pollDeviceAuthorization(input2) {
|
|
|
81213
81467
|
throw error61;
|
|
81214
81468
|
}
|
|
81215
81469
|
}
|
|
81216
|
-
const record9 =
|
|
81470
|
+
const record9 = isRecord7(body) ? body : {};
|
|
81217
81471
|
const code = typeof record9.error === "string" ? record9.error : undefined;
|
|
81218
81472
|
if (code === "authorization_pending" || code === undefined && record9.status === "pending") {
|
|
81219
81473
|
await wait();
|
|
@@ -81241,7 +81495,7 @@ function pendingDeviceSignInStem(env3) {
|
|
|
81241
81495
|
}
|
|
81242
81496
|
function pendingDeviceSignInPath(origin, env3 = process.env) {
|
|
81243
81497
|
const { dir, stem } = pendingDeviceSignInStem(env3);
|
|
81244
|
-
return join37(dir, `${stem}-${
|
|
81498
|
+
return join37(dir, `${stem}-${createHash21("sha256").update(origin).digest("hex").slice(0, 16)}.json`);
|
|
81245
81499
|
}
|
|
81246
81500
|
function savePendingDeviceSignIn(origin, start, env3 = process.env, now3 = Date.now()) {
|
|
81247
81501
|
const path = pendingDeviceSignInPath(origin, env3);
|
|
@@ -81271,7 +81525,7 @@ function loadPendingDeviceSignIn(origin, env3 = process.env, now3 = Date.now())
|
|
|
81271
81525
|
} catch {
|
|
81272
81526
|
return null;
|
|
81273
81527
|
}
|
|
81274
|
-
if (!
|
|
81528
|
+
if (!isRecord7(parsed))
|
|
81275
81529
|
return null;
|
|
81276
81530
|
const deviceCode = boundedText(parsed.deviceCode, 512);
|
|
81277
81531
|
const userCode = boundedText(parsed.userCode, 64);
|
|
@@ -81313,7 +81567,7 @@ function pendingDeviceSignInOrigins(env3 = process.env, now3 = Date.now()) {
|
|
|
81313
81567
|
continue;
|
|
81314
81568
|
try {
|
|
81315
81569
|
const parsed = JSON.parse(readFileSync21(join37(location.dir, entry), "utf8"));
|
|
81316
|
-
if (
|
|
81570
|
+
if (isRecord7(parsed) && typeof parsed.origin === "string" && Number(parsed.expiresAt) > now3 && pendingDeviceSignInPath(parsed.origin, env3) === join37(location.dir, entry))
|
|
81317
81571
|
origins.push(parsed.origin);
|
|
81318
81572
|
} catch {}
|
|
81319
81573
|
}
|
|
@@ -81355,7 +81609,7 @@ async function persistSignIn(result2, origin, env3 = process.env, issuedBy = "si
|
|
|
81355
81609
|
headers: { Authorization: `Bearer ${result2.token}` },
|
|
81356
81610
|
body: JSON.stringify({ name: "cli" })
|
|
81357
81611
|
});
|
|
81358
|
-
apiKey =
|
|
81612
|
+
apiKey = isRecord7(created) && typeof created.key === "string" && created.key.trim() ? created.key : undefined;
|
|
81359
81613
|
}
|
|
81360
81614
|
if (!apiKey)
|
|
81361
81615
|
throw new Error("Sign-in succeeded but the server returned no API key");
|
|
@@ -81394,7 +81648,7 @@ async function revokeStoredKey(origin, apiKey) {
|
|
|
81394
81648
|
}
|
|
81395
81649
|
return { outcome: "failed", detail: "the server could not be reached" };
|
|
81396
81650
|
}
|
|
81397
|
-
if (
|
|
81651
|
+
if (isRecord7(body) && body.revoked === true)
|
|
81398
81652
|
return { outcome: "revoked" };
|
|
81399
81653
|
return { outcome: "not_revoked", detail: "the server answered without confirming revocation" };
|
|
81400
81654
|
}
|
|
@@ -81518,10 +81772,10 @@ async function readSignedInAccount(env3 = process.env) {
|
|
|
81518
81772
|
const body = await new RemoteSkillsAuthClient(connection.apiOrigin).request("/api/auth/whoami", {
|
|
81519
81773
|
headers: { Authorization: `Bearer ${connection.apiKey}` }
|
|
81520
81774
|
});
|
|
81521
|
-
const record9 =
|
|
81522
|
-
const data =
|
|
81523
|
-
const user =
|
|
81524
|
-
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 : {};
|
|
81525
81779
|
const email4 = boundedText(user.email, 320);
|
|
81526
81780
|
const org = boundedText(organization2.slug, 200) ?? boundedText(organization2.name, 200);
|
|
81527
81781
|
return { ...base2, ...email4 ? { email: email4 } : {}, ...org ? { organization: org } : {} };
|
|
@@ -81936,7 +82190,7 @@ function optionPrefix(program2, args) {
|
|
|
81936
82190
|
}
|
|
81937
82191
|
|
|
81938
82192
|
// src/lib/revision.ts
|
|
81939
|
-
import { createHash as
|
|
82193
|
+
import { createHash as createHash22 } from "crypto";
|
|
81940
82194
|
function revisionIdOf(content) {
|
|
81941
82195
|
const canonicalContent = {
|
|
81942
82196
|
slug: content.slug,
|
|
@@ -81956,8 +82210,8 @@ function revisionIdOf(content) {
|
|
|
81956
82210
|
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
81957
82211
|
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
81958
82212
|
}
|
|
81959
|
-
const
|
|
81960
|
-
return
|
|
82213
|
+
const canonical6 = JSON.stringify(canonicalContent);
|
|
82214
|
+
return createHash22("sha256").update(canonical6).digest("hex");
|
|
81961
82215
|
}
|
|
81962
82216
|
var REVISION_ID_PATTERN;
|
|
81963
82217
|
var init_revision = __esm(() => {
|
|
@@ -82406,7 +82660,7 @@ var init_pull = __esm(() => {
|
|
|
82406
82660
|
});
|
|
82407
82661
|
|
|
82408
82662
|
// src/lib/skill-context.ts
|
|
82409
|
-
import { createHash as
|
|
82663
|
+
import { createHash as createHash23 } from "crypto";
|
|
82410
82664
|
import { resolve as resolve19 } from "path";
|
|
82411
82665
|
function words2(value) {
|
|
82412
82666
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
@@ -82495,7 +82749,7 @@ ${content}`;
|
|
|
82495
82749
|
`);
|
|
82496
82750
|
const receipt = {
|
|
82497
82751
|
schemaVersion: 1,
|
|
82498
|
-
id:
|
|
82752
|
+
id: createHash23("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
82499
82753
|
profileId: profile.profileId,
|
|
82500
82754
|
profileRevision: profile.profileRevision,
|
|
82501
82755
|
authority: profile.authority,
|
|
@@ -82517,8 +82771,8 @@ var init_skill_context = __esm(() => {
|
|
|
82517
82771
|
});
|
|
82518
82772
|
|
|
82519
82773
|
// src/lib/session-reconciliation.ts
|
|
82520
|
-
import { createHash as
|
|
82521
|
-
import { isDeepStrictEqual as
|
|
82774
|
+
import { createHash as createHash24 } from "crypto";
|
|
82775
|
+
import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
|
|
82522
82776
|
function exactTimestamp(value) {
|
|
82523
82777
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
82524
82778
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -82575,7 +82829,7 @@ async function reconcileSkillSessionIfSafe(sessionId, profileId, options = {}) {
|
|
|
82575
82829
|
for (const key of old.loaded) {
|
|
82576
82830
|
const previous = oldByKey.get(key);
|
|
82577
82831
|
const current = targetBySlug.get(previous.slug);
|
|
82578
|
-
if (!current || !
|
|
82832
|
+
if (!current || !isDeepStrictEqual9({ ...previous, profileRevision: target.profileRevision }, current)) {
|
|
82579
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.");
|
|
82580
82834
|
}
|
|
82581
82835
|
}
|
|
@@ -82657,7 +82911,7 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
82657
82911
|
const result2 = replaceSkillSession(skillSessionSnapshotBinding(snapshot2), replacement, { ...plan, planDigest }, options, () => assertPlanWindow(issuedAt, expiresAt, (options.now ?? Date.now)()));
|
|
82658
82912
|
return { applied: true, plan, planDigest, ...result2 };
|
|
82659
82913
|
}
|
|
82660
|
-
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");
|
|
82661
82915
|
var init_session_reconciliation = __esm(() => {
|
|
82662
82916
|
init_profile_client();
|
|
82663
82917
|
init_selection_cache();
|
|
@@ -83793,7 +84047,7 @@ function handleDiff(name, options) {
|
|
|
83793
84047
|
const sourcePath = getSkillPath(bare);
|
|
83794
84048
|
const canonicalDir = join39(resolveCorpusRoot(), normalized);
|
|
83795
84049
|
const canonicalSkillMd = join39(canonicalDir, "SKILL.md");
|
|
83796
|
-
const
|
|
84050
|
+
const canonical6 = {
|
|
83797
84051
|
present: existsSync22(canonicalSkillMd),
|
|
83798
84052
|
path: canonicalDir,
|
|
83799
84053
|
...existsSync22(canonicalSkillMd) ? { hash: hashSkillMarkdownFile(canonicalSkillMd) } : {},
|
|
@@ -83825,7 +84079,7 @@ function handleDiff(name, options) {
|
|
|
83825
84079
|
stub = undefined;
|
|
83826
84080
|
}
|
|
83827
84081
|
}
|
|
83828
|
-
const diverged = present &&
|
|
84082
|
+
const diverged = present && canonical6.present && managed && hash5 !== canonical6.hash;
|
|
83829
84083
|
homes.push({
|
|
83830
84084
|
agent,
|
|
83831
84085
|
path: dir,
|
|
@@ -83840,7 +84094,7 @@ function handleDiff(name, options) {
|
|
|
83840
84094
|
if (options.json) {
|
|
83841
84095
|
console.log(JSON.stringify({
|
|
83842
84096
|
name: bare,
|
|
83843
|
-
canonical:
|
|
84097
|
+
canonical: canonical6,
|
|
83844
84098
|
pinned,
|
|
83845
84099
|
installedVersion,
|
|
83846
84100
|
registryVersion,
|
|
@@ -83848,10 +84102,10 @@ function handleDiff(name, options) {
|
|
|
83848
84102
|
homes
|
|
83849
84103
|
}, null, 2));
|
|
83850
84104
|
} else {
|
|
83851
|
-
if (!
|
|
84105
|
+
if (!canonical6.present) {
|
|
83852
84106
|
console.log(source_default.yellow(`${bare}: no canonical corpus entry`));
|
|
83853
84107
|
} else {
|
|
83854
|
-
console.log(source_default.dim(`${bare} canonical: ${
|
|
84108
|
+
console.log(source_default.dim(`${bare} canonical: ${canonical6.hash?.slice(0, 12)}\u2026`));
|
|
83855
84109
|
}
|
|
83856
84110
|
if (pinned) {
|
|
83857
84111
|
console.log(upToDate ? source_default.green(`\u2713 pin is up to date (${installedVersion})`) : source_default.yellow(`pin metadata differs: ${installedVersion} \u2192 ${registryVersion}`));
|
|
@@ -83862,14 +84116,14 @@ function handleDiff(name, options) {
|
|
|
83862
84116
|
} else if (!home.managed) {
|
|
83863
84117
|
console.log(`${source_default.dim("\u2022")} ${home.agent}: ${source_default.yellow("unmarked (adoption candidate)")}`);
|
|
83864
84118
|
} else if (home.diverged) {
|
|
83865
|
-
const stubNote = home.stub === true ? " (home is a pointer stub; canonical holds content)" :
|
|
83866
|
-
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}`);
|
|
83867
84121
|
} else {
|
|
83868
84122
|
console.log(`${source_default.green("\u2713")} ${home.agent}: ${source_default.green("matches canonical")}`);
|
|
83869
84123
|
}
|
|
83870
84124
|
}
|
|
83871
84125
|
}
|
|
83872
|
-
if (divergedHomes.length > 0 || homes.length > 0 && !
|
|
84126
|
+
if (divergedHomes.length > 0 || homes.length > 0 && !canonical6.present)
|
|
83873
84127
|
process.exitCode = 1;
|
|
83874
84128
|
}
|
|
83875
84129
|
var init_introspect = __esm(() => {
|
|
@@ -87175,7 +87429,7 @@ var init_execution_grants = __esm(() => {
|
|
|
87175
87429
|
});
|
|
87176
87430
|
|
|
87177
87431
|
// src/lib/execution-grant-client.ts
|
|
87178
|
-
import { isDeepStrictEqual as
|
|
87432
|
+
import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
|
|
87179
87433
|
function id(value) {
|
|
87180
87434
|
if (!grantIdentifier(value))
|
|
87181
87435
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -87227,7 +87481,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
|
|
|
87227
87481
|
headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
|
|
87228
87482
|
body: JSON.stringify({ grants: snapshot2 })
|
|
87229
87483
|
}));
|
|
87230
|
-
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !
|
|
87484
|
+
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual10(policy.grants, snapshot2)) {
|
|
87231
87485
|
throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
|
|
87232
87486
|
}
|
|
87233
87487
|
return policy;
|
|
@@ -87238,7 +87492,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
87238
87492
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
87239
87493
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
87240
87494
|
}
|
|
87241
|
-
if (!
|
|
87495
|
+
if (!isDeepStrictEqual10(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual10(value.bindings.consumer, snapshot2.consumer)) {
|
|
87242
87496
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
87243
87497
|
}
|
|
87244
87498
|
return value;
|
|
@@ -87257,7 +87511,7 @@ __export(exports_selected_run, {
|
|
|
87257
87511
|
prepareSelectedSecretBindings: () => prepareSelectedSecretBindings,
|
|
87258
87512
|
executeSelectedLocal: () => executeSelectedLocal
|
|
87259
87513
|
});
|
|
87260
|
-
import { createHash as
|
|
87514
|
+
import { createHash as createHash25 } from "crypto";
|
|
87261
87515
|
import { spawn as spawn3 } from "child_process";
|
|
87262
87516
|
import { mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync4, writeFileSync as writeFileSync15, realpathSync as realpathSync11 } from "fs";
|
|
87263
87517
|
import { dirname as dirname23, join as join45, resolve as resolve20 } from "path";
|
|
@@ -87383,7 +87637,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
87383
87637
|
selection: selection2,
|
|
87384
87638
|
target: "local",
|
|
87385
87639
|
runtime: runtimeName,
|
|
87386
|
-
inputDigest: `sha256:${
|
|
87640
|
+
inputDigest: `sha256:${createHash25("sha256").update(input2).digest("hex")}`,
|
|
87387
87641
|
runDirectory,
|
|
87388
87642
|
...executionGrant ? { executionGrant } : {},
|
|
87389
87643
|
...bindings ? { secretBinding: { schema: bindings.schema, selection: bindings.selection, consumer: bindings.consumer, secretsAuthority: bindings.secretsAuthority, bindings: bindings.bindings } } : {},
|
|
@@ -91600,7 +91854,7 @@ var init_runs = __esm(() => {
|
|
|
91600
91854
|
});
|
|
91601
91855
|
|
|
91602
91856
|
// src/lib/run-state.ts
|
|
91603
|
-
import { createHash as
|
|
91857
|
+
import { createHash as createHash26, randomBytes } from "crypto";
|
|
91604
91858
|
import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync32, readdirSync as readdirSync18, statSync as statSync16, writeFileSync as writeFileSync16 } from "fs";
|
|
91605
91859
|
import { extname, join as join46, relative as relative7 } from "path";
|
|
91606
91860
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
@@ -91737,7 +91991,7 @@ function collectRunArtifacts(context) {
|
|
|
91737
91991
|
artifacts.push({
|
|
91738
91992
|
path: toProjectRelative(context.targetDir, path),
|
|
91739
91993
|
mime: mimeForPath(path),
|
|
91740
|
-
sha256:
|
|
91994
|
+
sha256: createHash26("sha256").update(bytes).digest("hex"),
|
|
91741
91995
|
sizeBytes: stat2.size
|
|
91742
91996
|
});
|
|
91743
91997
|
}
|
|
@@ -97463,8 +97717,8 @@ var require_resolve = __commonJS((exports) => {
|
|
|
97463
97717
|
}
|
|
97464
97718
|
return count;
|
|
97465
97719
|
}
|
|
97466
|
-
function getFullPath(resolver, id2 = "",
|
|
97467
|
-
if (
|
|
97720
|
+
function getFullPath(resolver, id2 = "", normalize5) {
|
|
97721
|
+
if (normalize5 !== false)
|
|
97468
97722
|
id2 = normalizeId(id2);
|
|
97469
97723
|
const p = resolver.parse(id2);
|
|
97470
97724
|
return _getFullPath(resolver, p);
|
|
@@ -99023,7 +99277,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
99023
99277
|
}
|
|
99024
99278
|
return decodedScheme;
|
|
99025
99279
|
}
|
|
99026
|
-
function
|
|
99280
|
+
function normalize5(uri, options) {
|
|
99027
99281
|
if (typeof uri === "string") {
|
|
99028
99282
|
uri = normalizeString(uri, options);
|
|
99029
99283
|
} else if (typeof uri === "object") {
|
|
@@ -99400,7 +99654,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
99400
99654
|
}
|
|
99401
99655
|
var fastUri = {
|
|
99402
99656
|
SCHEMES,
|
|
99403
|
-
normalize:
|
|
99657
|
+
normalize: normalize5,
|
|
99404
99658
|
resolve: resolve21,
|
|
99405
99659
|
resolveComponent,
|
|
99406
99660
|
equal,
|
|
@@ -104410,7 +104664,7 @@ async function continueRecurringRecovery(customer, directory, options) {
|
|
|
104410
104664
|
throw error61;
|
|
104411
104665
|
}
|
|
104412
104666
|
}
|
|
104413
|
-
var uuid11,
|
|
104667
|
+
var uuid11, sha5, common2, intentSchema, stateSchema, invalid6 = () => {
|
|
104414
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.");
|
|
104415
104669
|
};
|
|
104416
104670
|
var init_recurring_recovery = __esm(() => {
|
|
@@ -104420,7 +104674,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
104420
104674
|
init_remote_recurring();
|
|
104421
104675
|
init_recurring_customer();
|
|
104422
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}$/);
|
|
104423
|
-
|
|
104677
|
+
sha5 = exports_external.string().regex(/^[a-f0-9]{64}$/);
|
|
104424
104678
|
common2 = exports_external.object({
|
|
104425
104679
|
contractVersion: exports_external.literal(1),
|
|
104426
104680
|
apiOrigin: exports_external.string().min(1).max(4096),
|
|
@@ -104433,7 +104687,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
104433
104687
|
common2.extend({
|
|
104434
104688
|
operation: exports_external.literal("activate"),
|
|
104435
104689
|
draftId: uuid11,
|
|
104436
|
-
termsSha256:
|
|
104690
|
+
termsSha256: sha5,
|
|
104437
104691
|
approvalDeadline: exports_external.string().datetime(),
|
|
104438
104692
|
approval: recurringActivationSchema
|
|
104439
104693
|
}).strict(),
|
|
@@ -104441,7 +104695,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
104441
104695
|
]);
|
|
104442
104696
|
stateSchema = exports_external.object({
|
|
104443
104697
|
contractVersion: exports_external.literal(1),
|
|
104444
|
-
intentSha256:
|
|
104698
|
+
intentSha256: sha5,
|
|
104445
104699
|
phase: exports_external.enum(["prepared", "pending", "observed"]),
|
|
104446
104700
|
consentId: uuid11.nullable()
|
|
104447
104701
|
}).strict();
|
|
@@ -106985,7 +107239,7 @@ var init_schedule_tools = __esm(() => {
|
|
|
106985
107239
|
});
|
|
106986
107240
|
|
|
106987
107241
|
// src/lib/native-storage.ts
|
|
106988
|
-
import { createHash as
|
|
107242
|
+
import { createHash as createHash27, createHmac as createHmac3 } from "crypto";
|
|
106989
107243
|
import {
|
|
106990
107244
|
existsSync as existsSync31,
|
|
106991
107245
|
mkdirSync as mkdirSync21,
|
|
@@ -106994,7 +107248,7 @@ import {
|
|
|
106994
107248
|
statSync as statSync18,
|
|
106995
107249
|
writeFileSync as writeFileSync20
|
|
106996
107250
|
} from "fs";
|
|
106997
|
-
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";
|
|
106998
107252
|
function resolveSkillsNativeStorageConfig(env3 = process.env) {
|
|
106999
107253
|
assertNoRetiredModeEnvVars(env3, {
|
|
107000
107254
|
app: SKILLS_ENV_NAMESPACE,
|
|
@@ -107062,7 +107316,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
107062
107316
|
files.push({
|
|
107063
107317
|
path: relativePath,
|
|
107064
107318
|
sizeBytes: bytes.byteLength,
|
|
107065
|
-
sha256:
|
|
107319
|
+
sha256: createHash27("sha256").update(bytes).digest("hex"),
|
|
107066
107320
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
107067
107321
|
});
|
|
107068
107322
|
}
|
|
@@ -108244,7 +108498,7 @@ var init_remote_account2 = __esm(() => {
|
|
|
108244
108498
|
});
|
|
108245
108499
|
|
|
108246
108500
|
// src/cli/commands/runtime-copyfile.ts
|
|
108247
|
-
import { createHash as
|
|
108501
|
+
import { createHash as createHash28, randomUUID as randomUUID9 } from "crypto";
|
|
108248
108502
|
import { Readable } from "stream";
|
|
108249
108503
|
import { createGunzip as createGunzip2 } from "zlib";
|
|
108250
108504
|
import {
|
|
@@ -108268,7 +108522,7 @@ function object9(value, code2) {
|
|
|
108268
108522
|
return value;
|
|
108269
108523
|
}
|
|
108270
108524
|
function hash5(bytes) {
|
|
108271
|
-
return
|
|
108525
|
+
return createHash28("sha256").update(bytes).digest("hex");
|
|
108272
108526
|
}
|
|
108273
108527
|
function isWithin(parent, child) {
|
|
108274
108528
|
const rel = relative9(parent, child);
|
|
@@ -108419,7 +108673,7 @@ function verifyIntegrity(bytes, integrity) {
|
|
|
108419
108673
|
const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(integrity);
|
|
108420
108674
|
if (!match)
|
|
108421
108675
|
throw new Error("REGISTRY_INTEGRITY_UNSUPPORTED");
|
|
108422
|
-
const actual =
|
|
108676
|
+
const actual = createHash28("sha512").update(bytes).digest("base64");
|
|
108423
108677
|
if (actual !== match[1])
|
|
108424
108678
|
throw new Error("TARBALL_INTEGRITY_MISMATCH");
|
|
108425
108679
|
}
|
|
@@ -109420,7 +109674,7 @@ var exports_runtime = {};
|
|
|
109420
109674
|
__export(exports_runtime, {
|
|
109421
109675
|
registerRuntime: () => registerRuntime
|
|
109422
109676
|
});
|
|
109423
|
-
import { createHash as
|
|
109677
|
+
import { createHash as createHash29, randomUUID as randomUUID10 } from "crypto";
|
|
109424
109678
|
import { lstatSync as lstatSync29, mkdirSync as mkdirSync25, readFileSync as readFileSync38, realpathSync as realpathSync15, writeFileSync as writeFileSync24 } from "fs";
|
|
109425
109679
|
import { basename as basename15, isAbsolute as isAbsolute25, join as join56 } from "path";
|
|
109426
109680
|
import { createInterface } from "readline";
|
|
@@ -109461,7 +109715,7 @@ function registerRuntime(parent) {
|
|
|
109461
109715
|
if (!expected)
|
|
109462
109716
|
throw new Error("Cloud artifact not found");
|
|
109463
109717
|
const bytes = await client2.download(id2, name);
|
|
109464
|
-
if (bytes.length !== expected.byteSize ||
|
|
109718
|
+
if (bytes.length !== expected.byteSize || createHash29("sha256").update(bytes).digest("hex") !== expected.sha256)
|
|
109465
109719
|
throw new Error("Cloud artifact integrity verification failed");
|
|
109466
109720
|
writeFileSync24(options.output, bytes, { flag: "wx", mode: 384 });
|
|
109467
109721
|
console.log(JSON.stringify({ path: options.output, sha256: expected.sha256, byteSize: bytes.length }));
|
|
@@ -111104,7 +111358,7 @@ var init_portable_snapshot_filter = __esm(() => {
|
|
|
111104
111358
|
});
|
|
111105
111359
|
|
|
111106
111360
|
// src/lib/station-snapshot.ts
|
|
111107
|
-
import { createHash as
|
|
111361
|
+
import { createHash as createHash30 } from "crypto";
|
|
111108
111362
|
import {
|
|
111109
111363
|
copyFileSync as copyFileSync2,
|
|
111110
111364
|
mkdirSync as mkdirSync26,
|
|
@@ -111119,7 +111373,7 @@ function validateStationId(stationId) {
|
|
|
111119
111373
|
}
|
|
111120
111374
|
}
|
|
111121
111375
|
function sha256File(filePath) {
|
|
111122
|
-
return
|
|
111376
|
+
return createHash30("sha256").update(readFileSync39(filePath)).digest("hex");
|
|
111123
111377
|
}
|
|
111124
111378
|
function scanHome(definition, homesRoot) {
|
|
111125
111379
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -111691,8 +111945,8 @@ var init_create_sync_config = __esm(() => {
|
|
|
111691
111945
|
});
|
|
111692
111946
|
|
|
111693
111947
|
// src/lib/codex-native-config.ts
|
|
111694
|
-
import { isDeepStrictEqual as
|
|
111695
|
-
function
|
|
111948
|
+
import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
|
|
111949
|
+
function isRecord8(value) {
|
|
111696
111950
|
if (!value || typeof value !== "object")
|
|
111697
111951
|
return false;
|
|
111698
111952
|
const prototype = Object.getPrototypeOf(value);
|
|
@@ -111713,10 +111967,10 @@ function decimalIdentity(text4) {
|
|
|
111713
111967
|
return `${match[1]}${coefficient}e${exponent}`;
|
|
111714
111968
|
}
|
|
111715
111969
|
function codexNativeConfigEqual(native, expected) {
|
|
111716
|
-
if (
|
|
111970
|
+
if (isDeepStrictEqual11(native, expected))
|
|
111717
111971
|
return true;
|
|
111718
111972
|
if (typeof expected === "number") {
|
|
111719
|
-
if (!Number.isFinite(expected) || !
|
|
111973
|
+
if (!Number.isFinite(expected) || !isRecord8(native))
|
|
111720
111974
|
return false;
|
|
111721
111975
|
const keys3 = Object.keys(native);
|
|
111722
111976
|
const value = native[NUMBER_TAG];
|
|
@@ -111728,7 +111982,7 @@ function codexNativeConfigEqual(native, expected) {
|
|
|
111728
111982
|
if (Array.isArray(native) || Array.isArray(expected)) {
|
|
111729
111983
|
return Array.isArray(native) && Array.isArray(expected) && native.length === expected.length && native.every((value, index) => codexNativeConfigEqual(value, expected[index]));
|
|
111730
111984
|
}
|
|
111731
|
-
if (!
|
|
111985
|
+
if (!isRecord8(native) || !isRecord8(expected))
|
|
111732
111986
|
return false;
|
|
111733
111987
|
const actual = native, wanted = expected;
|
|
111734
111988
|
return Object.keys(actual).length === Object.keys(wanted).length && Object.keys(wanted).every((key) => Object.hasOwn(actual, key) && codexNativeConfigEqual(actual[key], wanted[key]));
|
|
@@ -111739,42 +111993,42 @@ var init_codex_native_config = () => {};
|
|
|
111739
111993
|
// src/lib/codex-hook-trust-identity.ts
|
|
111740
111994
|
import { realpathSync as realpathSync16 } from "fs";
|
|
111741
111995
|
import { dirname as dirname33, isAbsolute as isAbsolute27, join as join59 } from "path";
|
|
111742
|
-
import { isDeepStrictEqual as
|
|
111996
|
+
import { isDeepStrictEqual as isDeepStrictEqual12 } from "util";
|
|
111743
111997
|
function inspectRecordedSkillsCli(command, expected) {
|
|
111744
|
-
|
|
111998
|
+
need6(isAbsolute27(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
|
|
111745
111999
|
const resolved = realpathSync16(command);
|
|
111746
112000
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
111747
112001
|
const manifest = snapshot(join59(dirname33(dirname33(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
111748
112002
|
const pkg = JSON.parse(manifest.text);
|
|
111749
|
-
|
|
111750
|
-
|
|
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");
|
|
111751
112005
|
const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
|
|
111752
|
-
|
|
112006
|
+
need6(isDeepStrictEqual12(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
111753
112007
|
return { receipt, recheck() {
|
|
111754
|
-
|
|
112008
|
+
need6(realpathSync16(command) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
111755
112009
|
unchanged2(cli);
|
|
111756
112010
|
unchanged2(manifest);
|
|
111757
112011
|
} };
|
|
111758
112012
|
}
|
|
111759
112013
|
function bindSkillsCli(command, reviewed) {
|
|
111760
112014
|
const normal = Bun.which("skills", { PATH: process.env.PATH });
|
|
111761
|
-
|
|
112015
|
+
need6(normal, "SKILLS_COMMAND_UNAVAILABLE");
|
|
111762
112016
|
const resolved = realpathSync16(normal);
|
|
111763
112017
|
const bound = isAbsolute27(command) ? command : command === "skills" ? normal : undefined;
|
|
111764
|
-
|
|
112018
|
+
need6(bound && realpathSync16(bound) === resolved, "SKILLS_COMMAND_MISMATCH");
|
|
111765
112019
|
const entrypoint = reviewed?.path ?? process.argv[1];
|
|
111766
|
-
|
|
112020
|
+
need6(entrypoint && realpathSync16(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
|
|
111767
112021
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
111768
112022
|
const manifest = snapshot(join59(dirname33(dirname33(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
111769
112023
|
const pkg = JSON.parse(manifest.text);
|
|
111770
|
-
|
|
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");
|
|
111771
112025
|
if (reviewed)
|
|
111772
|
-
|
|
111773
|
-
|
|
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");
|
|
111774
112028
|
return {
|
|
111775
112029
|
receipt: { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 },
|
|
111776
112030
|
recheck() {
|
|
111777
|
-
|
|
112031
|
+
need6(Bun.which("skills", { PATH: process.env.PATH }) === normal && realpathSync16(normal) === resolved && realpathSync16(bound) === resolved && realpathSync16(entrypoint) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
111778
112032
|
unchanged2(cli);
|
|
111779
112033
|
unchanged2(manifest);
|
|
111780
112034
|
}
|
|
@@ -111785,19 +112039,19 @@ var init_codex_hook_trust_identity = __esm(() => {
|
|
|
111785
112039
|
});
|
|
111786
112040
|
|
|
111787
112041
|
// src/lib/codex-hook-policy-recovery.ts
|
|
111788
|
-
import { isDeepStrictEqual as
|
|
112042
|
+
import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
|
|
111789
112043
|
function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
|
|
111790
|
-
|
|
111791
|
-
|
|
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");
|
|
111792
112046
|
const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
|
|
111793
112047
|
const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
|
|
111794
|
-
|
|
111795
|
-
const changes = ["roots", "sources"].filter((key) => !
|
|
111796
|
-
|
|
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");
|
|
111797
112051
|
const compared = structuredClone(current);
|
|
111798
112052
|
compared.bridge.discovery.claude.roots = oldDiscovery.roots;
|
|
111799
112053
|
compared.bridge.discovery.claude.sources = oldDiscovery.sources;
|
|
111800
|
-
|
|
112054
|
+
need6(isDeepStrictEqual13(before2, compared), "RECONCILE_POLICY_CHANGED");
|
|
111801
112055
|
verifyAgentDiscovery(newDiscovery);
|
|
111802
112056
|
return { reason: options.reason, changes };
|
|
111803
112057
|
}
|
|
@@ -111809,21 +112063,21 @@ var init_codex_hook_policy_recovery = __esm(() => {
|
|
|
111809
112063
|
|
|
111810
112064
|
// src/lib/agent-codex-trust.ts
|
|
111811
112065
|
import { lstatSync as lstatSync31, mkdirSync as mkdirSync27, readdirSync as readdirSync23, readFileSync as readFileSync40, realpathSync as realpathSync17, statSync as statSync21 } from "fs";
|
|
111812
|
-
import { createHash as
|
|
112066
|
+
import { createHash as createHash31, randomUUID as randomUUID11 } from "crypto";
|
|
111813
112067
|
import { execFileSync } from "child_process";
|
|
111814
112068
|
import { dirname as dirname34, isAbsolute as isAbsolute28, join as join60, resolve as resolve25 } from "path";
|
|
111815
112069
|
import { homedir as homedir12 } from "os";
|
|
111816
|
-
import { isDeepStrictEqual as
|
|
112070
|
+
import { isDeepStrictEqual as isDeepStrictEqual14 } from "util";
|
|
111817
112071
|
function verifyReleaseArtifact(proof, skillsCli) {
|
|
111818
|
-
|
|
112072
|
+
need6(isAbsolute28(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
111819
112073
|
const tarPath = realpathSync17(proof.packageTarPath);
|
|
111820
|
-
|
|
112074
|
+
need6(statSync21(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
111821
112075
|
const tarBytes = readFileSync40(tarPath);
|
|
111822
|
-
|
|
112076
|
+
need6(tarBytes.byteLength === proof.packageTarBytes && createHash31("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
|
|
111823
112077
|
const packageRoot = dirname34(dirname34(realpathSync17(skillsCli.receipt.path)));
|
|
111824
112078
|
const manifest = readFileSync40(join60(packageRoot, "package.json"));
|
|
111825
|
-
|
|
111826
|
-
|
|
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");
|
|
111827
112081
|
const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync40(skillsCli.receipt.path)]];
|
|
111828
112082
|
for (const [member, expected] of members2) {
|
|
111829
112083
|
let actual;
|
|
@@ -111832,7 +112086,7 @@ function verifyReleaseArtifact(proof, skillsCli) {
|
|
|
111832
112086
|
} catch {
|
|
111833
112087
|
throw new Error("RECONCILE_RELEASE_ARTIFACT_MEMBER_MISSING");
|
|
111834
112088
|
}
|
|
111835
|
-
|
|
112089
|
+
need6(Buffer.compare(actual, expected) === 0, "RECONCILE_RELEASE_ARTIFACT_MEMBER_CHANGED");
|
|
111836
112090
|
}
|
|
111837
112091
|
}
|
|
111838
112092
|
async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc) {
|
|
@@ -111840,48 +112094,48 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111840
112094
|
try {
|
|
111841
112095
|
const home = resolve25(options.home ?? homedir12()), dataDir3 = resolve25(options.dataDir ?? getDataDirReadOnly());
|
|
111842
112096
|
const parent = resolve25(join60(dataDir3, "native-hook-trust")), journal = resolve25(options.journal);
|
|
111843
|
-
|
|
112097
|
+
need6(journal.startsWith(parent + "/") && /^[a-f0-9-]{36}$/.test(journal.slice(parent.length + 1)), "RECONCILE_JOURNAL_PATH");
|
|
111844
112098
|
const dir = lstatSync31(journal, { throwIfNoEntry: false });
|
|
111845
|
-
|
|
111846
|
-
|
|
111847
|
-
|
|
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");
|
|
111848
112102
|
const stoppedFile = snapshot(join60(journal, "stopped.json"), true);
|
|
111849
112103
|
const intentFile = snapshot(join60(journal, "intent.json"), true), intent = JSON.parse(intentFile.text);
|
|
111850
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);
|
|
111851
112105
|
const legacyIntent = intent.home === undefined && intent.configSha256 === undefined && intent.configVersion === undefined && intent.declarations === undefined && intent.admitted === undefined && intent.nativeHooks === undefined;
|
|
111852
|
-
|
|
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");
|
|
111853
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 }));
|
|
111854
112108
|
const admitted = intent.admitted ?? intent.hooks;
|
|
111855
112109
|
const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
|
|
111856
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 };
|
|
111857
|
-
|
|
111858
|
-
|
|
111859
|
-
|
|
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");
|
|
111860
112114
|
const policyCurrent = snapshot(join60(dataDir3, "agent-policy.json"), true);
|
|
111861
112115
|
const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
|
|
111862
112116
|
const recovery = claudeRecovery ?? additionsRecovery;
|
|
111863
|
-
|
|
111864
|
-
|
|
111865
|
-
|
|
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");
|
|
111866
112120
|
const policyChange = claudeRecovery ? reviewedClaudeDiscoveryChange(policyBefore.text, policyCurrent.text, claudeRecovery) : undefined;
|
|
111867
112121
|
let supersededBinding = false;
|
|
111868
112122
|
if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
|
|
111869
|
-
|
|
112123
|
+
need6(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
|
|
111870
112124
|
const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
|
|
111871
|
-
|
|
112125
|
+
need6(isDeepStrictEqual14(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
|
|
111872
112126
|
supersededBinding = true;
|
|
111873
112127
|
}
|
|
111874
112128
|
if (supersededBinding || recovery) {
|
|
111875
|
-
|
|
112129
|
+
need6(Array.isArray(intent.nativeHooks) && intent.nativeHooks.length > 0, "RECONCILE_NATIVE_INVENTORY_REQUIRED");
|
|
111876
112130
|
const proof = options.releaseProof;
|
|
111877
|
-
|
|
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");
|
|
111878
112132
|
}
|
|
111879
112133
|
const currentConfig = snapshot(intent.configPath, true);
|
|
111880
112134
|
const policy = parseManagedSkillPolicy(policyCurrent.text), binding = policy.bridge;
|
|
111881
112135
|
const alias = binding.rootAliases?.find((item) => item.agent === "codex"), physicalRoot = alias?.target ?? join60(home, ".codex"), hooksPath = join60(physicalRoot, "hooks.json");
|
|
111882
|
-
|
|
112136
|
+
need6(resolve25(intent.configPath) === resolve25(join60(physicalRoot, "config.toml")), "RECONCILE_CONFIG_PATH_CHANGED");
|
|
111883
112137
|
const nativeConfigPath = intent.nativeConfigPath ?? intent.configPath;
|
|
111884
|
-
|
|
112138
|
+
need6([join60(home, ".codex/config.toml"), join60(physicalRoot, "config.toml")].includes(nativeConfigPath), "RECONCILE_NATIVE_CONFIG_PATH_CHANGED");
|
|
111885
112139
|
const codexHome = nativeConfigPath !== intent.configPath ? undefined : dirname34(nativeConfigPath);
|
|
111886
112140
|
const hooksCurrent = snapshot(hooksPath);
|
|
111887
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;
|
|
@@ -111889,7 +112143,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111889
112143
|
const managedKeys = managedHooks.map((h) => h.key);
|
|
111890
112144
|
const witnessSame = codexTrustReconcileWitness(configBefore.text, managedKeys) === codexTrustReconcileWitness(currentConfig.text, managedKeys);
|
|
111891
112145
|
const unmanagedSame = codexTrustUnmanagedSemantic(configBefore.text, managedKeys) === codexTrustUnmanagedSemantic(currentConfig.text, managedKeys);
|
|
111892
|
-
|
|
112146
|
+
need6(unmanagedSame && (witnessSame || supersededBinding), "RECONCILE_UNRELATED_CONFIG_CHANGED");
|
|
111893
112147
|
const before2 = structure3(configBefore.text), current = structure3(currentConfig.text), expectedConfig = structuredClone(before2);
|
|
111894
112148
|
expectedConfig.hooks ??= {};
|
|
111895
112149
|
expectedConfig.hooks.state ??= {};
|
|
@@ -111901,10 +112155,10 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111901
112155
|
if (supersededBinding) {
|
|
111902
112156
|
for (const hook of intent.hooks) {
|
|
111903
112157
|
const state = current.hooks?.state?.[hook.key];
|
|
111904
|
-
|
|
112158
|
+
need6(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
|
|
111905
112159
|
}
|
|
111906
112160
|
} else
|
|
111907
|
-
|
|
112161
|
+
need6(isDeepStrictEqual14(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
|
|
111908
112162
|
const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
|
|
111909
112163
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
|
|
111910
112164
|
const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
|
|
@@ -111913,29 +112167,29 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111913
112167
|
const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
111914
112168
|
if (supersededBinding)
|
|
111915
112169
|
verifyReleaseArtifact(options.releaseProof, skillsCli);
|
|
111916
|
-
const skillsBindingChanged = !
|
|
111917
|
-
|
|
112170
|
+
const skillsBindingChanged = !isDeepStrictEqual14(skillsCli.receipt, intent.skillsCli);
|
|
112171
|
+
need6(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
111918
112172
|
if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
|
|
111919
|
-
|
|
112173
|
+
need6(supersededBinding, "RECONCILE_HOOKS_CHANGED");
|
|
111920
112174
|
const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
|
|
111921
112175
|
for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
|
|
111922
112176
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
111923
|
-
|
|
112177
|
+
need6(isDeepStrictEqual14(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
111924
112178
|
}
|
|
111925
112179
|
}
|
|
111926
112180
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
111927
|
-
|
|
112181
|
+
need6(rpc.version === intent.nativeVersion, "RECONCILE_NATIVE_VERSION_CHANGED");
|
|
111928
112182
|
const discovered = await rpc.request("hooks/list", { cwds: [home] });
|
|
111929
|
-
|
|
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");
|
|
111930
112184
|
const nativeHooks = discovered.data[0].hooks;
|
|
111931
112185
|
const recordedNativeHooks = intent.nativeHooks;
|
|
111932
112186
|
if (recordedNativeHooks)
|
|
111933
|
-
|
|
112187
|
+
need6(nativeHooks.length === recordedNativeHooks.length + (additions?.hooks.length ?? 0), "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
|
|
111934
112188
|
const plannedKeys = new Set(intent.hooks.map((hook) => hook.key));
|
|
111935
112189
|
let nativeTrustPending = false;
|
|
111936
112190
|
for (const expected of [...recordedNativeHooks ?? [], ...additions?.hooks ?? []]) {
|
|
111937
112191
|
const matches2 = nativeHooks.filter((hook) => hook.key === expected.key);
|
|
111938
|
-
|
|
112192
|
+
need6(matches2.length === 1, "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
|
|
111939
112193
|
const actual = safeNativeHook(matches2[0]);
|
|
111940
112194
|
const stable = (hook) => {
|
|
111941
112195
|
const { enabled, trustStatus, ...identity5 } = hook;
|
|
@@ -111943,35 +112197,35 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111943
112197
|
delete identity5.command;
|
|
111944
112198
|
return identity5;
|
|
111945
112199
|
};
|
|
111946
|
-
|
|
112200
|
+
need6(isDeepStrictEqual14(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
111947
112201
|
if (!plannedKeys.has(expected.key))
|
|
111948
|
-
|
|
112202
|
+
need6(isDeepStrictEqual14(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
|
|
111949
112203
|
}
|
|
111950
112204
|
for (const wanted of intent.hooks) {
|
|
111951
112205
|
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join60(home, ".codex/hooks.json"), hooksPath));
|
|
111952
|
-
|
|
112206
|
+
need6(matches2.length === 1, "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
111953
112207
|
const h = matches2[0];
|
|
111954
112208
|
const trusted = h.trustStatus === "trusted";
|
|
111955
|
-
|
|
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");
|
|
111956
112210
|
if (!trusted)
|
|
111957
112211
|
nativeTrustPending = true;
|
|
111958
112212
|
if (supersededBinding) {
|
|
111959
112213
|
const nativeToCliEvent = { userPromptSubmit: "UserPromptSubmit", sessionStart: "SessionStart", subagentStart: "SubagentStart" };
|
|
111960
|
-
|
|
112214
|
+
need6(h.command === renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, nativeToCliEvent[wanted.event] ?? ""), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
111961
112215
|
}
|
|
111962
112216
|
}
|
|
111963
112217
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
111964
112218
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
111965
|
-
|
|
112219
|
+
need6(layers?.length === 1 && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
|
|
111966
112220
|
const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
|
|
111967
|
-
|
|
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");
|
|
111968
112222
|
const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
111969
112223
|
const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
111970
|
-
|
|
112224
|
+
need6(finalLayers?.length === 1 && !finalLayers[0].disabledReason && codexNativeConfigEqual(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
|
|
111971
112225
|
if (recovery) {
|
|
111972
112226
|
for (const response of [config2, finalConfig]) {
|
|
111973
112227
|
const layerIndex = response.layers.findIndex((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
111974
|
-
|
|
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");
|
|
111975
112229
|
}
|
|
111976
112230
|
recoveryCli.recheck();
|
|
111977
112231
|
verifyReleaseArtifact(options.releaseProof, recoveryCli);
|
|
@@ -111992,7 +112246,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
111992
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;
|
|
111993
112247
|
const recoveryPlanDigest = recoveryPlan ? makePlanDigest(recoveryPlan) : undefined;
|
|
111994
112248
|
if (recovery?.reviewedPlanDigest !== undefined)
|
|
111995
|
-
|
|
112249
|
+
need6(recovery.reviewedPlanDigest === recoveryPlanDigest, "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
111996
112250
|
const preview2 = !!recovery && recovery.reviewedPlanDigest === undefined;
|
|
111997
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 } : {} };
|
|
111998
112252
|
if (preview2)
|
|
@@ -112015,7 +112269,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112015
112269
|
const binding = policy.bridge, alias = binding.rootAliases?.find((item) => item.agent === "codex");
|
|
112016
112270
|
const skillsCli = bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
112017
112271
|
const physicalRoot = alias?.target ?? join60(home, ".codex"), hooksPath = join60(physicalRoot, "hooks.json"), configPath = join60(physicalRoot, "config.toml");
|
|
112018
|
-
|
|
112272
|
+
need6(options.codexHome === undefined || isAbsolute28(options.codexHome) && [join60(home, ".codex"), physicalRoot].includes(options.codexHome), "NATIVE_HOME_NOT_ADMITTED");
|
|
112019
112273
|
const codexHome = options.codexHome === undefined ? undefined : physicalRoot;
|
|
112020
112274
|
const nativeConfigPath = join60(codexHome ?? join60(home, ".codex"), "config.toml");
|
|
112021
112275
|
const hooksFile = snapshot(hooksPath), configFile = snapshot(configPath, true), original = structure3(configFile.text);
|
|
@@ -112023,38 +112277,38 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112023
112277
|
for (const [event, [nativeEvent, label]] of Object.entries(eventNames)) {
|
|
112024
112278
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
112025
112279
|
const groups = declarations[event];
|
|
112026
|
-
|
|
112280
|
+
need6(Array.isArray(groups), "DECLARATION_CHANGED");
|
|
112027
112281
|
const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
|
|
112028
|
-
|
|
112282
|
+
need6(matches2.length === 1 && isDeepStrictEqual14(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
|
|
112029
112283
|
expected.push({ event: nativeEvent, key: `${join60(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
|
|
112030
112284
|
}
|
|
112031
112285
|
codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
|
|
112032
112286
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
112033
|
-
|
|
112287
|
+
need6(SUPPORTED_CODEX_HOOK_VERSIONS.includes(rpc.version), "NATIVE_UNSUPPORTED_VERSION");
|
|
112034
112288
|
const list2 = async () => {
|
|
112035
112289
|
const response = await rpc.request("hooks/list", { cwds: [home] });
|
|
112036
112290
|
const nativeRoots = [...new Set([physicalRoot, join60(home, ".codex")])];
|
|
112037
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`));
|
|
112038
112292
|
const entry = response?.data?.[0];
|
|
112039
|
-
|
|
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");
|
|
112040
112294
|
return response.data[0].hooks;
|
|
112041
112295
|
};
|
|
112042
112296
|
const admit = (hooks) => expected.map((wanted) => {
|
|
112043
112297
|
const canonicalKey = wanted.key.replace(join60(home, ".codex/hooks.json"), hooksPath);
|
|
112044
112298
|
const matches2 = hooks.filter((h2) => h2.key === wanted.key || h2.key === canonicalKey);
|
|
112045
|
-
|
|
112299
|
+
need6(matches2.length === 1, "AMBIGUOUS_IDENTITY");
|
|
112046
112300
|
const h = matches2[0];
|
|
112047
|
-
|
|
112048
|
-
|
|
112049
|
-
|
|
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");
|
|
112050
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 };
|
|
112051
112305
|
});
|
|
112052
112306
|
const discovered = await list2(), admitted = admit(discovered);
|
|
112053
112307
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
112054
112308
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
112055
|
-
|
|
112056
|
-
|
|
112057
|
-
|
|
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");
|
|
112058
112312
|
const keys3 = admitted.map((h) => h.key), planned = admitted.filter((h) => !h.enabled || h.trustStatus !== "trusted");
|
|
112059
112313
|
const preservationText = codexTrustTextWitness(configFile.text, keys3);
|
|
112060
112314
|
const checkInputs = () => {
|
|
@@ -112069,16 +112323,16 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112069
112323
|
const planDigest = makePlanDigest(digestInput);
|
|
112070
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 };
|
|
112071
112325
|
if (options.apply)
|
|
112072
|
-
|
|
112326
|
+
need6(options.reviewedPlanDigest === planDigest, "PLAN_CHANGED: run the dry-run again and pass its reviewed --plan-digest with --apply");
|
|
112073
112327
|
const parent = join60(dataDir3, "native-hook-trust");
|
|
112074
112328
|
const st = lstatSync31(parent, { throwIfNoEntry: false });
|
|
112075
112329
|
if (st) {
|
|
112076
|
-
|
|
112330
|
+
need6(st.isDirectory() && !st.isSymbolicLink() && st.uid === process.getuid() && (st.mode & 511) === 448, "UNSAFE_JOURNAL");
|
|
112077
112331
|
const priorJournals = readdirSync23(parent);
|
|
112078
|
-
|
|
112332
|
+
need6(priorJournals.length < 1000, "JOURNAL_BOUND");
|
|
112079
112333
|
for (const name of priorJournals) {
|
|
112080
|
-
|
|
112081
|
-
|
|
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");
|
|
112082
112336
|
}
|
|
112083
112337
|
}
|
|
112084
112338
|
if (!options.apply || !planned.length)
|
|
@@ -112091,7 +112345,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112091
112345
|
save(join60(journal, "hooks.before.json"), hooksFile.bytes);
|
|
112092
112346
|
save(join60(journal, "policy.before.json"), policyFile.bytes);
|
|
112093
112347
|
save(join60(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
|
|
112094
|
-
|
|
112348
|
+
need6(isDeepStrictEqual14(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
|
|
112095
112349
|
checkInputs();
|
|
112096
112350
|
let result2;
|
|
112097
112351
|
try {
|
|
@@ -112099,22 +112353,22 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112099
112353
|
} catch {
|
|
112100
112354
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_WRITE_FAILED: reconcile the private journal before retrying");
|
|
112101
112355
|
}
|
|
112102
|
-
|
|
112356
|
+
need6(result2?.status === "ok" && result2.filePath === configPath && /^sha256:[a-f0-9]{64}$/.test(result2.version), "NATIVE_WRITE_OVERRIDDEN");
|
|
112103
112357
|
const after2 = snapshot(configPath, true), expectedConfig = structuredClone(original);
|
|
112104
112358
|
expectedConfig.hooks ??= {};
|
|
112105
112359
|
expectedConfig.hooks.state ??= {};
|
|
112106
112360
|
for (const h of planned)
|
|
112107
112361
|
expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
|
|
112108
|
-
|
|
112362
|
+
need6(isDeepStrictEqual14(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
|
|
112109
112363
|
const savedConfig = snapshot(join60(journal, "config.before.toml"), true);
|
|
112110
|
-
|
|
112364
|
+
need6(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
|
|
112111
112365
|
const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
|
|
112112
112366
|
unchanged2(policyFile);
|
|
112113
112367
|
unchanged2(hooksFile);
|
|
112114
112368
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
|
|
112115
112369
|
const current = await list2(), confirmed = admit(current);
|
|
112116
|
-
|
|
112117
|
-
|
|
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");
|
|
112118
112372
|
unchanged2(after2);
|
|
112119
112373
|
unchanged2(savedConfig);
|
|
112120
112374
|
unchanged2(hooksFile);
|
|
@@ -112137,7 +112391,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
112137
112391
|
}
|
|
112138
112392
|
}
|
|
112139
112393
|
var json2 = (value) => JSON.stringify(value, null, 2) + `
|
|
112140
|
-
`, 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) => {
|
|
112141
112395
|
const copy = structuredClone(value);
|
|
112142
112396
|
if (copy?.bridge?.commands && Object.hasOwn(copy.bridge.commands, "codex"))
|
|
112143
112397
|
copy.bridge.commands.codex = "<reviewed-codex-binding>";
|
|
@@ -112277,7 +112531,7 @@ __export(exports_agent_integration, {
|
|
|
112277
112531
|
registerAgentIntegration: () => registerAgentIntegration
|
|
112278
112532
|
});
|
|
112279
112533
|
import { existsSync as existsSync35, lstatSync as lstatSync32, readFileSync as readFileSync41, writeFileSync as writeFileSync26 } from "fs";
|
|
112280
|
-
import { createHash as
|
|
112534
|
+
import { createHash as createHash32 } from "crypto";
|
|
112281
112535
|
import { homedir as homedir13 } from "os";
|
|
112282
112536
|
import { isAbsolute as isAbsolute29 } from "path";
|
|
112283
112537
|
function pinnedHookProfile(input3, configuredProfile) {
|
|
@@ -112375,7 +112629,7 @@ function registerAgentIntegration(parent) {
|
|
|
112375
112629
|
skillCount: catalog.skills.length,
|
|
112376
112630
|
output: options.output,
|
|
112377
112631
|
bytes: Buffer.byteLength(bytes),
|
|
112378
|
-
sha256:
|
|
112632
|
+
sha256: createHash32("sha256").update(bytes).digest("hex")
|
|
112379
112633
|
};
|
|
112380
112634
|
await writeCliOutput(options.json ? JSON.stringify(receipt) : `Captured ${receipt.skillCount} native Codex skill(s) in ${receipt.output}.`);
|
|
112381
112635
|
} catch (error61) {
|
|
@@ -112384,10 +112638,10 @@ function registerAgentIntegration(parent) {
|
|
|
112384
112638
|
process.exitCode = 1;
|
|
112385
112639
|
}
|
|
112386
112640
|
});
|
|
112387
|
-
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) => {
|
|
112388
|
-
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;
|
|
112389
112643
|
if (!capture2)
|
|
112390
|
-
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");
|
|
112391
112645
|
await writeCliOutput(JSON.stringify(capture2(options.path), null, 2));
|
|
112392
112646
|
});
|
|
112393
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) => {
|
|
@@ -112406,7 +112660,7 @@ function registerAgentIntegration(parent) {
|
|
|
112406
112660
|
hook.command("agents").option("--json", "Output the adapter capability inventory", false).description("Show maintained native adapters and explicit coverage limits").action(async () => {
|
|
112407
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));
|
|
112408
112662
|
});
|
|
112409
|
-
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 Codex
|
|
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) => {
|
|
112410
112664
|
try {
|
|
112411
112665
|
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync41(options.discoveryInputs, "utf8")) : undefined;
|
|
112412
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 });
|
|
@@ -112889,7 +113143,7 @@ var init_grants = __esm(() => {
|
|
|
112889
113143
|
});
|
|
112890
113144
|
|
|
112891
113145
|
// src/lib/station-hydrate.ts
|
|
112892
|
-
import { createHash as
|
|
113146
|
+
import { createHash as createHash33 } from "crypto";
|
|
112893
113147
|
import {
|
|
112894
113148
|
copyFileSync as copyFileSync3,
|
|
112895
113149
|
mkdirSync as mkdirSync28,
|
|
@@ -113092,7 +113346,7 @@ function skillSha256(skill) {
|
|
|
113092
113346
|
return sha256File(skill.files[0].winner.fullPath);
|
|
113093
113347
|
}
|
|
113094
113348
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
113095
|
-
return
|
|
113349
|
+
return createHash33("sha256").update(joined.sort().join(`
|
|
113096
113350
|
`)).digest("hex");
|
|
113097
113351
|
}
|
|
113098
113352
|
function writeStationHydration(options) {
|
|
@@ -113249,7 +113503,7 @@ var init_hydrate = __esm(() => {
|
|
|
113249
113503
|
});
|
|
113250
113504
|
|
|
113251
113505
|
// src/lib/prepare-skill.ts
|
|
113252
|
-
import { createHash as
|
|
113506
|
+
import { createHash as createHash34, randomUUID as randomUUID12 } from "crypto";
|
|
113253
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";
|
|
113254
113508
|
import { tmpdir as tmpdir2 } from "os";
|
|
113255
113509
|
import { dirname as dirname36, isAbsolute as isAbsolute30, join as join62, relative as relative11, resolve as resolve27, sep as sep14 } from "path";
|
|
@@ -113266,7 +113520,7 @@ function prepareSkill(name, options) {
|
|
|
113266
113520
|
let manifest;
|
|
113267
113521
|
try {
|
|
113268
113522
|
const parsed = JSON.parse(manifestFile.bytes.toString("utf8"));
|
|
113269
|
-
if (!
|
|
113523
|
+
if (!isRecord9(parsed))
|
|
113270
113524
|
throw new Error("not an object");
|
|
113271
113525
|
manifest = parsed;
|
|
113272
113526
|
} catch {
|
|
@@ -113288,7 +113542,7 @@ function prepareSkill(name, options) {
|
|
|
113288
113542
|
if (documentKind !== undefined && documentKind !== kind) {
|
|
113289
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.");
|
|
113290
113544
|
}
|
|
113291
|
-
if (manifest.provenance !== undefined && !
|
|
113545
|
+
if (manifest.provenance !== undefined && !isRecord9(manifest.provenance))
|
|
113292
113546
|
throw new Error("skill.json provenance must be an object.");
|
|
113293
113547
|
const provenance = manifest.provenance;
|
|
113294
113548
|
const candidate2 = { ...manifest, version: options.version, kind, provenance: { ...provenance } };
|
|
@@ -113353,7 +113607,7 @@ function escapedRelativePath(path) {
|
|
|
113353
113607
|
return JSON.stringify(bounded);
|
|
113354
113608
|
}
|
|
113355
113609
|
function validateRuntimeEntrypoint(candidate2, root2) {
|
|
113356
|
-
if (candidate2.kind !== "executable" || !
|
|
113610
|
+
if (candidate2.kind !== "executable" || !isRecord9(candidate2.runtime))
|
|
113357
113611
|
return;
|
|
113358
113612
|
const entry = candidate2.runtime.entrypoint;
|
|
113359
113613
|
if (typeof entry !== "string")
|
|
@@ -113363,7 +113617,7 @@ function validateRuntimeEntrypoint(candidate2, root2) {
|
|
|
113363
113617
|
throw new Error("Executable runtime.entrypoint must name an existing regular file inside the skill directory.");
|
|
113364
113618
|
}
|
|
113365
113619
|
}
|
|
113366
|
-
function
|
|
113620
|
+
function isRecord9(value) {
|
|
113367
113621
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
113368
113622
|
}
|
|
113369
113623
|
function validateRawCandidate(candidate2) {
|
|
@@ -113388,7 +113642,7 @@ function validateRawCandidate(candidate2) {
|
|
|
113388
113642
|
if (!Array.isArray(items))
|
|
113389
113643
|
throw new Error(`skill.json ${field2} must be an array.`);
|
|
113390
113644
|
for (const item of items) {
|
|
113391
|
-
if (!
|
|
113645
|
+
if (!isRecord9(item) || typeof item.name !== "string" || !item.name.trim())
|
|
113392
113646
|
throw new Error(`Invalid skill.json ${field2} name.`);
|
|
113393
113647
|
if (field2 === "inputs" && (typeof item.type !== "string" || !item.type.trim() || item.required !== undefined && typeof item.required !== "boolean"))
|
|
113394
113648
|
throw new Error("Invalid skill.json input type or required flag.");
|
|
@@ -113407,7 +113661,7 @@ function validateRawCandidate(candidate2) {
|
|
|
113407
113661
|
}
|
|
113408
113662
|
}
|
|
113409
113663
|
function validateRuntimeEntrypointCompatibility(candidate2) {
|
|
113410
|
-
if (candidate2.kind !== "executable" || !
|
|
113664
|
+
if (candidate2.kind !== "executable" || !isRecord9(candidate2.runtime))
|
|
113411
113665
|
return;
|
|
113412
113666
|
const runtime = candidate2.runtime.runtime;
|
|
113413
113667
|
const entrypoint = candidate2.runtime.entrypoint;
|
|
@@ -113448,7 +113702,7 @@ function snapshot2(root2) {
|
|
|
113448
113702
|
if (rootStat.isSymbolicLink() || !rootStat.isDirectory())
|
|
113449
113703
|
throw new Error("Skill root must be a real directory, not a symlink.");
|
|
113450
113704
|
const files = [], directories = [];
|
|
113451
|
-
const hash6 =
|
|
113705
|
+
const hash6 = createHash34("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
|
|
113452
113706
|
let count = 0, bytes = 0;
|
|
113453
113707
|
const walk2 = (relative12) => {
|
|
113454
113708
|
for (const name of readdirSync25(join62(root2, relative12)).sort()) {
|
|
@@ -114734,7 +114988,7 @@ async function apiRequest(path, options, instance) {
|
|
|
114734
114988
|
const origin = instance ?? getApiUrl(`${(options?.method || "GET").toUpperCase()} ${path}`);
|
|
114735
114989
|
return new RemoteSkillsAuthClient(origin).request(path, options);
|
|
114736
114990
|
}
|
|
114737
|
-
function
|
|
114991
|
+
function isRecord10(value) {
|
|
114738
114992
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
114739
114993
|
}
|
|
114740
114994
|
function commandErrorPayload(err, fallback) {
|
|
@@ -114748,11 +115002,11 @@ function commandErrorPayload(err, fallback) {
|
|
|
114748
115002
|
...err.apiUrl ? { apiUrl: err.apiUrl } : {}
|
|
114749
115003
|
};
|
|
114750
115004
|
}
|
|
114751
|
-
const code2 =
|
|
115005
|
+
const code2 = isRecord10(err) && typeof err.code === "string" ? err.code : undefined;
|
|
114752
115006
|
return {
|
|
114753
115007
|
error: err?.message || fallback,
|
|
114754
115008
|
...code2 ? { code: code2 } : {},
|
|
114755
|
-
...
|
|
115009
|
+
...isRecord10(err) && Array.isArray(err.next) ? { next: err.next } : {}
|
|
114756
115010
|
};
|
|
114757
115011
|
}
|
|
114758
115012
|
function writeCommandError(err, fallback, json3) {
|
|
@@ -114777,7 +115031,7 @@ function stringField2(value) {
|
|
|
114777
115031
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
114778
115032
|
}
|
|
114779
115033
|
function recordField(value) {
|
|
114780
|
-
return
|
|
115034
|
+
return isRecord10(value) ? value : undefined;
|
|
114781
115035
|
}
|
|
114782
115036
|
function identityApiKey(live) {
|
|
114783
115037
|
const root2 = recordField(live);
|
|
@@ -115250,7 +115504,7 @@ async function runWhoami(options) {
|
|
|
115250
115504
|
} catch {}
|
|
115251
115505
|
payload.permissions = { publish: access?.permissions?.publish ?? null, profilesWrite: access?.permissions?.profilesWrite ?? null };
|
|
115252
115506
|
const apiKey = identityApiKey(res);
|
|
115253
|
-
if (access?.scopes && (apiKey === undefined ||
|
|
115507
|
+
if (access?.scopes && (apiKey === undefined || isRecord10(apiKey) && !Object.hasOwn(apiKey, "scopes"))) {
|
|
115254
115508
|
payload.scopes = access.scopes;
|
|
115255
115509
|
}
|
|
115256
115510
|
if (options.json) {
|