@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/dist/index.js
CHANGED
|
@@ -7262,7 +7262,7 @@ import {
|
|
|
7262
7262
|
statSync as statSync10,
|
|
7263
7263
|
writeFileSync as writeFileSync8
|
|
7264
7264
|
} from "fs";
|
|
7265
|
-
import { basename as basename11, dirname as dirname19, isAbsolute as isAbsolute17, join as join29, normalize as
|
|
7265
|
+
import { basename as basename11, dirname as dirname19, isAbsolute as isAbsolute17, join as join29, normalize as normalize4, relative as relative6, resolve as resolve17, sep as sep8 } from "path";
|
|
7266
7266
|
|
|
7267
7267
|
// src/lib/registry-data/index.ts
|
|
7268
7268
|
var SKILLS = [];
|
|
@@ -9038,12 +9038,14 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9038
9038
|
}
|
|
9039
9039
|
if (bridge.codexPluginSkills !== undefined)
|
|
9040
9040
|
for (const control of array(bridge.codexPluginSkills, 4096)) {
|
|
9041
|
-
requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256"].includes(key)));
|
|
9041
|
+
requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "remotePluginId"].includes(key)));
|
|
9042
9042
|
text(control.name, 129);
|
|
9043
9043
|
text(control.pluginId, 1024);
|
|
9044
9044
|
text(control.namespace, 64);
|
|
9045
9045
|
text(control.pluginParent);
|
|
9046
9046
|
requireBound(isAbsolute2(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
|
|
9047
|
+
if (control.remotePluginId !== undefined)
|
|
9048
|
+
requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
|
|
9047
9049
|
if (control.appSha256 !== undefined)
|
|
9048
9050
|
requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
|
|
9049
9051
|
}
|
|
@@ -9066,8 +9068,8 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9066
9068
|
text(source.path);
|
|
9067
9069
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
9068
9070
|
if (source.hashMode !== undefined)
|
|
9069
|
-
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));
|
|
9070
|
-
if (source.hashMode === "codex-settings-v1")
|
|
9071
|
+
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));
|
|
9072
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2")
|
|
9071
9073
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "config.toml");
|
|
9072
9074
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
9073
9075
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "settings.json");
|
|
@@ -9103,10 +9105,10 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9103
9105
|
}
|
|
9104
9106
|
|
|
9105
9107
|
// src/lib/agent-discovery.ts
|
|
9106
|
-
import { createHash as
|
|
9108
|
+
import { createHash as createHash18 } from "crypto";
|
|
9107
9109
|
import { lstatSync as lstatSync21, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync7 } from "fs";
|
|
9108
9110
|
import { basename as basename9, dirname as dirname17, isAbsolute as isAbsolute16, join as join27, resolve as resolve15, sep as sep6 } from "path";
|
|
9109
|
-
import { isDeepStrictEqual as
|
|
9111
|
+
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
9110
9112
|
|
|
9111
9113
|
// node_modules/yaml/dist/index.js
|
|
9112
9114
|
var composer = require_composer();
|
|
@@ -10877,7 +10879,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
10877
10879
|
// package.json
|
|
10878
10880
|
var package_default = {
|
|
10879
10881
|
name: "@hasna/skills",
|
|
10880
|
-
version: "0.10.
|
|
10882
|
+
version: "0.10.22",
|
|
10881
10883
|
description: "Skills library for AI coding agents",
|
|
10882
10884
|
type: "module",
|
|
10883
10885
|
bin: {
|
|
@@ -13027,6 +13029,25 @@ function hashNativeJsonControls(text2, excludedMetadata) {
|
|
|
13027
13029
|
import { basename as basename5, dirname as dirname10, join as join14, relative as relative4, resolve as resolve9, sep as sep4 } from "path";
|
|
13028
13030
|
import { lstatSync as lstatSync12, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "fs";
|
|
13029
13031
|
|
|
13032
|
+
// src/lib/codex-native-compatibility.ts
|
|
13033
|
+
var MEASURED_CODEX_CAPABILITIES = {
|
|
13034
|
+
"codex-cli 0.153.0": ["hooks"],
|
|
13035
|
+
"codex-cli 0.154.0": ["hooks"],
|
|
13036
|
+
"codex-cli 0.155.0": ["hooks"],
|
|
13037
|
+
"codex-cli 0.155.1": ["hooks"],
|
|
13038
|
+
"codex-cli 0.156.1": ["hooks"],
|
|
13039
|
+
"codex-cli 0.157.0": ["hooks"],
|
|
13040
|
+
"codex-cli 0.157.1": ["hooks"],
|
|
13041
|
+
"codex-cli 0.158.0": ["hooks"],
|
|
13042
|
+
"codex-cli 0.159.0": ["hooks"],
|
|
13043
|
+
"codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
|
|
13044
|
+
"codex-cli 0.160.0": ["hooks", "qualified-skill-catalog"]
|
|
13045
|
+
};
|
|
13046
|
+
function supportsCodexNativeCapability(version, capability) {
|
|
13047
|
+
return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
|
|
13048
|
+
}
|
|
13049
|
+
var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
|
|
13050
|
+
|
|
13030
13051
|
// src/lib/codex-native-skill-catalog.ts
|
|
13031
13052
|
import { realpathSync as realpathSync4 } from "fs";
|
|
13032
13053
|
import { isAbsolute as isAbsolute10, normalize as normalize2 } from "path";
|
|
@@ -13036,7 +13057,6 @@ import { execFile, spawn } from "child_process";
|
|
|
13036
13057
|
import { realpathSync as realpathSync3, statSync as statSync7 } from "fs";
|
|
13037
13058
|
import { isAbsolute as isAbsolute9 } from "path";
|
|
13038
13059
|
import { promisify } from "util";
|
|
13039
|
-
var SUPPORTED_CODEX_HOOK_VERSIONS = ["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.155.0", "codex-cli 0.155.1", "codex-cli 0.156.1", "codex-cli 0.157.0", "codex-cli 0.157.1", "codex-cli 0.158.0", "codex-cli 0.159.0", "codex-cli 0.159.2"];
|
|
13040
13060
|
async function connectCodexHookRpc(options) {
|
|
13041
13061
|
const timeoutMs = options.timeoutMs ?? 20000;
|
|
13042
13062
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 20000)
|
|
@@ -13054,7 +13074,7 @@ async function connectCodexHookRpc(options) {
|
|
|
13054
13074
|
} catch {
|
|
13055
13075
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE");
|
|
13056
13076
|
}
|
|
13057
|
-
if (!
|
|
13077
|
+
if (!supportsCodexNativeCapability(version, "hooks"))
|
|
13058
13078
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED_VERSION");
|
|
13059
13079
|
const current = statSync7(binary);
|
|
13060
13080
|
if (realpathSync3(found) !== binary || ["dev", "ino", "size", "mtimeMs", "ctimeMs"].some((k) => stat[k] !== current[k]))
|
|
@@ -13179,12 +13199,12 @@ var MAX_SKILLS = 4096;
|
|
|
13179
13199
|
var MAX_NAME_BYTES = 1024;
|
|
13180
13200
|
var MAX_PATH_BYTES = 16384;
|
|
13181
13201
|
var MAX_PLUGIN_ID_BYTES = 1024;
|
|
13182
|
-
var SUPPORTED_NAME_CONTROL_VERSIONS = new Set(["codex-cli 0.159.2"]);
|
|
13183
13202
|
function refuse() {
|
|
13184
13203
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_INVALID");
|
|
13185
13204
|
}
|
|
13186
13205
|
var object4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13187
13206
|
var scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value);
|
|
13207
|
+
var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
|
|
13188
13208
|
var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute10(value);
|
|
13189
13209
|
function projectCodexNativeSkillCatalog(response, cwd) {
|
|
13190
13210
|
if (!absolutePath2(cwd) || !object4(response) || !Array.isArray(response.data) || response.data.length !== 1)
|
|
@@ -13212,7 +13232,11 @@ function projectCodexInstalledPlugins(response) {
|
|
|
13212
13232
|
for (const value of marketplace.plugins) {
|
|
13213
13233
|
if (!object4(value) || !scalar(value.id, MAX_PLUGIN_ID_BYTES) || !scalar(value.name, MAX_NAME_BYTES) || value.id !== `${value.name}@${marketplace.name}`)
|
|
13214
13234
|
refuse();
|
|
13215
|
-
plugins.push({
|
|
13235
|
+
plugins.push({
|
|
13236
|
+
...value,
|
|
13237
|
+
marketplace: marketplace.name,
|
|
13238
|
+
sourceType: object4(value.source) && value.source.type === "remote" ? "remote" : undefined
|
|
13239
|
+
});
|
|
13216
13240
|
}
|
|
13217
13241
|
}
|
|
13218
13242
|
return projectCodexInstalledPluginEntries(plugins);
|
|
@@ -13222,13 +13246,21 @@ function projectCodexInstalledPluginEntries(value) {
|
|
|
13222
13246
|
refuse();
|
|
13223
13247
|
const ids = new Set;
|
|
13224
13248
|
return value.map((item) => {
|
|
13225
|
-
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))
|
|
13249
|
+
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")
|
|
13226
13250
|
refuse();
|
|
13227
13251
|
const at = item.id.lastIndexOf("@");
|
|
13228
13252
|
if (at < 1 || item.id.slice(0, at) !== item.name || at === item.id.length - 1 || ids.has(item.id))
|
|
13229
13253
|
refuse();
|
|
13230
13254
|
ids.add(item.id);
|
|
13231
|
-
return {
|
|
13255
|
+
return {
|
|
13256
|
+
id: item.id,
|
|
13257
|
+
name: item.name,
|
|
13258
|
+
installed: item.installed,
|
|
13259
|
+
enabled: item.enabled,
|
|
13260
|
+
localVersion: item.localVersion ?? null,
|
|
13261
|
+
...item.sourceType === "remote" ? { sourceType: "remote" } : {},
|
|
13262
|
+
...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
|
|
13263
|
+
};
|
|
13232
13264
|
});
|
|
13233
13265
|
}
|
|
13234
13266
|
async function captureCodexNativeSkillCatalog(options, connect = connectCodexHookRpc) {
|
|
@@ -13241,7 +13273,7 @@ async function captureCodexNativeSkillCatalog(options, connect = connectCodexHoo
|
|
|
13241
13273
|
...options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }
|
|
13242
13274
|
});
|
|
13243
13275
|
try {
|
|
13244
|
-
if (!
|
|
13276
|
+
if (!supportsCodexNativeCapability(rpc.version, "qualified-skill-catalog"))
|
|
13245
13277
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_UNSUPPORTED_VERSION");
|
|
13246
13278
|
const response = await rpc.request("skills/list", { cwds: [options.cwd], forceReload: true });
|
|
13247
13279
|
const installed = await rpc.request("plugin/installed", { cwds: [options.cwd] });
|
|
@@ -13299,6 +13331,21 @@ var CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
|
|
|
13299
13331
|
function refuse2() {
|
|
13300
13332
|
throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
|
|
13301
13333
|
}
|
|
13334
|
+
function remoteInstallationMatches(parent, remotePluginId, read2) {
|
|
13335
|
+
try {
|
|
13336
|
+
if (!remotePluginIdentifier(remotePluginId) || !lstatSync12(parent).isDirectory() || realpathSync5(parent) !== parent)
|
|
13337
|
+
return false;
|
|
13338
|
+
const path = join14(parent, ".codex-remote-plugin-install.json"), stat = lstatSync12(path, { throwIfNoEntry: false });
|
|
13339
|
+
if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
|
|
13340
|
+
return false;
|
|
13341
|
+
const text2 = read2(path);
|
|
13342
|
+
hashNativeJsonControls(text2);
|
|
13343
|
+
const value = JSON.parse(text2);
|
|
13344
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 2 && value.schema_version === 1 && value.remote_plugin_id === remotePluginId);
|
|
13345
|
+
} catch {
|
|
13346
|
+
return false;
|
|
13347
|
+
}
|
|
13348
|
+
}
|
|
13302
13349
|
function appControls(path, read2) {
|
|
13303
13350
|
const text2 = read2(path);
|
|
13304
13351
|
let value, sha2562;
|
|
@@ -13388,6 +13435,10 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
|
|
|
13388
13435
|
for (const parent of new Set(controls.map((control) => control.pluginParent))) {
|
|
13389
13436
|
if (!parent.startsWith(cache + sep4) || !lstatSync12(parent, { throwIfNoEntry: false })?.isDirectory())
|
|
13390
13437
|
return false;
|
|
13438
|
+
for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
|
|
13439
|
+
if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read2))
|
|
13440
|
+
return false;
|
|
13441
|
+
}
|
|
13391
13442
|
const names = readdirSync6(parent);
|
|
13392
13443
|
entries += names.length;
|
|
13393
13444
|
if (entries > 4096)
|
|
@@ -13414,7 +13465,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
|
|
|
13414
13465
|
}
|
|
13415
13466
|
}
|
|
13416
13467
|
function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, rules) {
|
|
13417
|
-
if (catalog?.version
|
|
13468
|
+
if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
|
|
13418
13469
|
refuse2();
|
|
13419
13470
|
const skills = projectCodexNativeSkillCatalog({ data: [{ cwd, errors: [], skills: catalog.skills }] }, cwd);
|
|
13420
13471
|
const allowed = new Set(documents), result = [];
|
|
@@ -13441,20 +13492,28 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, r
|
|
|
13441
13492
|
const listed = skills.filter((skill) => skill.path === document2);
|
|
13442
13493
|
if (listed.length > 1)
|
|
13443
13494
|
refuse2();
|
|
13495
|
+
let remotePluginId;
|
|
13444
13496
|
if (listed.length === 1) {
|
|
13445
13497
|
if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
|
|
13446
13498
|
refuse2();
|
|
13447
13499
|
} else {
|
|
13448
|
-
const installed = installedPlugins.filter((
|
|
13449
|
-
if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled
|
|
13500
|
+
const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
|
|
13501
|
+
if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
|
|
13450
13502
|
refuse2();
|
|
13451
|
-
|
|
13452
|
-
|
|
13503
|
+
const plugin = installed[0];
|
|
13504
|
+
if (plugin.localVersion !== null) {
|
|
13505
|
+
if (plugin.localVersion !== basename5(dirname10(dirname10(dirname10(document2)))))
|
|
13506
|
+
continue;
|
|
13507
|
+
} else {
|
|
13508
|
+
if (plugin.sourceType !== "remote" || !plugin.remotePluginId || !remoteInstallationMatches(parsed.pluginParent, plugin.remotePluginId, read2) || !lstatSync12(document2).isFile() || realpathSync5(document2) !== document2)
|
|
13509
|
+
refuse2();
|
|
13510
|
+
remotePluginId = plugin.remotePluginId;
|
|
13511
|
+
}
|
|
13453
13512
|
const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
|
|
13454
13513
|
if (!exactPathDeny || !isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true }, rules))
|
|
13455
13514
|
refuse2();
|
|
13456
13515
|
}
|
|
13457
|
-
result.push({ ...parsed, pluginId: expectedPluginId });
|
|
13516
|
+
result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
|
|
13458
13517
|
}
|
|
13459
13518
|
if (!result.length || new Set(result.map((item) => item.name)).size !== result.length)
|
|
13460
13519
|
refuse2();
|
|
@@ -13470,7 +13529,7 @@ function isReviewedCodexPluginSkillDisabled(document2, cache, controls, rules, r
|
|
|
13470
13529
|
return false;
|
|
13471
13530
|
}
|
|
13472
13531
|
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);
|
|
13473
|
-
if (!reviewed)
|
|
13532
|
+
if (!reviewed || reviewed.remotePluginId !== undefined && !remoteInstallationMatches(reviewed.pluginParent, reviewed.remotePluginId, read2))
|
|
13474
13533
|
return false;
|
|
13475
13534
|
return isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: reviewed.pluginId, enabled: true }, rules);
|
|
13476
13535
|
}
|
|
@@ -13489,7 +13548,7 @@ enabled = false
|
|
|
13489
13548
|
return text2;
|
|
13490
13549
|
}
|
|
13491
13550
|
|
|
13492
|
-
// src/lib/codex-settings-witness.ts
|
|
13551
|
+
// src/lib/codex-settings-witness-v1.ts
|
|
13493
13552
|
import { createHash as createHash9 } from "crypto";
|
|
13494
13553
|
var sha = (text2) => createHash9("sha256").update(text2).digest("hex");
|
|
13495
13554
|
var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
@@ -13583,19 +13642,167 @@ function captureCodexSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
|
13583
13642
|
const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
13584
13643
|
return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
|
|
13585
13644
|
}
|
|
13645
|
+
|
|
13646
|
+
// src/lib/codex-settings-witness.ts
|
|
13647
|
+
import { createHash as createHash10 } from "crypto";
|
|
13648
|
+
var sha2 = (text2) => createHash10("sha256").update(text2).digest("hex");
|
|
13649
|
+
var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
13650
|
+
var VERBOSITY2 = new Set(["low", "medium", "high"]);
|
|
13651
|
+
var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
13652
|
+
var CODEX_DISCOVERY_PROJECTION_FIELDS = ["plugins", "marketplaces", "skills"];
|
|
13653
|
+
var INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
|
|
13654
|
+
function need4(value) {
|
|
13655
|
+
if (!value)
|
|
13656
|
+
throw new Error("Invalid Codex settings witness");
|
|
13657
|
+
}
|
|
13658
|
+
function isRecord3(value) {
|
|
13659
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
13660
|
+
}
|
|
13661
|
+
function isDate(value) {
|
|
13662
|
+
return value instanceof Date;
|
|
13663
|
+
}
|
|
13664
|
+
function numericTokens2(text2) {
|
|
13665
|
+
const result = [];
|
|
13666
|
+
for (let at = 0;at < text2.length; ) {
|
|
13667
|
+
const char = text2[at];
|
|
13668
|
+
if (char === "#") {
|
|
13669
|
+
while (at < text2.length && text2[at] !== `
|
|
13670
|
+
`)
|
|
13671
|
+
at++;
|
|
13672
|
+
continue;
|
|
13673
|
+
}
|
|
13674
|
+
if (char === '"' || char === "'") {
|
|
13675
|
+
const quote = char, triple = text2.slice(at, at + 3) === quote.repeat(3);
|
|
13676
|
+
at += triple ? 3 : 1;
|
|
13677
|
+
while (at < text2.length) {
|
|
13678
|
+
if (quote === '"' && text2[at] === "\\") {
|
|
13679
|
+
at += 2;
|
|
13680
|
+
continue;
|
|
13681
|
+
}
|
|
13682
|
+
if (text2.slice(at, at + (triple ? 3 : 1)) === quote.repeat(triple ? 3 : 1)) {
|
|
13683
|
+
at += triple ? 3 : 1;
|
|
13684
|
+
break;
|
|
13685
|
+
}
|
|
13686
|
+
at++;
|
|
13687
|
+
}
|
|
13688
|
+
continue;
|
|
13689
|
+
}
|
|
13690
|
+
if (/[0-9+-]/.test(char)) {
|
|
13691
|
+
const start = at;
|
|
13692
|
+
while (at < text2.length && !/[\s,\]{}#=]/.test(text2[at]))
|
|
13693
|
+
at++;
|
|
13694
|
+
if (at > start) {
|
|
13695
|
+
result.push(text2.slice(start, at));
|
|
13696
|
+
continue;
|
|
13697
|
+
}
|
|
13698
|
+
}
|
|
13699
|
+
at++;
|
|
13700
|
+
}
|
|
13701
|
+
return result.sort();
|
|
13702
|
+
}
|
|
13703
|
+
function canonical4(value, depth = 0, budget = { remaining: 65536 }) {
|
|
13704
|
+
need4(depth <= 32 && --budget.remaining >= 0);
|
|
13705
|
+
if (value === null)
|
|
13706
|
+
return ["null"];
|
|
13707
|
+
if (typeof value === "string")
|
|
13708
|
+
return ["string", value];
|
|
13709
|
+
if (typeof value === "boolean")
|
|
13710
|
+
return ["boolean", value];
|
|
13711
|
+
if (typeof value === "number") {
|
|
13712
|
+
need4(Number.isFinite(value));
|
|
13713
|
+
return ["number", String(value)];
|
|
13714
|
+
}
|
|
13715
|
+
if (isDate(value)) {
|
|
13716
|
+
need4(Number.isFinite(value.getTime()));
|
|
13717
|
+
return ["date", value.toISOString()];
|
|
13718
|
+
}
|
|
13719
|
+
if (Array.isArray(value))
|
|
13720
|
+
return ["array", value.map((child) => canonical4(child, depth + 1, budget))];
|
|
13721
|
+
need4(isRecord3(value));
|
|
13722
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget)])];
|
|
13723
|
+
}
|
|
13724
|
+
function normalize3(config) {
|
|
13725
|
+
if (isRecord3(config.hooks)) {
|
|
13726
|
+
delete config.hooks.state;
|
|
13727
|
+
if (!Object.keys(config.hooks).length)
|
|
13728
|
+
delete config.hooks;
|
|
13729
|
+
}
|
|
13730
|
+
if (isRecord3(config.skills) && Array.isArray(config.skills.config)) {
|
|
13731
|
+
const skills = config.skills;
|
|
13732
|
+
const retained = skills.config.filter((entry) => !disablesOneSkill(entry));
|
|
13733
|
+
if (retained.length !== skills.config.length) {
|
|
13734
|
+
if (retained.length)
|
|
13735
|
+
skills.config = retained;
|
|
13736
|
+
else
|
|
13737
|
+
delete skills.config;
|
|
13738
|
+
}
|
|
13739
|
+
if (!Object.keys(skills).length)
|
|
13740
|
+
delete config.skills;
|
|
13741
|
+
}
|
|
13742
|
+
inference(config);
|
|
13743
|
+
if (isRecord3(config.profiles)) {
|
|
13744
|
+
for (const profile of Object.values(config.profiles))
|
|
13745
|
+
if (isRecord3(profile))
|
|
13746
|
+
inference(profile);
|
|
13747
|
+
}
|
|
13748
|
+
}
|
|
13749
|
+
function disablesOneSkill(entry) {
|
|
13750
|
+
if (!isRecord3(entry) || entry.enabled !== false)
|
|
13751
|
+
return false;
|
|
13752
|
+
const keys = Object.keys(entry);
|
|
13753
|
+
if (!keys.every((key) => key === "path" || key === "name" || key === "enabled"))
|
|
13754
|
+
return false;
|
|
13755
|
+
const selector = ["path", "name"].filter((key) => Object.hasOwn(entry, key));
|
|
13756
|
+
return selector.length === 1 && typeof entry[selector[0]] === "string" && entry[selector[0]].length > 0;
|
|
13757
|
+
}
|
|
13758
|
+
function inference(target) {
|
|
13759
|
+
for (const key of INFERENCE)
|
|
13760
|
+
if (Object.hasOwn(target, key)) {
|
|
13761
|
+
const value = target[key];
|
|
13762
|
+
if (key === "model")
|
|
13763
|
+
need4(typeof value === "string" && MODEL.test(value));
|
|
13764
|
+
else
|
|
13765
|
+
need4(typeof value === "string" && (key === "model_reasoning_effort" ? EFFORT2.has(value) : VERBOSITY2.has(value)));
|
|
13766
|
+
delete target[key];
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13769
|
+
function settingsDigest2(text2) {
|
|
13770
|
+
let config;
|
|
13771
|
+
try {
|
|
13772
|
+
config = Bun.TOML.parse(text2);
|
|
13773
|
+
} catch {
|
|
13774
|
+
throw new Error("Invalid Codex settings witness");
|
|
13775
|
+
}
|
|
13776
|
+
need4(isRecord3(config));
|
|
13777
|
+
normalize3(config);
|
|
13778
|
+
return sha2(`hasna.skills.codex-settings.v2\x00${JSON.stringify([canonical4(config), numericTokens2(text2)])}`);
|
|
13779
|
+
}
|
|
13780
|
+
function hashCodexSettingsReplacementV2(text2, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
13781
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
|
|
13782
|
+
budget.remaining -= Buffer.byteLength(text2);
|
|
13783
|
+
return settingsDigest2(text2);
|
|
13784
|
+
}
|
|
13785
|
+
function captureCodexSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
13786
|
+
const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
13787
|
+
return { path, hashMode: "codex-settings-v2", sha256: settingsDigest2(text2) };
|
|
13788
|
+
}
|
|
13789
|
+
function readCodexSettingsPreimage(path, budget = { remaining: 2 * 1024 * 1024 }) {
|
|
13790
|
+
return readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
13791
|
+
}
|
|
13586
13792
|
function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
|
|
13587
|
-
|
|
13793
|
+
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));
|
|
13588
13794
|
const budget = { remaining: 2 * 1024 * 1024 };
|
|
13589
13795
|
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
|
|
13590
|
-
|
|
13591
|
-
const sha2562 =
|
|
13592
|
-
if (
|
|
13796
|
+
need4((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
|
|
13797
|
+
const sha2562 = settingsDigest2(current);
|
|
13798
|
+
if (settingsDigest2(before) !== sha2562)
|
|
13593
13799
|
throw new Error("Codex non-preference settings changed; explicit discovery review required");
|
|
13594
|
-
return { path: previous.path, hashMode: "codex-settings-
|
|
13800
|
+
return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
|
|
13595
13801
|
}
|
|
13596
13802
|
|
|
13597
13803
|
// src/lib/agent-integration.ts
|
|
13598
|
-
import { createHash as
|
|
13804
|
+
import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
|
|
13805
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
13599
13806
|
import { existsSync as existsSync8, lstatSync as lstatSync15, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync12, 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 constants7, linkSync } from "fs";
|
|
13600
13807
|
import { dirname as dirname11, join as join17, resolve as resolve10, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
|
|
13601
13808
|
import { homedir as homedir2 } from "os";
|
|
@@ -13681,7 +13888,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
|
|
|
13681
13888
|
}
|
|
13682
13889
|
|
|
13683
13890
|
// src/lib/agent-bridge.ts
|
|
13684
|
-
import { createHash as
|
|
13891
|
+
import { createHash as createHash11 } from "crypto";
|
|
13685
13892
|
import { lstatSync as lstatSync14, readFileSync as readFileSync11, readdirSync as readdirSync7 } from "fs";
|
|
13686
13893
|
import { join as join16 } from "path";
|
|
13687
13894
|
var CLI_BRIDGE_NAME = "skills-cli";
|
|
@@ -13700,7 +13907,7 @@ Use the Skills CLI as the skill authority for this station.
|
|
|
13700
13907
|
|
|
13701
13908
|
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.
|
|
13702
13909
|
`;
|
|
13703
|
-
var CLI_BRIDGE_DIGEST =
|
|
13910
|
+
var CLI_BRIDGE_DIGEST = createHash11("sha256").update(markdown).digest("hex");
|
|
13704
13911
|
var CLI_BRIDGE_FILES = Object.freeze({
|
|
13705
13912
|
"SKILL.md": markdown,
|
|
13706
13913
|
".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
|
|
@@ -13992,7 +14199,7 @@ function disableCodexBundledSkills(text2) {
|
|
|
13992
14199
|
}
|
|
13993
14200
|
|
|
13994
14201
|
// src/lib/agent-integration.ts
|
|
13995
|
-
var
|
|
14202
|
+
var sha3 = (data) => createHash12("sha256").update(data).digest("hex");
|
|
13996
14203
|
var ROOTS = NATIVE_SKILL_ROOTS;
|
|
13997
14204
|
function assertSafePath(path) {
|
|
13998
14205
|
let cursor = resolve10(path);
|
|
@@ -14099,7 +14306,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
14099
14306
|
}
|
|
14100
14307
|
}
|
|
14101
14308
|
function treeHash(root) {
|
|
14102
|
-
const hash2 =
|
|
14309
|
+
const hash2 = createHash12("sha256");
|
|
14103
14310
|
let bytes = 0, files = 0;
|
|
14104
14311
|
function visit2(path) {
|
|
14105
14312
|
const stat = lstatSync15(path);
|
|
@@ -14484,7 +14691,7 @@ function planAgentIntegration(options) {
|
|
|
14484
14691
|
const observedNativeSources = [];
|
|
14485
14692
|
const codexPluginSkills = options.codexNativeCatalog ? reviewCodexPluginSkillControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join17(entry.path, "SKILL.md")), canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), resolve10(options.projectDir ?? home), (path) => {
|
|
14486
14693
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
14487
|
-
observedNativeSources.push({ path, sha256:
|
|
14694
|
+
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
14488
14695
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
14489
14696
|
}, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
|
|
14490
14697
|
const changes = [];
|
|
@@ -14512,7 +14719,7 @@ function planAgentIntegration(options) {
|
|
|
14512
14719
|
throw new Error("Refusing to overwrite an unrecognized Hermes supervisor");
|
|
14513
14720
|
if (supervisorBefore !== supervisorAfter)
|
|
14514
14721
|
changes.push({ path: supervisorPath, before: supervisorBefore, after: supervisorAfter });
|
|
14515
|
-
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256:
|
|
14722
|
+
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256: sha3(supervisorAfter) };
|
|
14516
14723
|
const after2 = configureHermesHooks(before, supervisor, previous);
|
|
14517
14724
|
if (before !== after2)
|
|
14518
14725
|
changes.push({ path, before, after: after2 });
|
|
@@ -14559,12 +14766,12 @@ function planAgentIntegration(options) {
|
|
|
14559
14766
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
14560
14767
|
home: resolve10(home),
|
|
14561
14768
|
includeVendor: true,
|
|
14562
|
-
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join17(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256:
|
|
14769
|
+
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join17(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
|
|
14563
14770
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
14564
14771
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
14565
14772
|
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join17(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
14566
14773
|
rootAliases: aliases,
|
|
14567
|
-
...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256:
|
|
14774
|
+
...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(join17(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join17(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview } : {},
|
|
14568
14775
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
14569
14776
|
} };
|
|
14570
14777
|
const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
|
|
@@ -14580,12 +14787,12 @@ function planClaudePreToolUseHookUpdate(options) {
|
|
|
14580
14787
|
return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
|
|
14581
14788
|
}
|
|
14582
14789
|
function claudeSettingsConsumers(home, path, discoveries, aliases) {
|
|
14583
|
-
const
|
|
14790
|
+
const canonical5 = (value) => canonicalAgentPath(value, aliases);
|
|
14584
14791
|
const containsSettings = (root) => {
|
|
14585
|
-
const distance = relative5(
|
|
14792
|
+
const distance = relative5(canonical5(root), path);
|
|
14586
14793
|
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep5}`) && !isAbsolute11(distance);
|
|
14587
14794
|
};
|
|
14588
|
-
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" ||
|
|
14795
|
+
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical5(join17(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical5(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
|
|
14589
14796
|
}
|
|
14590
14797
|
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
14591
14798
|
try {
|
|
@@ -14617,7 +14824,7 @@ function planClaudeHookEventsUpdate(options) {
|
|
|
14617
14824
|
const path = canonicalAgentPath(join17(home, ".claude", "settings.json"), aliases);
|
|
14618
14825
|
assertSafePath(path);
|
|
14619
14826
|
const before = lstatSync15(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
|
|
14620
|
-
if ((before === null ? "absent" :
|
|
14827
|
+
if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
|
|
14621
14828
|
throw new Error("Claude settings preimage changed");
|
|
14622
14829
|
assertClaudeHookEventsReplacement(before, options.replacement, options.events);
|
|
14623
14830
|
const agents = bridge.agents;
|
|
@@ -14683,7 +14890,7 @@ function atomicWrite2(path, content) {
|
|
|
14683
14890
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
14684
14891
|
const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
14685
14892
|
const snapshot = readManagedSkillPolicySnapshot(dataDir2);
|
|
14686
|
-
if (!snapshot ||
|
|
14893
|
+
if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
|
|
14687
14894
|
throw new Error("Managed policy preimage changed");
|
|
14688
14895
|
const policy = snapshot.value, aliases = policy.bridge?.rootAliases ?? [];
|
|
14689
14896
|
recheckRootAliases(aliases);
|
|
@@ -14692,7 +14899,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
14692
14899
|
throw new Error("Settings witness upgrade requires an existing reviewed native binding");
|
|
14693
14900
|
const configPath = canonicalAgentPath(join17(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
|
|
14694
14901
|
const settings = readOptional(configPath);
|
|
14695
|
-
if (settings === null ||
|
|
14902
|
+
if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
|
|
14696
14903
|
throw new Error("Native settings preimage changed");
|
|
14697
14904
|
const sources = binding.sources.filter((source) => source.path === configPath && source.format === undefined && source.fields === undefined);
|
|
14698
14905
|
if (sources.length !== 1)
|
|
@@ -14700,15 +14907,40 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
14700
14907
|
const previous = sources[0];
|
|
14701
14908
|
if (typeof previous.sha256 !== "string")
|
|
14702
14909
|
throw new Error("Settings witness upgrade requires a preserved settings preimage");
|
|
14910
|
+
const replaced = [previous];
|
|
14911
|
+
if (options.agent === "codex") {
|
|
14912
|
+
if (previous.hashMode !== undefined && previous.hashMode !== "bytes" && previous.hashMode !== "codex-settings-v1")
|
|
14913
|
+
throw new Error("Codex settings witness upgrade requires a preserved raw or codex-settings-v1 configuration witness");
|
|
14914
|
+
const preimage = readCodexSettingsPreimage(options.reviewedPreimage);
|
|
14915
|
+
for (const source of binding.sources) {
|
|
14916
|
+
if (source === previous || source.path !== configPath)
|
|
14917
|
+
continue;
|
|
14918
|
+
const fields = source.fields;
|
|
14919
|
+
if (source.hashMode !== undefined || source.format !== "toml" || !Array.isArray(fields) || !isDeepStrictEqual3(fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]) || typeof source.sha256 !== "string")
|
|
14920
|
+
throw new Error("Codex settings witness upgrade found an unrecognized configuration witness");
|
|
14921
|
+
if (projectNativeDiscoveryFields(preimage, "toml", fields, configPath) !== source.sha256)
|
|
14922
|
+
throw new Error("Reviewed preimage does not explain the Codex discovery projection witness");
|
|
14923
|
+
replaced.push(source);
|
|
14924
|
+
}
|
|
14925
|
+
}
|
|
14703
14926
|
const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
14704
|
-
const
|
|
14927
|
+
const rebound = [];
|
|
14928
|
+
for (const source of binding.sources) {
|
|
14929
|
+
if (!replaced.includes(source)) {
|
|
14930
|
+
rebound.push(source);
|
|
14931
|
+
continue;
|
|
14932
|
+
}
|
|
14933
|
+
if (source === replaced[0])
|
|
14934
|
+
rebound.push(next);
|
|
14935
|
+
}
|
|
14936
|
+
const replacement = { ...binding, sources: rebound };
|
|
14705
14937
|
verifyAgentDiscovery(replacement);
|
|
14706
14938
|
const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
14707
|
-
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join17(dataDir2, "agent-policy.json"), before: snapshot.text, after }], nativeSkills: [], observedPolicy: { path: join17(dataDir2, "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 } };
|
|
14939
|
+
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join17(dataDir2, "agent-policy.json"), before: snapshot.text, after }], nativeSkills: [], observedPolicy: { path: join17(dataDir2, "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 })) } };
|
|
14708
14940
|
}
|
|
14709
14941
|
function applyAgentIntegration(plan) {
|
|
14710
14942
|
for (const source of plan.observedNativeSources ?? [])
|
|
14711
|
-
if (
|
|
14943
|
+
if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
|
|
14712
14944
|
throw new Error("Native identity source changed after planning");
|
|
14713
14945
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
14714
14946
|
throw new Error("Native settings changed after witness planning");
|
|
@@ -14728,7 +14960,7 @@ function applyAgentIntegration(plan) {
|
|
|
14728
14960
|
const upgrade = plan.settingsWitnessUpgrade;
|
|
14729
14961
|
if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
|
|
14730
14962
|
throw new Error("Invalid settings witness upgrade plan");
|
|
14731
|
-
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256:
|
|
14963
|
+
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
14732
14964
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
|
|
14733
14965
|
throw new Error("Settings witness upgrade plan changed");
|
|
14734
14966
|
provenDiscovery = verified.discoveryBefore[0];
|
|
@@ -14756,7 +14988,7 @@ function applyAgentIntegration(plan) {
|
|
|
14756
14988
|
chmodSync(backupRoot, 448);
|
|
14757
14989
|
for (const [index, change] of plan.changes.entries())
|
|
14758
14990
|
if (change.before !== null) {
|
|
14759
|
-
const backup = join17(backupRoot, `${index}-${
|
|
14991
|
+
const backup = join17(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
|
|
14760
14992
|
writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
|
|
14761
14993
|
if (readFileSync12(backup, "utf8") !== change.before)
|
|
14762
14994
|
throw new Error("Native configuration preservation readback failed");
|
|
@@ -14775,7 +15007,7 @@ function applyAgentIntegration(plan) {
|
|
|
14775
15007
|
}
|
|
14776
15008
|
recheckRootAliases(aliases);
|
|
14777
15009
|
for (const source of plan.observedNativeSources ?? [])
|
|
14778
|
-
if (
|
|
15010
|
+
if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
|
|
14779
15011
|
throw new Error("Native identity source changed during application");
|
|
14780
15012
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
14781
15013
|
throw new Error("Native settings changed during witness application");
|
|
@@ -14787,7 +15019,7 @@ function applyAgentIntegration(plan) {
|
|
|
14787
15019
|
}
|
|
14788
15020
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
14789
15021
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
14790
|
-
atomicWrite2(join17(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null :
|
|
15022
|
+
atomicWrite2(join17(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 } : {} }) + `
|
|
14791
15023
|
`);
|
|
14792
15024
|
} catch (error) {
|
|
14793
15025
|
for (const change of written.reverse()) {
|
|
@@ -14866,8 +15098,8 @@ function archiveNativeSkills(inventory, options) {
|
|
|
14866
15098
|
const operationId = randomUUID2(), archiveRoot = join17(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join17(archiveRoot, "receipt.json");
|
|
14867
15099
|
const entries = selected.map((entry, index) => ({
|
|
14868
15100
|
source: entry.vendor ? join17(entry.path, "SKILL.md") : entry.path,
|
|
14869
|
-
archive: join17(archiveRoot, `${index}-${
|
|
14870
|
-
hash: entry.vendor ?
|
|
15101
|
+
archive: join17(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
|
|
15102
|
+
hash: entry.vendor ? sha3(readNativeBytes(join17(entry.path, "SKILL.md"))) : entry.hash,
|
|
14871
15103
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
14872
15104
|
status: "planned"
|
|
14873
15105
|
}));
|
|
@@ -14878,7 +15110,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
14878
15110
|
const stat = lstatSync15(entry.archive);
|
|
14879
15111
|
if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
|
|
14880
15112
|
throw new Error("Archive type changed");
|
|
14881
|
-
if ((entry.discoveryOnly ?
|
|
15113
|
+
if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
14882
15114
|
throw new Error("Archive bytes changed");
|
|
14883
15115
|
};
|
|
14884
15116
|
assertSafePath(archiveRoot);
|
|
@@ -14998,7 +15230,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
14998
15230
|
throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
|
|
14999
15231
|
if (agent === "hermes") {
|
|
15000
15232
|
const supervisor = binding.supervisors?.hermes;
|
|
15001
|
-
if (supervisor?.path !== join17(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !==
|
|
15233
|
+
if (supervisor?.path !== join17(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
|
|
15002
15234
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
15003
15235
|
assertHermesProtection(home, config, command, profile, supervisor);
|
|
15004
15236
|
} else if (agent === "opencode") {
|
|
@@ -16296,7 +16528,7 @@ function exactProfileSelection(spec, profile) {
|
|
|
16296
16528
|
}
|
|
16297
16529
|
|
|
16298
16530
|
// src/lib/plugin-projection.ts
|
|
16299
|
-
import { createHash as
|
|
16531
|
+
import { createHash as createHash13 } from "crypto";
|
|
16300
16532
|
import { posix } from "path";
|
|
16301
16533
|
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
|
|
16302
16534
|
var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -16307,7 +16539,7 @@ function pluginNeed(value, message) {
|
|
|
16307
16539
|
if (!value)
|
|
16308
16540
|
pluginRefusal(message);
|
|
16309
16541
|
}
|
|
16310
|
-
var pluginHash = (bytes) =>
|
|
16542
|
+
var pluginHash = (bytes) => createHash13("sha256").update(bytes).digest("hex");
|
|
16311
16543
|
function pluginObject(value) {
|
|
16312
16544
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
16313
16545
|
}
|
|
@@ -16495,7 +16727,7 @@ function buildPluginProjection(entries) {
|
|
|
16495
16727
|
}
|
|
16496
16728
|
|
|
16497
16729
|
// src/lib/plugin-projection-store.ts
|
|
16498
|
-
import { createHash as
|
|
16730
|
+
import { createHash as createHash14, randomUUID as randomUUID3 } from "crypto";
|
|
16499
16731
|
import { constants as constants8, closeSync as closeSync9, fchmodSync, fstatSync as fstatSync9, fsyncSync as fsyncSync3, linkSync as linkSync2, lstatSync as lstatSync17, mkdirSync as mkdirSync8, openSync as openSync9, opendirSync as opendirSync4, readSync as readSync7, renameSync as renameSync4, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync4, writeSync as writeSync2 } from "fs";
|
|
16500
16732
|
import { dirname as dirname13, isAbsolute as isAbsolute12, join as join23, parse as parse5, resolve as resolve11 } from "path";
|
|
16501
16733
|
var READ_FLAGS = constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
|
|
@@ -16511,7 +16743,7 @@ function stat(path) {
|
|
|
16511
16743
|
return pluginRefusal("Cannot inspect plugin filesystem entry");
|
|
16512
16744
|
}
|
|
16513
16745
|
}
|
|
16514
|
-
function
|
|
16746
|
+
function canonical5(path) {
|
|
16515
16747
|
pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute12(path) && resolve11(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
|
|
16516
16748
|
}
|
|
16517
16749
|
function owner(stats, system = false) {
|
|
@@ -16522,7 +16754,7 @@ function same3(left, right, identityOnly = false) {
|
|
|
16522
16754
|
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");
|
|
16523
16755
|
}
|
|
16524
16756
|
function ancestors(path, create = false) {
|
|
16525
|
-
|
|
16757
|
+
canonical5(path);
|
|
16526
16758
|
const result = [], parent = dirname13(path), filesystemRoot = parse5(parent).root;
|
|
16527
16759
|
let current = filesystemRoot;
|
|
16528
16760
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
@@ -16837,7 +17069,7 @@ function pluginExecutableDigest(path) {
|
|
|
16837
17069
|
const opened = fstatSync9(fd, { bigint: true });
|
|
16838
17070
|
same3(before, opened);
|
|
16839
17071
|
fileShape(opened, maximum, "executable");
|
|
16840
|
-
const hash2 =
|
|
17072
|
+
const hash2 = createHash14("sha256"), buffer = new Uint8Array(64 * 1024);
|
|
16841
17073
|
let remaining = Number(opened.size);
|
|
16842
17074
|
while (remaining > 0) {
|
|
16843
17075
|
const count = readSync7(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
|
|
@@ -17396,23 +17628,23 @@ function captureManagedPluginRegistry(path, managedPlugins) {
|
|
|
17396
17628
|
}
|
|
17397
17629
|
|
|
17398
17630
|
// src/lib/claude-marketplace-registry.ts
|
|
17399
|
-
import { createHash as
|
|
17631
|
+
import { createHash as createHash15 } from "crypto";
|
|
17400
17632
|
import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, lstatSync as lstatSync19, openSync as openSync11, readSync as readSync9 } from "fs";
|
|
17401
17633
|
import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute15, resolve as resolve14 } from "path";
|
|
17402
17634
|
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
17403
|
-
function
|
|
17635
|
+
function need5(value, reason) {
|
|
17404
17636
|
if (!value)
|
|
17405
17637
|
throw new Error(`Claude marketplace registry ${reason}`);
|
|
17406
17638
|
}
|
|
17407
17639
|
function absolutePath3(value) {
|
|
17408
|
-
|
|
17640
|
+
need5(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve14(value) === value, "requires a normalized absolute path");
|
|
17409
17641
|
}
|
|
17410
17642
|
var 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;
|
|
17411
17643
|
function parents2(path) {
|
|
17412
17644
|
const result = [];
|
|
17413
17645
|
for (let at = dirname15(path);; at = dirname15(at)) {
|
|
17414
17646
|
const stat2 = lstatSync19(at, { bigint: true });
|
|
17415
|
-
|
|
17647
|
+
need5(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
17416
17648
|
result.push([at, stat2]);
|
|
17417
17649
|
if (at === dirname15(at))
|
|
17418
17650
|
return result;
|
|
@@ -17420,15 +17652,15 @@ function parents2(path) {
|
|
|
17420
17652
|
}
|
|
17421
17653
|
function readRegistry2(path, budget) {
|
|
17422
17654
|
absolutePath3(path);
|
|
17423
|
-
|
|
17424
|
-
|
|
17655
|
+
need5(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
|
|
17656
|
+
need5(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
|
|
17425
17657
|
const ancestors2 = parents2(path), initial = lstatSync19(path, { bigint: true });
|
|
17426
|
-
|
|
17427
|
-
|
|
17658
|
+
need5(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
|
|
17659
|
+
need5(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
|
|
17428
17660
|
const fd = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
|
|
17429
17661
|
try {
|
|
17430
17662
|
const opened = fstatSync11(fd, { bigint: true });
|
|
17431
|
-
|
|
17663
|
+
need5(opened.isFile() && sameFile2(initial, opened), "changed during open");
|
|
17432
17664
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
17433
17665
|
let length = 0;
|
|
17434
17666
|
while (length < bytes.length) {
|
|
@@ -17438,10 +17670,10 @@ function readRegistry2(path, budget) {
|
|
|
17438
17670
|
length += count;
|
|
17439
17671
|
}
|
|
17440
17672
|
const after = fstatSync11(fd, { bigint: true }), current = lstatSync19(path, { bigint: true });
|
|
17441
|
-
|
|
17673
|
+
need5(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
17442
17674
|
for (const [ancestor, before] of ancestors2) {
|
|
17443
17675
|
const now = lstatSync19(ancestor, { bigint: true });
|
|
17444
|
-
|
|
17676
|
+
need5(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
17445
17677
|
}
|
|
17446
17678
|
budget.remaining -= length;
|
|
17447
17679
|
try {
|
|
@@ -17461,7 +17693,7 @@ function parse6(text2) {
|
|
|
17461
17693
|
at++;
|
|
17462
17694
|
};
|
|
17463
17695
|
function string() {
|
|
17464
|
-
|
|
17696
|
+
need5(text2[at] === '"', "contains invalid JSON");
|
|
17465
17697
|
const start = at++;
|
|
17466
17698
|
while (at < text2.length) {
|
|
17467
17699
|
const character = text2[at++];
|
|
@@ -17472,7 +17704,7 @@ function parse6(text2) {
|
|
|
17472
17704
|
} catch {
|
|
17473
17705
|
throw new Error("Claude marketplace registry contains invalid JSON string");
|
|
17474
17706
|
}
|
|
17475
|
-
|
|
17707
|
+
need5(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
|
|
17476
17708
|
return value2;
|
|
17477
17709
|
}
|
|
17478
17710
|
if (character === "\\")
|
|
@@ -17481,7 +17713,7 @@ function parse6(text2) {
|
|
|
17481
17713
|
throw new Error("Claude marketplace registry contains an unterminated JSON string");
|
|
17482
17714
|
}
|
|
17483
17715
|
function value(depth) {
|
|
17484
|
-
|
|
17716
|
+
need5(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
17485
17717
|
space();
|
|
17486
17718
|
const character = text2[at];
|
|
17487
17719
|
if (character === '"')
|
|
@@ -17495,17 +17727,17 @@ function parse6(text2) {
|
|
|
17495
17727
|
if (object5) {
|
|
17496
17728
|
space();
|
|
17497
17729
|
const key = string();
|
|
17498
|
-
|
|
17730
|
+
need5(!keys.has(key), "contains a duplicate JSON key");
|
|
17499
17731
|
keys.add(key);
|
|
17500
17732
|
space();
|
|
17501
|
-
|
|
17733
|
+
need5(text2[at++] === ":", "contains invalid JSON");
|
|
17502
17734
|
entries.push([key, value(depth + 1)]);
|
|
17503
17735
|
} else
|
|
17504
17736
|
items.push(value(depth + 1));
|
|
17505
17737
|
space();
|
|
17506
17738
|
if (text2[at] === end)
|
|
17507
17739
|
break;
|
|
17508
|
-
|
|
17740
|
+
need5(text2[at++] === ",", "contains invalid JSON");
|
|
17509
17741
|
}
|
|
17510
17742
|
at++;
|
|
17511
17743
|
return object5 ? { kind: "object", entries } : { kind: "array", items };
|
|
@@ -17517,14 +17749,14 @@ function parse6(text2) {
|
|
|
17517
17749
|
}
|
|
17518
17750
|
numberPattern.lastIndex = at;
|
|
17519
17751
|
const number = numberPattern.exec(text2);
|
|
17520
|
-
|
|
17752
|
+
need5(number, "contains invalid JSON");
|
|
17521
17753
|
at += number[0].length;
|
|
17522
17754
|
return { kind: "number", value: number[0] };
|
|
17523
17755
|
}
|
|
17524
17756
|
const root2 = value(0);
|
|
17525
17757
|
space();
|
|
17526
|
-
|
|
17527
|
-
|
|
17758
|
+
need5(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
|
|
17759
|
+
need5(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
|
|
17528
17760
|
return root2;
|
|
17529
17761
|
}
|
|
17530
17762
|
function exactKeys(value, keys) {
|
|
@@ -17532,7 +17764,7 @@ function exactKeys(value, keys) {
|
|
|
17532
17764
|
}
|
|
17533
17765
|
var field = (object5, key) => object5.entries.find(([name]) => name === key)?.[1];
|
|
17534
17766
|
function textField(value) {
|
|
17535
|
-
|
|
17767
|
+
need5(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
|
|
17536
17768
|
return value.value;
|
|
17537
17769
|
}
|
|
17538
17770
|
function normalizeRow(row, version = 1) {
|
|
@@ -17540,7 +17772,7 @@ function normalizeRow(row, version = 1) {
|
|
|
17540
17772
|
if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
17541
17773
|
return;
|
|
17542
17774
|
const autoUpdate = field(row, "autoUpdate");
|
|
17543
|
-
|
|
17775
|
+
need5(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
17544
17776
|
}
|
|
17545
17777
|
const source = field(row, "source");
|
|
17546
17778
|
if (source?.kind !== "object")
|
|
@@ -17556,34 +17788,34 @@ function normalizeRow(row, version = 1) {
|
|
|
17556
17788
|
absolutePath3(sourceLocation);
|
|
17557
17789
|
absolutePath3(textField(field(row, "installLocation")));
|
|
17558
17790
|
const updated = textField(field(row, "lastUpdated"));
|
|
17559
|
-
|
|
17791
|
+
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");
|
|
17560
17792
|
const millis = Date.parse(updated);
|
|
17561
|
-
|
|
17793
|
+
need5(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
17562
17794
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
17563
17795
|
}
|
|
17564
17796
|
function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
17565
17797
|
const value = parse6(readRegistry2(path, budget));
|
|
17566
17798
|
for (const [, row] of value.entries)
|
|
17567
17799
|
normalizeRow(row);
|
|
17568
|
-
const sha2562 =
|
|
17800
|
+
const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
17569
17801
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
17570
17802
|
}
|
|
17571
17803
|
function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
17572
17804
|
const value = parse6(readRegistry2(path, budget));
|
|
17573
17805
|
for (const [, row] of value.entries)
|
|
17574
17806
|
normalizeRow(row, 2);
|
|
17575
|
-
const sha2562 =
|
|
17807
|
+
const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
17576
17808
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
17577
17809
|
}
|
|
17578
17810
|
|
|
17579
17811
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
17580
|
-
import { createHash as
|
|
17812
|
+
import { createHash as createHash17 } from "crypto";
|
|
17581
17813
|
|
|
17582
17814
|
// src/lib/codex-hook-trust-files.ts
|
|
17583
17815
|
import { closeSync as closeSync12, constants as constants11, fstatSync as fstatSync12, fsyncSync as fsyncSync4, lstatSync as lstatSync20, openSync as openSync12, readSync as readSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
17584
|
-
import { createHash as
|
|
17816
|
+
import { createHash as createHash16 } from "crypto";
|
|
17585
17817
|
import { dirname as dirname16 } from "path";
|
|
17586
|
-
function
|
|
17818
|
+
function need6(value, code) {
|
|
17587
17819
|
if (!value)
|
|
17588
17820
|
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
17589
17821
|
}
|
|
@@ -17591,8 +17823,8 @@ var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs
|
|
|
17591
17823
|
function safeParents(file) {
|
|
17592
17824
|
for (let p = dirname16(file);; p = dirname16(p)) {
|
|
17593
17825
|
const s = lstatSync20(p);
|
|
17594
|
-
|
|
17595
|
-
|
|
17826
|
+
need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
|
|
17827
|
+
need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
17596
17828
|
if (dirname16(p) === p)
|
|
17597
17829
|
break;
|
|
17598
17830
|
}
|
|
@@ -17601,12 +17833,12 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
17601
17833
|
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
17602
17834
|
safeParents(file);
|
|
17603
17835
|
const initial = lstatSync20(file, { bigint: true });
|
|
17604
|
-
|
|
17605
|
-
|
|
17836
|
+
need6(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
|
|
17837
|
+
need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
|
|
17606
17838
|
const fd = openSync12(file, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
|
|
17607
17839
|
try {
|
|
17608
17840
|
const before = fstatSync12(fd, { bigint: true });
|
|
17609
|
-
|
|
17841
|
+
need6(sameStat(initial, before), "INPUT_CHANGED");
|
|
17610
17842
|
const bytes = Buffer.alloc(Number(before.size) + 1);
|
|
17611
17843
|
let length = 0;
|
|
17612
17844
|
while (length < bytes.length) {
|
|
@@ -17615,16 +17847,16 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
17615
17847
|
break;
|
|
17616
17848
|
length += n2;
|
|
17617
17849
|
}
|
|
17618
|
-
|
|
17850
|
+
need6(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync20(file, { bigint: true })), "INPUT_CHANGED");
|
|
17619
17851
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
17620
|
-
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256:
|
|
17852
|
+
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
|
|
17621
17853
|
} finally {
|
|
17622
17854
|
closeSync12(fd);
|
|
17623
17855
|
}
|
|
17624
17856
|
}
|
|
17625
17857
|
function unchanged2(before) {
|
|
17626
17858
|
const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
17627
|
-
|
|
17859
|
+
need6(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
17628
17860
|
}
|
|
17629
17861
|
|
|
17630
17862
|
// src/lib/codex-hook-additions-review.ts
|
|
@@ -17633,11 +17865,12 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
|
17633
17865
|
var proofs = new WeakMap;
|
|
17634
17866
|
|
|
17635
17867
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
17636
|
-
var
|
|
17868
|
+
var sha4 = (text2) => createHash17("sha256").update(text2).digest("hex");
|
|
17869
|
+
var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
|
|
17637
17870
|
var proofs2 = new WeakMap;
|
|
17638
17871
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
17639
17872
|
const proof = proofs2.get(witness);
|
|
17640
|
-
|
|
17873
|
+
need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
17641
17874
|
unchanged2(proof.before);
|
|
17642
17875
|
unchanged2(proof.current);
|
|
17643
17876
|
proof.recheckAdditions();
|
|
@@ -17646,12 +17879,12 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
17646
17879
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
17647
17880
|
return false;
|
|
17648
17881
|
assertCodexHookDiscoveryRecovery(binding, witness);
|
|
17649
|
-
|
|
17882
|
+
need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
17650
17883
|
return true;
|
|
17651
17884
|
}
|
|
17652
17885
|
|
|
17653
17886
|
// src/lib/agent-discovery.ts
|
|
17654
|
-
var digest = (text2) =>
|
|
17887
|
+
var digest = (text2) => createHash18("sha256").update(text2).digest("hex");
|
|
17655
17888
|
function parseConfig(text2, path, toml = false) {
|
|
17656
17889
|
try {
|
|
17657
17890
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -17683,14 +17916,26 @@ function read2(path, changes) {
|
|
|
17683
17916
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
17684
17917
|
return readFileSync17(path, "utf8");
|
|
17685
17918
|
}
|
|
17919
|
+
function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
17920
|
+
const object5 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
|
|
17921
|
+
if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
|
|
17922
|
+
throw new Error("Expected native discovery configuration object");
|
|
17923
|
+
return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object5[field2] ?? null]))));
|
|
17924
|
+
}
|
|
17925
|
+
function isSupersededCodexProjection(agent, configPath, source) {
|
|
17926
|
+
return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual4(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
|
|
17927
|
+
}
|
|
17928
|
+
function supersedesCodexProjection(sources, configPath) {
|
|
17929
|
+
return sources.some((source) => source.path === configPath && source.hashMode === "codex-settings-v2");
|
|
17930
|
+
}
|
|
17686
17931
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
17687
|
-
if (source.hashMode === "codex-settings-v1") {
|
|
17932
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") {
|
|
17688
17933
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
17689
17934
|
throw new Error("Codex settings witnesses require exact typed metadata");
|
|
17690
|
-
const current = captureCodexSettings(source.path, budget).sha256;
|
|
17935
|
+
const current = (source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
|
|
17691
17936
|
if (current !== source.sha256)
|
|
17692
17937
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
17693
|
-
return changes?.has(source.path) ? hashCodexSettingsReplacement(changes.get(source.path), budget) : current;
|
|
17938
|
+
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
|
|
17694
17939
|
}
|
|
17695
17940
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
17696
17941
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
@@ -17729,10 +17974,7 @@ function projected(source, changes, budget = discoveryByteBudget()) {
|
|
|
17729
17974
|
return null;
|
|
17730
17975
|
if (!source.format)
|
|
17731
17976
|
return digest(text2);
|
|
17732
|
-
|
|
17733
|
-
if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
|
|
17734
|
-
throw new Error("Expected native discovery configuration object");
|
|
17735
|
-
return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field2) => [field2, object5[field2] ?? null]))));
|
|
17977
|
+
return projectNativeDiscoveryFields(text2, source.format, source.fields ?? [], source.path);
|
|
17736
17978
|
}
|
|
17737
17979
|
function verifyAgentDiscovery(binding, codexRecovery) {
|
|
17738
17980
|
if (codexRecovery)
|
|
@@ -17760,7 +18002,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
17760
18002
|
safe4(root2);
|
|
17761
18003
|
}
|
|
17762
18004
|
function assertMarketplaceBinding(binding, source) {
|
|
17763
|
-
if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
18005
|
+
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
17764
18006
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
17765
18007
|
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"))
|
|
17766
18008
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
@@ -17789,8 +18031,8 @@ function captureDiscoveryPathSources(paths) {
|
|
|
17789
18031
|
const budget = discoveryByteBudget();
|
|
17790
18032
|
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
|
|
17791
18033
|
}
|
|
17792
|
-
function assertProjectDiscovery(agent, directories, home,
|
|
17793
|
-
const homeConfigPath =
|
|
18034
|
+
function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15, reviewed) {
|
|
18035
|
+
const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
|
|
17794
18036
|
for (const directory of directories) {
|
|
17795
18037
|
const isHome = resolve15(directory) === resolve15(home);
|
|
17796
18038
|
if (agent === "hermes") {
|
|
@@ -17801,7 +18043,7 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15
|
|
|
17801
18043
|
continue;
|
|
17802
18044
|
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"];
|
|
17803
18045
|
for (const suffix of paths) {
|
|
17804
|
-
const path =
|
|
18046
|
+
const path = canonical6(join27(directory, suffix)), raw = read2(path);
|
|
17805
18047
|
if (raw === null)
|
|
17806
18048
|
continue;
|
|
17807
18049
|
const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
|
|
@@ -17817,14 +18059,14 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15
|
|
|
17817
18059
|
const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
|
|
17818
18060
|
if (exact && projected(exact) === exact.sha256 && managed.length) {
|
|
17819
18061
|
const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
|
|
17820
|
-
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 ===
|
|
18062
|
+
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))));
|
|
17821
18063
|
}
|
|
17822
18064
|
}
|
|
17823
18065
|
if (keys.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)))
|
|
17824
18066
|
throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
|
|
17825
18067
|
}
|
|
17826
18068
|
if (agent === "claude") {
|
|
17827
|
-
const commands =
|
|
18069
|
+
const commands = canonical6(join27(directory, ".claude/commands"));
|
|
17828
18070
|
safe4(commands);
|
|
17829
18071
|
if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
17830
18072
|
throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
|
|
@@ -17835,10 +18077,10 @@ function agentDiscoveryConfigPath(home, agent) {
|
|
|
17835
18077
|
return join27(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
17836
18078
|
}
|
|
17837
18079
|
function resolveAgentDiscovery(options) {
|
|
17838
|
-
const
|
|
18080
|
+
const canonical6 = options.canonical ?? resolve15, home = resolve15(options.home), agent = options.agent;
|
|
17839
18081
|
const sources = [], roots = new Set, builtinNames = [];
|
|
17840
|
-
const witness = (path, format,
|
|
17841
|
-
const source = { path:
|
|
18082
|
+
const witness = (path, format, fields2) => {
|
|
18083
|
+
const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
|
|
17842
18084
|
source.sha256 = projected(source);
|
|
17843
18085
|
sources.push(source);
|
|
17844
18086
|
return read2(source.path);
|
|
@@ -17846,7 +18088,7 @@ function resolveAgentDiscovery(options) {
|
|
|
17846
18088
|
if (agent === "hermes")
|
|
17847
18089
|
assertHermesEnvironment(home);
|
|
17848
18090
|
const configPath = agentDiscoveryConfigPath(home, agent);
|
|
17849
|
-
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [
|
|
18091
|
+
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"]);
|
|
17850
18092
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
17851
18093
|
const unresolved = (detail) => {
|
|
17852
18094
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
@@ -17905,11 +18147,12 @@ function resolveAgentDiscovery(options) {
|
|
|
17905
18147
|
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
17906
18148
|
unresolved("invalid retained review");
|
|
17907
18149
|
verifyAgentDiscovery(retained);
|
|
17908
|
-
if (!retained.sources.some((source) => source.path ===
|
|
18150
|
+
if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
|
|
17909
18151
|
unresolved("retained review is missing the full agent configuration source");
|
|
17910
|
-
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 !==
|
|
18152
|
+
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)))
|
|
17911
18153
|
unresolved("retained settings witness names another configuration source");
|
|
17912
|
-
|
|
18154
|
+
const covered = supersedesCodexProjection(retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
|
|
18155
|
+
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual4(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
17913
18156
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
17914
18157
|
return retained;
|
|
17915
18158
|
}
|
|
@@ -17923,14 +18166,15 @@ function resolveAgentDiscovery(options) {
|
|
|
17923
18166
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
17924
18167
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
17925
18168
|
verifyAgentDiscovery(supplied);
|
|
17926
|
-
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 !==
|
|
18169
|
+
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)))
|
|
17927
18170
|
throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
|
|
17928
|
-
if (!review.sources.some((source) => source.path ===
|
|
18171
|
+
if (!review.sources.some((source) => source.path === canonical6(configPath)))
|
|
17929
18172
|
throw new Error("Discovery review must include the agent configuration source");
|
|
17930
|
-
|
|
18173
|
+
const reviewedSources = supersedesCodexProjection(review.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
|
|
18174
|
+
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...reviewedSources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
|
|
17931
18175
|
}
|
|
17932
18176
|
function plugin(root2) {
|
|
17933
|
-
root2 =
|
|
18177
|
+
root2 = canonical6(root2);
|
|
17934
18178
|
safe4(root2);
|
|
17935
18179
|
const manifestPath = join27(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
17936
18180
|
const raw = witness(manifestPath);
|
|
@@ -17962,7 +18206,7 @@ function resolveAgentDiscovery(options) {
|
|
|
17962
18206
|
if (agent === "claude") {
|
|
17963
18207
|
const registrations = witness(join27(home, ".claude/plugins/installed_plugins.json"));
|
|
17964
18208
|
const installed = registrations === null ? {} : parseConfig(registrations, join27(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
17965
|
-
const commands =
|
|
18209
|
+
const commands = canonical6(join27(home, ".claude/commands"));
|
|
17966
18210
|
safe4(commands);
|
|
17967
18211
|
if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
17968
18212
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
@@ -18036,7 +18280,7 @@ function resolveAgentDiscovery(options) {
|
|
|
18036
18280
|
roots.add(join27(root2, "skills"));
|
|
18037
18281
|
}
|
|
18038
18282
|
}
|
|
18039
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join27(home, ".hermes/plugins"), join27(home, ".hermes/hermes-agent")].map((path) =>
|
|
18283
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join27(home, ".hermes/plugins"), join27(home, ".hermes/hermes-agent")].map((path) => canonical6(path))) } : {} };
|
|
18040
18284
|
}
|
|
18041
18285
|
|
|
18042
18286
|
// src/lib/authoring-path.ts
|
|
@@ -18228,7 +18472,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
18228
18472
|
return { name: skillName, path: skillPath, manifest, created: true };
|
|
18229
18473
|
}
|
|
18230
18474
|
function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
18231
|
-
const absoluteSource =
|
|
18475
|
+
const absoluteSource = normalize4(sourceDir);
|
|
18232
18476
|
if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
|
|
18233
18477
|
throw new Error(`Import directory not found: ${sourceDir}`);
|
|
18234
18478
|
}
|
|
@@ -18295,7 +18539,7 @@ function assertDisjointImportPaths(source, destination) {
|
|
|
18295
18539
|
}
|
|
18296
18540
|
}
|
|
18297
18541
|
function portPortableSkill(sourcePath, options = {}) {
|
|
18298
|
-
const absoluteSource =
|
|
18542
|
+
const absoluteSource = normalize4(sourcePath);
|
|
18299
18543
|
if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
|
|
18300
18544
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
18301
18545
|
}
|
|
@@ -18571,7 +18815,7 @@ function safeIsDirectory(path) {
|
|
|
18571
18815
|
function isSafeRelativePath2(value) {
|
|
18572
18816
|
if (!value.trim() || isAbsolute17(value))
|
|
18573
18817
|
return false;
|
|
18574
|
-
const normalized =
|
|
18818
|
+
const normalized = normalize4(value).replace(/\\/g, "/");
|
|
18575
18819
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
18576
18820
|
}
|
|
18577
18821
|
function add3(target, code, message) {
|
|
@@ -18885,7 +19129,7 @@ function getAllTags() {
|
|
|
18885
19129
|
return Array.from(tagSet).sort();
|
|
18886
19130
|
}
|
|
18887
19131
|
// src/lib/run-state.ts
|
|
18888
|
-
import { createHash as
|
|
19132
|
+
import { createHash as createHash19, randomBytes } from "crypto";
|
|
18889
19133
|
import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync19, readdirSync as readdirSync13, statSync as statSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
18890
19134
|
import { extname, join as join31, relative as relative7 } from "path";
|
|
18891
19135
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
@@ -19012,7 +19256,7 @@ function collectRunArtifacts(context) {
|
|
|
19012
19256
|
artifacts.push({
|
|
19013
19257
|
path: toProjectRelative(context.targetDir, path),
|
|
19014
19258
|
mime: mimeForPath(path),
|
|
19015
|
-
sha256:
|
|
19259
|
+
sha256: createHash19("sha256").update(bytes).digest("hex"),
|
|
19016
19260
|
sizeBytes: stat2.size
|
|
19017
19261
|
});
|
|
19018
19262
|
}
|
|
@@ -22370,15 +22614,15 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
|
22370
22614
|
} catch (_err) {}
|
|
22371
22615
|
}
|
|
22372
22616
|
const input = payload.value;
|
|
22373
|
-
const
|
|
22374
|
-
const isValidDate =
|
|
22617
|
+
const isDate2 = input instanceof Date;
|
|
22618
|
+
const isValidDate = isDate2 && !Number.isNaN(input.getTime());
|
|
22375
22619
|
if (isValidDate)
|
|
22376
22620
|
return payload;
|
|
22377
22621
|
payload.issues.push({
|
|
22378
22622
|
expected: "date",
|
|
22379
22623
|
code: "invalid_type",
|
|
22380
22624
|
input,
|
|
22381
|
-
...
|
|
22625
|
+
...isDate2 ? { received: "Invalid Date" } : {},
|
|
22382
22626
|
inst
|
|
22383
22627
|
});
|
|
22384
22628
|
return payload;
|
|
@@ -38431,7 +38675,7 @@ function clone2(value) {
|
|
|
38431
38675
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
38432
38676
|
}
|
|
38433
38677
|
// src/lib/canonical-json.ts
|
|
38434
|
-
import { createHash as
|
|
38678
|
+
import { createHash as createHash20 } from "crypto";
|
|
38435
38679
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
38436
38680
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
38437
38681
|
}
|
|
@@ -38475,7 +38719,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
38475
38719
|
return visit3(value, 0);
|
|
38476
38720
|
}
|
|
38477
38721
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
38478
|
-
return
|
|
38722
|
+
return createHash20("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
38479
38723
|
}
|
|
38480
38724
|
|
|
38481
38725
|
// src/lib/remote-recurring-contract.ts
|
|
@@ -39231,7 +39475,7 @@ function getApiUrl(action, env = process.env, options = {}) {
|
|
|
39231
39475
|
// src/lib/remote-run-contract.ts
|
|
39232
39476
|
var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
|
|
39233
39477
|
function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
39234
|
-
const record6 =
|
|
39478
|
+
const record6 = isRecord4(payload) ? payload : {};
|
|
39235
39479
|
return {
|
|
39236
39480
|
contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
|
|
39237
39481
|
...pickString(record6, "id"),
|
|
@@ -39253,7 +39497,7 @@ function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
|
39253
39497
|
...hasOwn(record6, "details") ? { details: record6.details } : {}
|
|
39254
39498
|
};
|
|
39255
39499
|
}
|
|
39256
|
-
function
|
|
39500
|
+
function isRecord4(value) {
|
|
39257
39501
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
39258
39502
|
}
|
|
39259
39503
|
function hasOwn(record6, key) {
|
|
@@ -39385,7 +39629,7 @@ function creditCheckoutFailure(value, status, key) {
|
|
|
39385
39629
|
|
|
39386
39630
|
// src/lib/remote-profile.ts
|
|
39387
39631
|
function customerNamePatch(input2, field2) {
|
|
39388
|
-
if (!
|
|
39632
|
+
if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
|
|
39389
39633
|
throw new Error("Provide only the requested name field.");
|
|
39390
39634
|
const value = input2[field2];
|
|
39391
39635
|
if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
|
|
@@ -39393,22 +39637,22 @@ function customerNamePatch(input2, field2) {
|
|
|
39393
39637
|
}
|
|
39394
39638
|
return { [field2]: value.trim() };
|
|
39395
39639
|
}
|
|
39396
|
-
function
|
|
39640
|
+
function isRecord5(value) {
|
|
39397
39641
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
39398
39642
|
}
|
|
39399
39643
|
function string4(value) {
|
|
39400
39644
|
return typeof value === "string" && value.length > 0;
|
|
39401
39645
|
}
|
|
39402
39646
|
function parseUpdatedProfile(value) {
|
|
39403
|
-
const user =
|
|
39404
|
-
if (!
|
|
39647
|
+
const user = isRecord5(value) && value.user;
|
|
39648
|
+
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)) {
|
|
39405
39649
|
throw new Error("The server returned an invalid account profile.");
|
|
39406
39650
|
}
|
|
39407
39651
|
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
39408
39652
|
}
|
|
39409
39653
|
function parseUpdatedWorkspace(value) {
|
|
39410
|
-
const organization2 =
|
|
39411
|
-
if (!
|
|
39654
|
+
const organization2 = isRecord5(value) && value.organization;
|
|
39655
|
+
if (!isRecord5(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
|
|
39412
39656
|
throw new Error("The server returned an invalid workspace.");
|
|
39413
39657
|
}
|
|
39414
39658
|
return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
|
|
@@ -40597,11 +40841,11 @@ function validateCronField(expr, min, max, label) {
|
|
|
40597
40841
|
return { valid: true };
|
|
40598
40842
|
}
|
|
40599
40843
|
function validateCron(expr) {
|
|
40600
|
-
const
|
|
40601
|
-
if (
|
|
40602
|
-
return { valid: false, error: `Expected 5 fields, got ${
|
|
40844
|
+
const fields2 = expr.trim().split(/\s+/);
|
|
40845
|
+
if (fields2.length !== 5) {
|
|
40846
|
+
return { valid: false, error: `Expected 5 fields, got ${fields2.length}. Format: "minute hour day-of-month month day-of-week"` };
|
|
40603
40847
|
}
|
|
40604
|
-
const [minuteF, hourF, domF, monthF, dowF] =
|
|
40848
|
+
const [minuteF, hourF, domF, monthF, dowF] = fields2;
|
|
40605
40849
|
const checks3 = [
|
|
40606
40850
|
{ expr: minuteF, min: 0, max: 59, label: "minute" },
|
|
40607
40851
|
{ expr: hourF, min: 0, max: 23, label: "hour" },
|
|
@@ -40742,7 +40986,7 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
40742
40986
|
saveSchedules(data, targetDir);
|
|
40743
40987
|
}
|
|
40744
40988
|
// src/lib/revision.ts
|
|
40745
|
-
import { createHash as
|
|
40989
|
+
import { createHash as createHash21 } from "crypto";
|
|
40746
40990
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
40747
40991
|
function revisionIdOf(content) {
|
|
40748
40992
|
const canonicalContent = {
|
|
@@ -40763,8 +41007,8 @@ function revisionIdOf(content) {
|
|
|
40763
41007
|
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
40764
41008
|
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
40765
41009
|
}
|
|
40766
|
-
const
|
|
40767
|
-
return
|
|
41010
|
+
const canonical6 = JSON.stringify(canonicalContent);
|
|
41011
|
+
return createHash21("sha256").update(canonical6).digest("hex");
|
|
40768
41012
|
}
|
|
40769
41013
|
function revisionIdOfRecord(record7) {
|
|
40770
41014
|
return revisionIdOf(record7);
|
|
@@ -41305,7 +41549,7 @@ async function requestInvitationEmail(origin, action, input2) {
|
|
|
41305
41549
|
}
|
|
41306
41550
|
|
|
41307
41551
|
// src/lib/remote-private-publications.ts
|
|
41308
|
-
import { createHash as
|
|
41552
|
+
import { createHash as createHash22 } from "crypto";
|
|
41309
41553
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
41310
41554
|
var failures2 = {
|
|
41311
41555
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -41352,7 +41596,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
|
41352
41596
|
var record8 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
41353
41597
|
var exact = (v, keys) => record8(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
|
|
41354
41598
|
var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
|
|
41355
|
-
var publicationSha256 = (bytes) =>
|
|
41599
|
+
var publicationSha256 = (bytes) => createHash22("sha256").update(bytes).digest("hex");
|
|
41356
41600
|
function checkedPublicationDeclaration(value) {
|
|
41357
41601
|
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)
|
|
41358
41602
|
return bad();
|
|
@@ -41651,7 +41895,7 @@ async function requestAuthApi(instance, path, options) {
|
|
|
41651
41895
|
const text3 = await res.text();
|
|
41652
41896
|
const body = text3 ? parseJsonBody(text3) : {};
|
|
41653
41897
|
if (!res.ok) {
|
|
41654
|
-
const record9 =
|
|
41898
|
+
const record9 = isRecord6(body) ? body : {};
|
|
41655
41899
|
const detail = typeof record9.detail === "string" ? record9.detail : undefined;
|
|
41656
41900
|
const error61 = typeof record9.error === "string" ? record9.error : undefined;
|
|
41657
41901
|
const code = typeof record9.code === "string" ? record9.code : undefined;
|
|
@@ -41679,7 +41923,7 @@ function condenseErrorBody(text3) {
|
|
|
41679
41923
|
return collapsed;
|
|
41680
41924
|
return `${collapsed.slice(0, MAX_ERROR_DETAIL_LENGTH - 1).trimEnd()}\u2026`;
|
|
41681
41925
|
}
|
|
41682
|
-
function
|
|
41926
|
+
function isRecord6(value) {
|
|
41683
41927
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
41684
41928
|
}
|
|
41685
41929
|
|
|
@@ -41836,7 +42080,7 @@ class RemoteSkillsAuthClient {
|
|
|
41836
42080
|
|
|
41837
42081
|
// src/lib/recurring-recovery.ts
|
|
41838
42082
|
var uuid12 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
41839
|
-
var
|
|
42083
|
+
var sha5 = exports_external.string().regex(/^[a-f0-9]{64}$/);
|
|
41840
42084
|
var common = exports_external.object({
|
|
41841
42085
|
contractVersion: exports_external.literal(1),
|
|
41842
42086
|
apiOrigin: exports_external.string().min(1).max(4096),
|
|
@@ -41849,7 +42093,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
|
|
|
41849
42093
|
common.extend({
|
|
41850
42094
|
operation: exports_external.literal("activate"),
|
|
41851
42095
|
draftId: uuid12,
|
|
41852
|
-
termsSha256:
|
|
42096
|
+
termsSha256: sha5,
|
|
41853
42097
|
approvalDeadline: exports_external.string().datetime(),
|
|
41854
42098
|
approval: recurringActivationSchema
|
|
41855
42099
|
}).strict(),
|
|
@@ -41857,7 +42101,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
|
|
|
41857
42101
|
]);
|
|
41858
42102
|
var stateSchema = exports_external.object({
|
|
41859
42103
|
contractVersion: exports_external.literal(1),
|
|
41860
|
-
intentSha256:
|
|
42104
|
+
intentSha256: sha5,
|
|
41861
42105
|
phase: exports_external.enum(["prepared", "pending", "observed"]),
|
|
41862
42106
|
consentId: uuid12.nullable()
|
|
41863
42107
|
}).strict();
|
|
@@ -43218,7 +43462,7 @@ function isApiMode(env = process.env) {
|
|
|
43218
43462
|
}
|
|
43219
43463
|
}
|
|
43220
43464
|
// src/lib/native-storage.ts
|
|
43221
|
-
import { createHash as
|
|
43465
|
+
import { createHash as createHash23, createHmac as createHmac2 } from "crypto";
|
|
43222
43466
|
import {
|
|
43223
43467
|
existsSync as existsSync19,
|
|
43224
43468
|
mkdirSync as mkdirSync15,
|
|
@@ -43227,7 +43471,7 @@ import {
|
|
|
43227
43471
|
statSync as statSync12,
|
|
43228
43472
|
writeFileSync as writeFileSync13
|
|
43229
43473
|
} from "fs";
|
|
43230
|
-
import { dirname as dirname23, join as join35, normalize as
|
|
43474
|
+
import { dirname as dirname23, join as join35, normalize as normalize5, relative as relative9, sep as sep9 } from "path";
|
|
43231
43475
|
var SKILLS_STORAGE_TABLES = [
|
|
43232
43476
|
"skills_sync_records",
|
|
43233
43477
|
"skills_sync_cursors"
|
|
@@ -43394,7 +43638,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
43394
43638
|
files.push({
|
|
43395
43639
|
path: relativePath,
|
|
43396
43640
|
sizeBytes: bytes.byteLength,
|
|
43397
|
-
sha256:
|
|
43641
|
+
sha256: createHash23("sha256").update(bytes).digest("hex"),
|
|
43398
43642
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
43399
43643
|
});
|
|
43400
43644
|
}
|
|
@@ -43419,7 +43663,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
|
|
|
43419
43663
|
continue;
|
|
43420
43664
|
}
|
|
43421
43665
|
const bytes = Buffer.from(file2.contentBase64, "base64");
|
|
43422
|
-
const hash4 =
|
|
43666
|
+
const hash4 = createHash23("sha256").update(bytes).digest("hex");
|
|
43423
43667
|
if (hash4 !== file2.sha256) {
|
|
43424
43668
|
throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
|
|
43425
43669
|
}
|
|
@@ -43731,7 +43975,7 @@ function walkFiles2(dir) {
|
|
|
43731
43975
|
return files;
|
|
43732
43976
|
}
|
|
43733
43977
|
function resolveSnapshotPath(targetDir, snapshotPath) {
|
|
43734
|
-
const normalizedPath =
|
|
43978
|
+
const normalizedPath = normalize5(snapshotPath);
|
|
43735
43979
|
if (normalizedPath.startsWith("..") || normalizedPath.includes(`${sep9}..${sep9}`) || normalizedPath.startsWith(sep9)) {
|
|
43736
43980
|
throw new Error(`Unsafe snapshot path: ${snapshotPath}`);
|
|
43737
43981
|
}
|
|
@@ -43751,7 +43995,7 @@ function toIsoString(value) {
|
|
|
43751
43995
|
return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
|
|
43752
43996
|
}
|
|
43753
43997
|
function sha256Hex2(value) {
|
|
43754
|
-
return
|
|
43998
|
+
return createHash23("sha256").update(value).digest("hex");
|
|
43755
43999
|
}
|
|
43756
44000
|
function normalizeHeaders(headers) {
|
|
43757
44001
|
const result = {};
|
|
@@ -43803,7 +44047,7 @@ function toArrayBuffer(bytes) {
|
|
|
43803
44047
|
return buffer;
|
|
43804
44048
|
}
|
|
43805
44049
|
// src/lib/station-snapshot.ts
|
|
43806
|
-
import { createHash as
|
|
44050
|
+
import { createHash as createHash24 } from "crypto";
|
|
43807
44051
|
import {
|
|
43808
44052
|
copyFileSync,
|
|
43809
44053
|
mkdirSync as mkdirSync16,
|
|
@@ -43978,7 +44222,7 @@ function validateStationId(stationId) {
|
|
|
43978
44222
|
}
|
|
43979
44223
|
}
|
|
43980
44224
|
function sha256File(filePath) {
|
|
43981
|
-
return
|
|
44225
|
+
return createHash24("sha256").update(readFileSync25(filePath)).digest("hex");
|
|
43982
44226
|
}
|
|
43983
44227
|
function scanHome(definition, homesRoot) {
|
|
43984
44228
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -44129,7 +44373,7 @@ function writeStationSnapshot(options) {
|
|
|
44129
44373
|
};
|
|
44130
44374
|
}
|
|
44131
44375
|
// src/lib/station-hydrate.ts
|
|
44132
|
-
import { createHash as
|
|
44376
|
+
import { createHash as createHash25 } from "crypto";
|
|
44133
44377
|
import {
|
|
44134
44378
|
copyFileSync as copyFileSync2,
|
|
44135
44379
|
mkdirSync as mkdirSync17,
|
|
@@ -44335,7 +44579,7 @@ function skillSha256(skill) {
|
|
|
44335
44579
|
return sha256File(skill.files[0].winner.fullPath);
|
|
44336
44580
|
}
|
|
44337
44581
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
44338
|
-
return
|
|
44582
|
+
return createHash25("sha256").update(joined.sort().join(`
|
|
44339
44583
|
`)).digest("hex");
|
|
44340
44584
|
}
|
|
44341
44585
|
function writeStationHydration(options) {
|
|
@@ -44663,7 +44907,7 @@ async function inspectLocked(client2, directory2, cancel) {
|
|
|
44663
44907
|
return privatePublicationResult(directory2, receipt);
|
|
44664
44908
|
}
|
|
44665
44909
|
// src/lib/skill-context.ts
|
|
44666
|
-
import { createHash as
|
|
44910
|
+
import { createHash as createHash26 } from "crypto";
|
|
44667
44911
|
import { resolve as resolve21 } from "path";
|
|
44668
44912
|
function words(value) {
|
|
44669
44913
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
@@ -44752,7 +44996,7 @@ ${content}`;
|
|
|
44752
44996
|
`);
|
|
44753
44997
|
const receipt = {
|
|
44754
44998
|
schemaVersion: 1,
|
|
44755
|
-
id:
|
|
44999
|
+
id: createHash26("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
44756
45000
|
profileId: profile.profileId,
|
|
44757
45001
|
profileRevision: profile.profileRevision,
|
|
44758
45002
|
authority: profile.authority,
|
|
@@ -44768,9 +45012,9 @@ ${content}`;
|
|
|
44768
45012
|
return { context: context2, selections, omitted, receipt };
|
|
44769
45013
|
}
|
|
44770
45014
|
// src/lib/session-reconciliation.ts
|
|
44771
|
-
import { createHash as
|
|
45015
|
+
import { createHash as createHash27 } from "crypto";
|
|
44772
45016
|
var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
|
|
44773
|
-
var digest3 = (value) =>
|
|
45017
|
+
var digest3 = (value) => createHash27("sha256").update(JSON.stringify(value)).digest("hex");
|
|
44774
45018
|
function exactTimestamp(value) {
|
|
44775
45019
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
44776
45020
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -45069,6 +45313,7 @@ export {
|
|
|
45069
45313
|
captureDiscoveryPathSources,
|
|
45070
45314
|
captureDiscoveryDirectories,
|
|
45071
45315
|
captureDiscoveryByteSources,
|
|
45316
|
+
captureCodexSettingsV2,
|
|
45072
45317
|
captureCodexSettings,
|
|
45073
45318
|
captureCodexNativeSkillCatalog,
|
|
45074
45319
|
captureClaudeSettingsV3,
|