@hasna/skills 0.10.51 → 0.10.52
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 +78 -0
- package/bin/index.js +1795 -1346
- package/bin/mcp.js +702 -457
- package/bin/migrate.js +1 -1
- package/bin/server.js +11 -4
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1023 -679
- package/dist/lib/agent-discovery.d.ts +37 -1
- package/dist/lib/agent-integration.d.ts +4 -1
- package/dist/lib/claude-marketplace-entry-witness.d.ts +26 -0
- package/dist/lib/claude-settings-witness.d.ts +25 -1
- package/dist/lib/codex-hook-trust-files.d.ts +17 -0
- package/dist/lib/selection-cache.d.ts +2 -2
- package/dist/lib/session-reconciliation.d.ts +9 -0
- package/dist/sdk/index.js +990 -649
- package/docs/plugin-admission.md +185 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5267,8 +5267,8 @@ var require_cst_visit = __commonJS((exports) => {
|
|
|
5267
5267
|
visit.REMOVE = REMOVE;
|
|
5268
5268
|
visit.itemAtPath = (cst, path) => {
|
|
5269
5269
|
let item = cst;
|
|
5270
|
-
for (const [
|
|
5271
|
-
const tok = item?.[
|
|
5270
|
+
for (const [field2, index] of path) {
|
|
5271
|
+
const tok = item?.[field2];
|
|
5272
5272
|
if (tok && "items" in tok) {
|
|
5273
5273
|
item = tok.items[index];
|
|
5274
5274
|
} else
|
|
@@ -5278,8 +5278,8 @@ var require_cst_visit = __commonJS((exports) => {
|
|
|
5278
5278
|
};
|
|
5279
5279
|
visit.parentCollection = (cst, path) => {
|
|
5280
5280
|
const parent = visit.itemAtPath(cst, path.slice(0, -1));
|
|
5281
|
-
const
|
|
5282
|
-
const coll = parent?.[
|
|
5281
|
+
const field2 = path[path.length - 1][0];
|
|
5282
|
+
const coll = parent?.[field2];
|
|
5283
5283
|
if (coll && "items" in coll)
|
|
5284
5284
|
return coll;
|
|
5285
5285
|
throw new Error("Parent collection not found");
|
|
@@ -5288,11 +5288,11 @@ var require_cst_visit = __commonJS((exports) => {
|
|
|
5288
5288
|
let ctrl = visitor(item, path);
|
|
5289
5289
|
if (typeof ctrl === "symbol")
|
|
5290
5290
|
return ctrl;
|
|
5291
|
-
for (const
|
|
5292
|
-
const token = item[
|
|
5291
|
+
for (const field2 of ["key", "value"]) {
|
|
5292
|
+
const token = item[field2];
|
|
5293
5293
|
if (token && "items" in token) {
|
|
5294
5294
|
for (let i = 0;i < token.items.length; ++i) {
|
|
5295
|
-
const ci = _visit(Object.freeze(path.concat([[
|
|
5295
|
+
const ci = _visit(Object.freeze(path.concat([[field2, i]])), token.items[i], visitor);
|
|
5296
5296
|
if (typeof ci === "number")
|
|
5297
5297
|
i = ci - 1;
|
|
5298
5298
|
else if (ci === BREAK)
|
|
@@ -5302,7 +5302,7 @@ var require_cst_visit = __commonJS((exports) => {
|
|
|
5302
5302
|
i -= 1;
|
|
5303
5303
|
}
|
|
5304
5304
|
}
|
|
5305
|
-
if (typeof ctrl === "function" &&
|
|
5305
|
+
if (typeof ctrl === "function" && field2 === "key")
|
|
5306
5306
|
ctrl = ctrl(item, path);
|
|
5307
5307
|
}
|
|
5308
5308
|
}
|
|
@@ -5728,9 +5728,9 @@ var require_lexer = __commonJS((exports) => {
|
|
|
5728
5728
|
}
|
|
5729
5729
|
}
|
|
5730
5730
|
*parseQuotedScalar() {
|
|
5731
|
-
const
|
|
5732
|
-
let end = this.buffer.indexOf(
|
|
5733
|
-
if (
|
|
5731
|
+
const quote2 = this.charAt(0);
|
|
5732
|
+
let end = this.buffer.indexOf(quote2, this.pos + 1);
|
|
5733
|
+
if (quote2 === "'") {
|
|
5734
5734
|
while (end !== -1 && this.buffer[end + 1] === "'")
|
|
5735
5735
|
end = this.buffer.indexOf("'", end + 2);
|
|
5736
5736
|
} else {
|
|
@@ -7262,7 +7262,7 @@ import {
|
|
|
7262
7262
|
statSync as statSync11,
|
|
7263
7263
|
writeFileSync as writeFileSync9
|
|
7264
7264
|
} from "fs";
|
|
7265
|
-
import { basename as
|
|
7265
|
+
import { basename as basename13, dirname as dirname22, isAbsolute as isAbsolute22, join as join34, normalize as normalize4, relative as relative7, resolve as resolve22, sep as sep10 } from "path";
|
|
7266
7266
|
|
|
7267
7267
|
// src/lib/registry-data/index.ts
|
|
7268
7268
|
var SKILLS = [];
|
|
@@ -8922,7 +8922,7 @@ var NATIVE_SKILL_ROOTS = [
|
|
|
8922
8922
|
// src/lib/authoring-path.ts
|
|
8923
8923
|
import { lstatSync as lstatSync26, realpathSync as realpathSync12 } from "fs";
|
|
8924
8924
|
import { homedir as homedir6 } from "os";
|
|
8925
|
-
import { basename as
|
|
8925
|
+
import { basename as basename12, dirname as dirname21, join as join33, resolve as resolve21, sep as sep9 } from "path";
|
|
8926
8926
|
|
|
8927
8927
|
// src/lib/agent-adapters.ts
|
|
8928
8928
|
var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
|
|
@@ -9126,6 +9126,10 @@ function parse(text) {
|
|
|
9126
9126
|
need(root.entries.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.rows, "exceeds its settings field limit");
|
|
9127
9127
|
return root;
|
|
9128
9128
|
}
|
|
9129
|
+
function parseNativeJsonObject(text) {
|
|
9130
|
+
need(typeof text === "string" && Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
|
|
9131
|
+
return parse(text);
|
|
9132
|
+
}
|
|
9129
9133
|
var BOOLEAN_PREFERENCES = new Set([
|
|
9130
9134
|
"autoScrollEnabled",
|
|
9131
9135
|
"axScreenReader",
|
|
@@ -9379,7 +9383,7 @@ function upgradeSumiSettingsWitness(previous, reviewedSettingsPath) {
|
|
|
9379
9383
|
}
|
|
9380
9384
|
|
|
9381
9385
|
// src/lib/agent-policy-limits.ts
|
|
9382
|
-
import { basename as
|
|
9386
|
+
import { basename as basename4, isAbsolute as isAbsolute4, join as join7, resolve as resolve4, sep as sep2 } from "path";
|
|
9383
9387
|
|
|
9384
9388
|
// src/lib/codex-native-compatibility.ts
|
|
9385
9389
|
var MEASURED_CODEX_CAPABILITIES = {
|
|
@@ -9401,6 +9405,199 @@ function supportsCodexNativeCapability(version, capability) {
|
|
|
9401
9405
|
}
|
|
9402
9406
|
var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
|
|
9403
9407
|
|
|
9408
|
+
// src/lib/claude-marketplace-entry-witness.ts
|
|
9409
|
+
import { createHash as createHash4 } from "crypto";
|
|
9410
|
+
import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute3, resolve as resolve3 } from "path";
|
|
9411
|
+
var CLAUDE_MARKETPLACE_ENTRY_HASH_MODE = "claude-marketplace-entry-v1";
|
|
9412
|
+
var DOMAIN = "hasna.skills.claude-marketplace-entry.v1\x00";
|
|
9413
|
+
var PATH_CHARACTERS = 4096;
|
|
9414
|
+
var NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
9415
|
+
function need2(value, reason) {
|
|
9416
|
+
if (!value)
|
|
9417
|
+
throw new Error(`Claude marketplace entry witness ${reason}`);
|
|
9418
|
+
}
|
|
9419
|
+
var bounded = (key) => key.length > 64 ? `${key.slice(0, 64)}...` : key;
|
|
9420
|
+
var quote = (text) => JSON.stringify(text).replace(/[^\x20-\x7e]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
9421
|
+
var child = (path, key) => path + (/^[A-Za-z0-9_$-]{1,64}$/.test(key) ? `.${key}` : `[${JSON.stringify(bounded(key))}]`);
|
|
9422
|
+
function unknownKey(selector, path, key) {
|
|
9423
|
+
throw new Error(path === null ? `${CLAUDE_MARKETPLACE_ENTRY_HASH_MODE}: unknown top-level key ${quote(bounded(key))} in ${selector.marketplace}` : `${CLAUDE_MARKETPLACE_ENTRY_HASH_MODE}: unknown key ${quote(child(path, key))} in ${selector.marketplace}`);
|
|
9424
|
+
}
|
|
9425
|
+
function knownKeys(object, allowed, selector, path) {
|
|
9426
|
+
for (const [key] of object.entries)
|
|
9427
|
+
if (!allowed.has(key))
|
|
9428
|
+
unknownKey(selector, path, key);
|
|
9429
|
+
}
|
|
9430
|
+
var reason = (error) => String(error?.message ?? error).replace(/^Claude settings witness /, "");
|
|
9431
|
+
var MARKETPLACE_KEYS = new Set(["$schema", "name", "owner", "plugins", "description", "version", "metadata", "forceRemoveDeletedPlugins", "allowCrossMarketplaceDependenciesOn", "renames"]);
|
|
9432
|
+
var MARKETPLACE_METADATA_KEYS = new Set(["description", "version", "pluginRoot"]);
|
|
9433
|
+
var ENTRY_BOUND_KEYS = new Set([
|
|
9434
|
+
"name",
|
|
9435
|
+
"source",
|
|
9436
|
+
"strict",
|
|
9437
|
+
"defaultEnabled",
|
|
9438
|
+
"dependencies",
|
|
9439
|
+
"relevance",
|
|
9440
|
+
"headers",
|
|
9441
|
+
"headersHelper",
|
|
9442
|
+
"settings",
|
|
9443
|
+
"userConfig",
|
|
9444
|
+
"types",
|
|
9445
|
+
"channels",
|
|
9446
|
+
"skills",
|
|
9447
|
+
"commands",
|
|
9448
|
+
"agents",
|
|
9449
|
+
"hooks",
|
|
9450
|
+
"mcpServers",
|
|
9451
|
+
"lspServers",
|
|
9452
|
+
"outputStyles",
|
|
9453
|
+
"workflows",
|
|
9454
|
+
"experimental",
|
|
9455
|
+
"themes",
|
|
9456
|
+
"monitors"
|
|
9457
|
+
]);
|
|
9458
|
+
var ENTRY_OMITTED_KEYS = new Set(["$schema", "description", "version", "author", "homepage", "repository", "license", "keywords", "category", "tags", "displayName", "metadata"]);
|
|
9459
|
+
var EXPERIMENTAL_KEYS = new Set(["themes", "monitors", "evals"]);
|
|
9460
|
+
var SOURCE_KEYS = Object.freeze({
|
|
9461
|
+
github: ["source", "repo", "ref", "sha"],
|
|
9462
|
+
url: ["source", "url", "ref", "sha", "path"],
|
|
9463
|
+
"git-subdir": ["source", "url", "path", "ref", "sha"],
|
|
9464
|
+
npm: ["source", "package", "version", "registry"],
|
|
9465
|
+
archive: ["source", "url", "sha256"],
|
|
9466
|
+
command: ["source", "command", "timeout", "mode"]
|
|
9467
|
+
});
|
|
9468
|
+
var field = (object, key) => object.entries.find(([name]) => name === key)?.[1];
|
|
9469
|
+
var isString = (value) => value?.kind === "string";
|
|
9470
|
+
var isBoolean = (value) => value?.kind === "literal" && (value.value === "true" || value.value === "false");
|
|
9471
|
+
var stringArray = (value) => value?.kind === "array" && value.items.every(isString);
|
|
9472
|
+
var stringObject = (value) => value?.kind === "object" && value.entries.every(([, item]) => isString(item));
|
|
9473
|
+
function claudeMarketplaceEntrySourceValid(source) {
|
|
9474
|
+
const path = source?.path;
|
|
9475
|
+
return typeof path === "string" && path.length > 0 && path.length <= PATH_CHARACTERS && !/[\x00-\x1f\x7f]/.test(path) && isAbsolute3(path) && resolve3(path) === path && basename3(path) === "marketplace.json" && basename3(dirname4(path)) === ".claude-plugin" && typeof source.marketplace === "string" && NAME.test(source.marketplace) && !source.marketplace.includes("..") && typeof source.plugin === "string" && NAME.test(source.plugin);
|
|
9476
|
+
}
|
|
9477
|
+
function canonical2(value) {
|
|
9478
|
+
if (value.kind === "string")
|
|
9479
|
+
return JSON.stringify(value.value);
|
|
9480
|
+
if (value.kind === "number" || value.kind === "literal")
|
|
9481
|
+
return value.value;
|
|
9482
|
+
if (value.kind === "array")
|
|
9483
|
+
return "[" + value.items.map(canonical2).join(",") + "]";
|
|
9484
|
+
return "{" + [...value.entries].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => JSON.stringify(key) + ":" + canonical2(item)).join(",") + "}";
|
|
9485
|
+
}
|
|
9486
|
+
function validateMarketplace(root, selector) {
|
|
9487
|
+
knownKeys(root, MARKETPLACE_KEYS, selector, null);
|
|
9488
|
+
const name = field(root, "name");
|
|
9489
|
+
need2(isString(name) && name.value === selector.marketplace, "marketplace name does not match the reviewed binding");
|
|
9490
|
+
for (const key of ["$schema", "description", "version"]) {
|
|
9491
|
+
const value = field(root, key);
|
|
9492
|
+
need2(value === undefined || isString(value), `has an invalid marketplace ${key}`);
|
|
9493
|
+
}
|
|
9494
|
+
const owner = field(root, "owner");
|
|
9495
|
+
need2(owner === undefined || owner.kind === "object", "has an invalid marketplace owner");
|
|
9496
|
+
const metadata = field(root, "metadata");
|
|
9497
|
+
if (metadata !== undefined) {
|
|
9498
|
+
need2(metadata.kind === "object", "has an invalid marketplace metadata object");
|
|
9499
|
+
knownKeys(metadata, MARKETPLACE_METADATA_KEYS, selector, "metadata");
|
|
9500
|
+
for (const [key, value] of metadata.entries)
|
|
9501
|
+
need2(isString(value), `has an invalid marketplace metadata.${key}`);
|
|
9502
|
+
}
|
|
9503
|
+
const remove = field(root, "forceRemoveDeletedPlugins");
|
|
9504
|
+
need2(remove === undefined || isBoolean(remove), "has an invalid forceRemoveDeletedPlugins value");
|
|
9505
|
+
const cross = field(root, "allowCrossMarketplaceDependenciesOn");
|
|
9506
|
+
need2(cross === undefined || stringArray(cross), "has an invalid allowCrossMarketplaceDependenciesOn value");
|
|
9507
|
+
const renames = field(root, "renames");
|
|
9508
|
+
if (renames !== undefined) {
|
|
9509
|
+
need2(renames.kind === "object" && renames.entries.every(([, target]) => isString(target) || target.kind === "literal" && target.value === "null"), "has an invalid renames map");
|
|
9510
|
+
need2(!renames.entries.some(([from]) => from.toLowerCase() === selector.plugin.toLowerCase()), "plugin id is redirected by the marketplace renames map");
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
function validateSource(source, selector, path) {
|
|
9514
|
+
if (isString(source)) {
|
|
9515
|
+
need2(source.value.length > 0, "has an empty plugin source");
|
|
9516
|
+
return;
|
|
9517
|
+
}
|
|
9518
|
+
need2(source?.kind === "object", "has an invalid plugin source");
|
|
9519
|
+
const type = field(source, "source");
|
|
9520
|
+
need2(isString(type) && Object.hasOwn(SOURCE_KEYS, type.value), "has an unreviewed plugin source type");
|
|
9521
|
+
knownKeys(source, new Set(SOURCE_KEYS[type.value]), selector, path);
|
|
9522
|
+
}
|
|
9523
|
+
function projectEntry(entry, selector) {
|
|
9524
|
+
const path = `plugins[${selector.plugin}]`;
|
|
9525
|
+
knownKeys(entry, { has: (key) => ENTRY_BOUND_KEYS.has(key) || ENTRY_OMITTED_KEYS.has(key) }, selector, path);
|
|
9526
|
+
const name = field(entry, "name");
|
|
9527
|
+
need2(isString(name) && name.value === selector.plugin, "selected entry name does not match");
|
|
9528
|
+
validateSource(field(entry, "source"), selector, `${path}.source`);
|
|
9529
|
+
for (const key of ["strict", "defaultEnabled"]) {
|
|
9530
|
+
const value = field(entry, key);
|
|
9531
|
+
need2(value === undefined || isBoolean(value), `has an invalid entry ${key}`);
|
|
9532
|
+
}
|
|
9533
|
+
const headersHelper = field(entry, "headersHelper");
|
|
9534
|
+
need2(headersHelper === undefined || isString(headersHelper), "has an invalid entry headersHelper");
|
|
9535
|
+
const experimental = field(entry, "experimental");
|
|
9536
|
+
if (experimental !== undefined) {
|
|
9537
|
+
need2(experimental.kind === "object", "has an invalid entry experimental object");
|
|
9538
|
+
knownKeys(experimental, EXPERIMENTAL_KEYS, selector, `${path}.experimental`);
|
|
9539
|
+
}
|
|
9540
|
+
for (const key of ["$schema", "description", "version", "homepage", "license", "category", "displayName"]) {
|
|
9541
|
+
const value = field(entry, key);
|
|
9542
|
+
need2(value === undefined || isString(value), `has an invalid entry ${key}`);
|
|
9543
|
+
}
|
|
9544
|
+
for (const [key, keys] of [["author", new Set(["name", "email", "url"])], ["repository", new Set(["type", "url", "directory"])]]) {
|
|
9545
|
+
const value = field(entry, key);
|
|
9546
|
+
if (value?.kind === "object")
|
|
9547
|
+
knownKeys(value, keys, selector, `${path}.${key}`);
|
|
9548
|
+
need2(value === undefined || isString(value) || stringObject(value), `has an invalid entry ${key}`);
|
|
9549
|
+
}
|
|
9550
|
+
for (const key of ["keywords", "tags"]) {
|
|
9551
|
+
const value = field(entry, key);
|
|
9552
|
+
need2(value === undefined || stringArray(value), `has an invalid entry ${key}`);
|
|
9553
|
+
}
|
|
9554
|
+
const metadata = field(entry, "metadata");
|
|
9555
|
+
need2(metadata === undefined || metadata.kind === "object", "has an invalid entry metadata object");
|
|
9556
|
+
return { kind: "object", entries: entry.entries.filter(([key]) => ENTRY_BOUND_KEYS.has(key)) };
|
|
9557
|
+
}
|
|
9558
|
+
function hashClaudeMarketplaceEntry(text, selector) {
|
|
9559
|
+
need2(selector && typeof selector.marketplace === "string" && NAME.test(selector.marketplace) && !selector.marketplace.includes("..") && typeof selector.plugin === "string" && NAME.test(selector.plugin), "requires an exact marketplace and plugin name");
|
|
9560
|
+
let root;
|
|
9561
|
+
try {
|
|
9562
|
+
root = parseNativeJsonObject(text);
|
|
9563
|
+
} catch (error) {
|
|
9564
|
+
throw new Error(`Claude marketplace entry witness cannot parse marketplace.json: ${reason(error)}`);
|
|
9565
|
+
}
|
|
9566
|
+
validateMarketplace(root, selector);
|
|
9567
|
+
const plugins = field(root, "plugins");
|
|
9568
|
+
need2(plugins?.kind === "array", "requires a plugins array");
|
|
9569
|
+
const entryName = (item) => {
|
|
9570
|
+
if (item.kind !== "object")
|
|
9571
|
+
return;
|
|
9572
|
+
const name = field(item, "name");
|
|
9573
|
+
return isString(name) ? name.value : undefined;
|
|
9574
|
+
};
|
|
9575
|
+
const exact = plugins.items.filter((item) => entryName(item) === selector.plugin);
|
|
9576
|
+
const folded = plugins.items.filter((item) => entryName(item)?.toLowerCase() === selector.plugin.toLowerCase());
|
|
9577
|
+
need2(exact.length > 0, "selected plugin entry is missing");
|
|
9578
|
+
need2(exact.length === 1 && folded.length === 1, "selected plugin entry is duplicated or ambiguous");
|
|
9579
|
+
const entry = projectEntry(exact[0], selector);
|
|
9580
|
+
const metadata = field(root, "metadata"), pluginRoot = metadata?.kind === "object" ? field(metadata, "pluginRoot") : undefined;
|
|
9581
|
+
const nullValue = { kind: "literal", value: "null" };
|
|
9582
|
+
const marketplace = { kind: "object", entries: [
|
|
9583
|
+
["name", field(root, "name")],
|
|
9584
|
+
["pluginRoot", pluginRoot ?? nullValue],
|
|
9585
|
+
["allowCrossMarketplaceDependenciesOn", field(root, "allowCrossMarketplaceDependenciesOn") ?? nullValue]
|
|
9586
|
+
] };
|
|
9587
|
+
const projection = { kind: "object", entries: [["marketplace", marketplace], ["plugin", entry]] };
|
|
9588
|
+
return createHash4("sha256").update(DOMAIN).update(canonical2(projection)).digest("hex");
|
|
9589
|
+
}
|
|
9590
|
+
function captureClaudeMarketplaceEntry(path, marketplace, plugin, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
9591
|
+
need2(claudeMarketplaceEntrySourceValid({ path, marketplace, plugin }), "requires a normalized absolute .claude-plugin/marketplace.json path and exact names");
|
|
9592
|
+
let text;
|
|
9593
|
+
try {
|
|
9594
|
+
text = readNativeSettingsWitnessFile(path, budget2, "marketplace.json");
|
|
9595
|
+
} catch (error) {
|
|
9596
|
+
throw new Error(`Claude marketplace entry witness cannot read marketplace.json: ${reason(error)}`);
|
|
9597
|
+
}
|
|
9598
|
+
return { path, hashMode: CLAUDE_MARKETPLACE_ENTRY_HASH_MODE, marketplace, plugin, sha256: hashClaudeMarketplaceEntry(text, { marketplace, plugin }) };
|
|
9599
|
+
}
|
|
9600
|
+
|
|
9404
9601
|
// src/lib/agent-policy-limits.ts
|
|
9405
9602
|
var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
|
|
9406
9603
|
function object(value) {
|
|
@@ -9455,7 +9652,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9455
9652
|
text(control.pluginId, 1024);
|
|
9456
9653
|
text(control.namespace, 64);
|
|
9457
9654
|
text(control.pluginParent);
|
|
9458
|
-
requireBound(
|
|
9655
|
+
requireBound(isAbsolute4(control.pluginParent) && resolve4(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
|
|
9459
9656
|
if (control.remotePluginId !== undefined)
|
|
9460
9657
|
requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
|
|
9461
9658
|
if (control.skillsOnly !== undefined)
|
|
@@ -9473,10 +9670,18 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9473
9670
|
text(control.pluginId, 1024);
|
|
9474
9671
|
text(control.namespace, 64);
|
|
9475
9672
|
text(control.pluginParent);
|
|
9476
|
-
requireBound(!ids.has(control.pluginId) &&
|
|
9673
|
+
requireBound(!ids.has(control.pluginId) && isAbsolute4(control.pluginParent) && resolve4(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
|
|
9477
9674
|
ids.add(control.pluginId);
|
|
9478
9675
|
}
|
|
9479
9676
|
}
|
|
9677
|
+
if (bridge.discoveryExecutables !== undefined)
|
|
9678
|
+
for (const [agent, executable] of Object.entries(record(bridge.discoveryExecutables, AGENT_POLICY_LIMITS.agents))) {
|
|
9679
|
+
requireBound(agent === "gemini" && object(executable) && Object.keys(executable).length === 3 && executable.command === "gemini");
|
|
9680
|
+
for (const path of [executable.path, executable.target]) {
|
|
9681
|
+
text(path);
|
|
9682
|
+
requireBound(!/[\x00-\x1f\x7f-\x9f\u200e\u200f\u202a-\u202e\u2066-\u2069]/.test(path) && isAbsolute4(path) && resolve4(path) === path);
|
|
9683
|
+
}
|
|
9684
|
+
}
|
|
9480
9685
|
if (bridge.disabledBuiltins !== undefined)
|
|
9481
9686
|
for (const builtin of array(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
|
|
9482
9687
|
requireBound(object(builtin));
|
|
@@ -9502,11 +9707,11 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9502
9707
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).every((key) => ["roots", "parents", "directories"].includes(key)) && roots.length + parents2.length > 0 && new Set(roots).size === roots.length && new Set(parents2).size === parents2.length);
|
|
9503
9708
|
for (const root of roots) {
|
|
9504
9709
|
text(root);
|
|
9505
|
-
requireBound(
|
|
9710
|
+
requireBound(isAbsolute4(root) && resolve4(root) === root);
|
|
9506
9711
|
}
|
|
9507
9712
|
for (const parent of parents2) {
|
|
9508
9713
|
text(parent);
|
|
9509
|
-
requireBound(
|
|
9714
|
+
requireBound(isAbsolute4(parent) && resolve4(parent) === parent);
|
|
9510
9715
|
}
|
|
9511
9716
|
for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
9512
9717
|
requireBound(object(directory));
|
|
@@ -9526,7 +9731,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9526
9731
|
text(input.namespace, 64);
|
|
9527
9732
|
text(input.pluginParent);
|
|
9528
9733
|
text(input.sourceRoot);
|
|
9529
|
-
requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) =>
|
|
9734
|
+
requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute4(path) && resolve4(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
|
|
9530
9735
|
ids.add(input.pluginId);
|
|
9531
9736
|
}
|
|
9532
9737
|
if (proof.directories !== undefined)
|
|
@@ -9543,17 +9748,19 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9543
9748
|
text(source.path);
|
|
9544
9749
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
9545
9750
|
if (source.hashMode !== undefined)
|
|
9546
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9751
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1", "claude-marketplace-entry-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9547
9752
|
if (source.hashMode === "claude-plugin-manifest-v1")
|
|
9548
|
-
requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) &&
|
|
9753
|
+
requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && source.path.endsWith(join7(sep2, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
|
|
9549
9754
|
if (source.hashMode === "sumi-settings-v1")
|
|
9550
|
-
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) &&
|
|
9755
|
+
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "sumi.json");
|
|
9551
9756
|
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
9552
|
-
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) &&
|
|
9757
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "config.toml");
|
|
9553
9758
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
|
|
9554
|
-
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) &&
|
|
9759
|
+
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "settings.json");
|
|
9555
9760
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
|
|
9556
|
-
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) &&
|
|
9761
|
+
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
|
|
9762
|
+
if (source.hashMode === "claude-marketplace-entry-v1")
|
|
9763
|
+
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && claudeMarketplaceEntrySourceValid(source));
|
|
9557
9764
|
if (source.hashMode === "claude-plugin-registry") {
|
|
9558
9765
|
requireBound(agent === "claude");
|
|
9559
9766
|
const managed = array(source.managedPlugins, 64);
|
|
@@ -9568,8 +9775,8 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9568
9775
|
if (source.format !== undefined)
|
|
9569
9776
|
requireBound(["json", "toml", "yaml"].includes(source.format));
|
|
9570
9777
|
if (source.fields !== undefined)
|
|
9571
|
-
for (const
|
|
9572
|
-
text(
|
|
9778
|
+
for (const field2 of array(source.fields, AGENT_POLICY_LIMITS.fields))
|
|
9779
|
+
text(field2, 256);
|
|
9573
9780
|
}
|
|
9574
9781
|
if (value.directories !== undefined)
|
|
9575
9782
|
for (const directory of array(value.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
@@ -9584,9 +9791,9 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9584
9791
|
}
|
|
9585
9792
|
|
|
9586
9793
|
// src/lib/agent-discovery.ts
|
|
9587
|
-
import { createHash as
|
|
9794
|
+
import { createHash as createHash25 } from "crypto";
|
|
9588
9795
|
import { lstatSync as lstatSync25, readFileSync as readFileSync20, readdirSync as readdirSync10, realpathSync as realpathSync11 } from "fs";
|
|
9589
|
-
import { basename as
|
|
9796
|
+
import { basename as basename11, dirname as dirname20, isAbsolute as isAbsolute21, join as join32, resolve as resolve20, sep as sep8 } from "path";
|
|
9590
9797
|
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
9591
9798
|
|
|
9592
9799
|
// node_modules/yaml/dist/index.js
|
|
@@ -9636,7 +9843,7 @@ var $visit = visit.visit;
|
|
|
9636
9843
|
var $visitAsync = visit.visitAsync;
|
|
9637
9844
|
|
|
9638
9845
|
// src/lib/agent-hermes.ts
|
|
9639
|
-
import { dirname as
|
|
9846
|
+
import { dirname as dirname5, isAbsolute as isAbsolute5, join as join8, resolve as resolve5 } from "path";
|
|
9640
9847
|
import { lstatSync as lstatSync4, readFileSync as readFileSync6, opendirSync } from "fs";
|
|
9641
9848
|
var HERMES_OPT_OUT = ".no-bundled-skills";
|
|
9642
9849
|
var EVENTS = ["pre_llm_call", "pre_tool_call"];
|
|
@@ -9674,7 +9881,7 @@ function quoted(value) {
|
|
|
9674
9881
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
9675
9882
|
}
|
|
9676
9883
|
function hermesHookDefinitions(supervisor) {
|
|
9677
|
-
if (!
|
|
9884
|
+
if (!isAbsolute5(supervisor.runtime) || !isAbsolute5(supervisor.path))
|
|
9678
9885
|
throw new Error("Expected absolute Hermes supervisor paths");
|
|
9679
9886
|
const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
|
|
9680
9887
|
return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
|
|
@@ -9710,16 +9917,16 @@ function assertHermesEnvironment(home) {
|
|
|
9710
9917
|
throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
|
|
9711
9918
|
}
|
|
9712
9919
|
const selected = process.env.HERMES_HOME?.trim();
|
|
9713
|
-
if (selected && (!
|
|
9920
|
+
if (selected && (!isAbsolute5(selected) || resolve5(selected) !== join8(resolve5(home), ".hermes")))
|
|
9714
9921
|
throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
|
|
9715
9922
|
if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
|
|
9716
9923
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
|
|
9717
9924
|
}
|
|
9718
9925
|
function read(path) {
|
|
9719
|
-
for (let current = path;; current =
|
|
9926
|
+
for (let current = path;; current = dirname5(current)) {
|
|
9720
9927
|
if (lstatSync4(current, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
9721
9928
|
throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes protection input");
|
|
9722
|
-
if (
|
|
9929
|
+
if (dirname5(current) === current)
|
|
9723
9930
|
break;
|
|
9724
9931
|
}
|
|
9725
9932
|
const stat = lstatSync4(path, { throwIfNoEntry: false });
|
|
@@ -9733,7 +9940,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
|
|
|
9733
9940
|
assertHermesEnvironment(home);
|
|
9734
9941
|
if (read(join8(home, ".hermes", HERMES_OPT_OUT)) === null)
|
|
9735
9942
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
|
|
9736
|
-
if (!supervisor || !
|
|
9943
|
+
if (!supervisor || !isAbsolute5(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
|
|
9737
9944
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
|
|
9738
9945
|
const raw = read(join8(home, ".hermes/shell-hooks-allowlist.json"));
|
|
9739
9946
|
let trust;
|
|
@@ -9759,10 +9966,10 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
|
|
|
9759
9966
|
}
|
|
9760
9967
|
function assertNoHermesLegacyShadow(root) {
|
|
9761
9968
|
let entries = 0;
|
|
9762
|
-
for (let cursor =
|
|
9969
|
+
for (let cursor = resolve5(root);; cursor = dirname5(cursor)) {
|
|
9763
9970
|
if (lstatSync4(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
9764
9971
|
throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes legacy discovery root");
|
|
9765
|
-
if (
|
|
9972
|
+
if (dirname5(cursor) === cursor)
|
|
9766
9973
|
break;
|
|
9767
9974
|
}
|
|
9768
9975
|
function scan(path, depth) {
|
|
@@ -9783,10 +9990,10 @@ function assertNoHermesLegacyShadow(root) {
|
|
|
9783
9990
|
const hasDescriptor = Boolean(lstatSync4(join8(path, "SKILL.md"), { throwIfNoEntry: false }));
|
|
9784
9991
|
const directory = opendirSync(path);
|
|
9785
9992
|
try {
|
|
9786
|
-
for (let
|
|
9787
|
-
if (hasDescriptor && ["references", "templates", "assets", "scripts"].includes(
|
|
9993
|
+
for (let child2 = directory.readSync();child2; child2 = directory.readSync()) {
|
|
9994
|
+
if (hasDescriptor && ["references", "templates", "assets", "scripts"].includes(child2.name))
|
|
9788
9995
|
continue;
|
|
9789
|
-
scan(join8(path,
|
|
9996
|
+
scan(join8(path, child2.name), depth + 1);
|
|
9790
9997
|
}
|
|
9791
9998
|
} finally {
|
|
9792
9999
|
directory.closeSync();
|
|
@@ -9843,18 +10050,18 @@ if (failed) process.exitCode = 2;
|
|
|
9843
10050
|
}
|
|
9844
10051
|
|
|
9845
10052
|
// src/lib/agent-discovery-directories.ts
|
|
9846
|
-
import { createHash as
|
|
10053
|
+
import { createHash as createHash5 } from "crypto";
|
|
9847
10054
|
import { lstatSync as lstatSync5, opendirSync as opendirSync2 } from "fs";
|
|
9848
|
-
import { dirname as
|
|
9849
|
-
function
|
|
10055
|
+
import { dirname as dirname6, isAbsolute as isAbsolute6, join as join9, resolve as resolve6 } from "path";
|
|
10056
|
+
function need3(value, message) {
|
|
9850
10057
|
if (!value)
|
|
9851
10058
|
throw new Error(message);
|
|
9852
10059
|
}
|
|
9853
10060
|
function safe(path) {
|
|
9854
|
-
|
|
9855
|
-
for (let cursor = path;; cursor =
|
|
9856
|
-
|
|
9857
|
-
if (
|
|
10061
|
+
need3(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute6(path) && resolve6(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
|
|
10062
|
+
for (let cursor = path;; cursor = dirname6(cursor)) {
|
|
10063
|
+
need3(!lstatSync5(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
|
|
10064
|
+
if (dirname6(cursor) === cursor)
|
|
9858
10065
|
break;
|
|
9859
10066
|
}
|
|
9860
10067
|
}
|
|
@@ -9868,45 +10075,45 @@ function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
|
|
|
9868
10075
|
const root = lstatSync5(path, { throwIfNoEntry: false });
|
|
9869
10076
|
if (!root)
|
|
9870
10077
|
return { path, sha256: null };
|
|
9871
|
-
|
|
9872
|
-
const hash =
|
|
10078
|
+
need3(root.isDirectory(), "Unsupported native discovery directory input");
|
|
10079
|
+
const hash = createHash5("sha256");
|
|
9873
10080
|
function walk(current, relative2, before, depth) {
|
|
9874
|
-
|
|
10081
|
+
need3(depth <= 64, "Native discovery directory depth limit exceeded");
|
|
9875
10082
|
const names = [], directory = opendirSync2(current);
|
|
9876
10083
|
try {
|
|
9877
10084
|
for (let entry = directory.readSync();entry; entry = directory.readSync()) {
|
|
9878
|
-
|
|
10085
|
+
need3(++budget2.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
|
|
9879
10086
|
names.push(entry.name);
|
|
9880
10087
|
}
|
|
9881
10088
|
} finally {
|
|
9882
10089
|
directory.closeSync();
|
|
9883
10090
|
}
|
|
9884
|
-
|
|
10091
|
+
need3(new Set(names).size === names.length, "Ambiguous native discovery directory names");
|
|
9885
10092
|
for (const name of names.sort()) {
|
|
9886
|
-
const
|
|
9887
|
-
if (omittedRoots.includes(
|
|
10093
|
+
const child2 = join9(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
|
|
10094
|
+
if (omittedRoots.includes(child2))
|
|
9888
10095
|
continue;
|
|
9889
|
-
|
|
9890
|
-
const stat = lstatSync5(
|
|
9891
|
-
|
|
10096
|
+
need3(child2.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
|
|
10097
|
+
const stat = lstatSync5(child2);
|
|
10098
|
+
need3(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
|
|
9892
10099
|
const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
|
|
9893
10100
|
`;
|
|
9894
10101
|
budget2.bytes += Buffer.byteLength(row);
|
|
9895
|
-
|
|
10102
|
+
need3(budget2.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
|
|
9896
10103
|
hash.update(row);
|
|
9897
10104
|
rows?.push(row);
|
|
9898
|
-
if (stat.isDirectory() && !excludedRoots.includes(
|
|
9899
|
-
walk(
|
|
10105
|
+
if (stat.isDirectory() && !excludedRoots.includes(child2))
|
|
10106
|
+
walk(child2, childRelative, stat, depth + 1);
|
|
9900
10107
|
}
|
|
9901
|
-
|
|
10108
|
+
need3(same(before, lstatSync5(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
|
|
9902
10109
|
}
|
|
9903
10110
|
walk(path, "", root, 0);
|
|
9904
10111
|
safe(path);
|
|
9905
10112
|
return { path, sha256: hash.digest("hex") };
|
|
9906
10113
|
}
|
|
9907
10114
|
function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
|
|
9908
|
-
|
|
9909
|
-
|
|
10115
|
+
need3(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid projected discovery directory collection");
|
|
10116
|
+
need3(omittedRoots.length <= AGENT_POLICY_LIMITS.discoveryRoots && new Set(omittedRoots).size === omittedRoots.length, "Invalid inactive discovery roots");
|
|
9910
10117
|
for (const root of omittedRoots)
|
|
9911
10118
|
safe(root);
|
|
9912
10119
|
const budget2 = { entries: 0, bytes: 0 };
|
|
@@ -9916,62 +10123,62 @@ function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
|
|
|
9916
10123
|
});
|
|
9917
10124
|
}
|
|
9918
10125
|
function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
|
|
9919
|
-
|
|
10126
|
+
need3(directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid projected discovery directory collection");
|
|
9920
10127
|
let count = 0, bytes = 0;
|
|
9921
10128
|
const expected = directories.map((directory) => {
|
|
9922
10129
|
safe(directory.path);
|
|
9923
|
-
|
|
9924
|
-
const hash =
|
|
10130
|
+
need3(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
|
|
10131
|
+
const hash = createHash5("sha256");
|
|
9925
10132
|
const kept = [];
|
|
9926
10133
|
for (const row of directory.entries) {
|
|
9927
|
-
|
|
10134
|
+
need3(typeof row === "string" && ++count <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Invalid reviewed directory membership rows");
|
|
9928
10135
|
bytes += Buffer.byteLength(row);
|
|
9929
|
-
|
|
10136
|
+
need3(bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Reviewed directory membership byte limit exceeded");
|
|
9930
10137
|
const value = JSON.parse(row);
|
|
9931
|
-
|
|
10138
|
+
need3(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute6(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
|
|
9932
10139
|
`, "Invalid reviewed directory member");
|
|
9933
10140
|
hash.update(row);
|
|
9934
10141
|
const path = join9(directory.path, value[0]);
|
|
9935
10142
|
if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
|
|
9936
10143
|
kept.push(row);
|
|
9937
10144
|
}
|
|
9938
|
-
|
|
10145
|
+
need3(directory.sha256 === null ? directory.entries.length === 0 : hash.digest("hex") === directory.sha256, "Reviewed directory inventory does not match its fingerprint");
|
|
9939
10146
|
const absent = omittedRoots.some((root) => directory.path === root || directory.path.startsWith(root + "/"));
|
|
9940
|
-
return { path: directory.path, sha256: absent || directory.sha256 === null ? null :
|
|
10147
|
+
return { path: directory.path, sha256: absent || directory.sha256 === null ? null : createHash5("sha256").update(kept.join("")).digest("hex") };
|
|
9941
10148
|
});
|
|
9942
10149
|
const current = captureDiscoveryDirectoryProjection(directories.map((item) => item.path), omittedRoots);
|
|
9943
|
-
|
|
10150
|
+
need3(current.every((item, index) => item.sha256 === expected[index].sha256), "Native discovery directory membership changed outside retired Codex roots");
|
|
9944
10151
|
}
|
|
9945
10152
|
function captureDiscoveryDirectories(paths, excludedRoots = []) {
|
|
9946
|
-
|
|
10153
|
+
need3(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
|
|
9947
10154
|
const budget2 = { entries: 0, bytes: 0 };
|
|
9948
10155
|
for (const root of excludedRoots)
|
|
9949
10156
|
safe(root);
|
|
9950
10157
|
return paths.map((path) => capture(path, budget2, excludedRoots));
|
|
9951
10158
|
}
|
|
9952
10159
|
function verifyDiscoveryDirectories(directories, excludedRoots = []) {
|
|
9953
|
-
|
|
10160
|
+
need3(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
|
|
9954
10161
|
for (const directory of directories)
|
|
9955
|
-
|
|
10162
|
+
need3(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
|
|
9956
10163
|
const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
|
|
9957
10164
|
for (let i = 0;i < directories.length; i++)
|
|
9958
|
-
|
|
10165
|
+
need3(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
|
|
9959
10166
|
}
|
|
9960
10167
|
|
|
9961
10168
|
// src/lib/agent-discovery-bytes.ts
|
|
9962
|
-
import { createHash as
|
|
10169
|
+
import { createHash as createHash6 } from "crypto";
|
|
9963
10170
|
import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync6, openSync as openSync2, readSync as readSync2 } from "fs";
|
|
9964
|
-
import { dirname as
|
|
10171
|
+
import { dirname as dirname7, isAbsolute as isAbsolute7, resolve as resolve7 } from "path";
|
|
9965
10172
|
function discoveryByteBudget() {
|
|
9966
10173
|
return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
|
|
9967
10174
|
}
|
|
9968
10175
|
function safe2(path) {
|
|
9969
|
-
if (typeof path !== "string" || !
|
|
10176
|
+
if (typeof path !== "string" || !isAbsolute7(path) || path !== resolve7(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
|
|
9970
10177
|
throw new Error("Expected a canonical absolute raw discovery path");
|
|
9971
|
-
for (let cursor = path;; cursor =
|
|
10178
|
+
for (let cursor = path;; cursor = dirname7(cursor)) {
|
|
9972
10179
|
if (lstatSync6(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
9973
10180
|
throw new Error("Refusing symlink raw discovery input");
|
|
9974
|
-
if (
|
|
10181
|
+
if (dirname7(cursor) === cursor)
|
|
9975
10182
|
break;
|
|
9976
10183
|
}
|
|
9977
10184
|
}
|
|
@@ -9990,7 +10197,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
|
|
|
9990
10197
|
if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
9991
10198
|
throw new Error("Raw discovery file byte limit exceeded");
|
|
9992
10199
|
consume(budget2, bytes);
|
|
9993
|
-
return
|
|
10200
|
+
return createHash6("sha256").update(text2).digest("hex");
|
|
9994
10201
|
}
|
|
9995
10202
|
const initial = lstatSync6(path, { throwIfNoEntry: false });
|
|
9996
10203
|
if (!initial)
|
|
@@ -10004,7 +10211,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
|
|
|
10004
10211
|
const opened = fstatSync2(fd);
|
|
10005
10212
|
if (!opened.isFile() || !same2(initial, opened))
|
|
10006
10213
|
throw new Error("Raw discovery input changed during open");
|
|
10007
|
-
const hash =
|
|
10214
|
+
const hash = createHash6("sha256"), buffer = Buffer.alloc(64 * 1024);
|
|
10008
10215
|
let bytes = 0;
|
|
10009
10216
|
while (true) {
|
|
10010
10217
|
const count = readSync2(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
|
|
@@ -10027,9 +10234,9 @@ function hashRawDiscoveryFile(path, budget2, changes) {
|
|
|
10027
10234
|
}
|
|
10028
10235
|
|
|
10029
10236
|
// src/lib/agent-discovery-path-bytes.ts
|
|
10030
|
-
import { createHash as
|
|
10237
|
+
import { createHash as createHash7 } from "crypto";
|
|
10031
10238
|
import { lstatSync as lstatSync7, readlinkSync } from "fs";
|
|
10032
|
-
import { dirname as
|
|
10239
|
+
import { dirname as dirname8, isAbsolute as isAbsolute8, join as join10, parse as parse2, resolve as resolve8, sep as sep3 } from "path";
|
|
10033
10240
|
function identity2(stat, mutable = false) {
|
|
10034
10241
|
return {
|
|
10035
10242
|
dev: String(stat.dev),
|
|
@@ -10040,12 +10247,12 @@ function identity2(stat, mutable = false) {
|
|
|
10040
10247
|
...mutable ? { size: String(stat.size), mtimeNs: String(stat.mtimeNs), ctimeNs: String(stat.ctimeNs) } : {}
|
|
10041
10248
|
};
|
|
10042
10249
|
}
|
|
10043
|
-
function
|
|
10044
|
-
if (typeof path !== "string" || !
|
|
10250
|
+
function canonical3(path) {
|
|
10251
|
+
if (typeof path !== "string" || !isAbsolute8(path) || path !== resolve8(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00") || Buffer.from(path).toString("utf8") !== path)
|
|
10045
10252
|
throw new Error("Expected a canonical absolute discovery path");
|
|
10046
10253
|
}
|
|
10047
10254
|
function trace(path) {
|
|
10048
|
-
|
|
10255
|
+
canonical3(path);
|
|
10049
10256
|
const rows = [], paths = [];
|
|
10050
10257
|
let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(sep3);
|
|
10051
10258
|
function record2(path2, row) {
|
|
@@ -10070,7 +10277,7 @@ function trace(path) {
|
|
|
10070
10277
|
if (!component || component === ".")
|
|
10071
10278
|
continue;
|
|
10072
10279
|
if (component === "..") {
|
|
10073
|
-
cursor =
|
|
10280
|
+
cursor = dirname8(cursor);
|
|
10074
10281
|
continue;
|
|
10075
10282
|
}
|
|
10076
10283
|
const candidate = join10(cursor, component);
|
|
@@ -10090,11 +10297,11 @@ function trace(path) {
|
|
|
10090
10297
|
if (!target || target.includes("\x00") || Buffer.byteLength(target) > AGENT_POLICY_LIMITS.pathCharacters || !after.isSymbolicLink() || JSON.stringify(identity2(stat, true)) !== JSON.stringify(identity2(after, true)))
|
|
10091
10298
|
throw new Error("Discovery link changed during read");
|
|
10092
10299
|
record2(candidate, { type: "link", target, ...identity2(stat, true) });
|
|
10093
|
-
if (
|
|
10300
|
+
if (isAbsolute8(target)) {
|
|
10094
10301
|
cursor = parse2(target).root;
|
|
10095
10302
|
root();
|
|
10096
10303
|
}
|
|
10097
|
-
const remainder =
|
|
10304
|
+
const remainder = isAbsolute8(target) ? target.slice(parse2(target).root.length) : target;
|
|
10098
10305
|
pending = [...remainder.split(sep3), ...pending];
|
|
10099
10306
|
if (pending.length + steps > AGENT_POLICY_LIMITS.discoveryPathSteps)
|
|
10100
10307
|
throw new Error("Discovery path step limit exceeded");
|
|
@@ -10137,15 +10344,15 @@ function hashDiscoveryPathFile(path, budget2, changes) {
|
|
|
10137
10344
|
const after = trace(path);
|
|
10138
10345
|
if (JSON.stringify(before.rows) !== JSON.stringify(after.rows) || before.file !== after.file)
|
|
10139
10346
|
throw new Error("Discovery path input changed during read");
|
|
10140
|
-
return
|
|
10347
|
+
return createHash7("sha256").update(JSON.stringify({ version: 1, path, rows: before.rows, content })).digest("hex");
|
|
10141
10348
|
}
|
|
10142
10349
|
|
|
10143
10350
|
// src/lib/claude-plugin-manifest-witness.ts
|
|
10144
|
-
import { createHash as
|
|
10351
|
+
import { createHash as createHash8 } from "crypto";
|
|
10145
10352
|
var CLAUDE_PLUGIN_MANIFEST_LIMITS = Object.freeze({ bytes: 1024 * 1024, depth: 32, nodes: 65536, stringCharacters: 16384 });
|
|
10146
|
-
function requireValue(value,
|
|
10353
|
+
function requireValue(value, reason2) {
|
|
10147
10354
|
if (!value)
|
|
10148
|
-
throw new Error("Claude plugin manifest " +
|
|
10355
|
+
throw new Error("Claude plugin manifest " + reason2);
|
|
10149
10356
|
}
|
|
10150
10357
|
function parse3(text2) {
|
|
10151
10358
|
requireValue(Buffer.byteLength(text2, "utf8") <= CLAUDE_PLUGIN_MANIFEST_LIMITS.bytes, "exceeds its byte limit");
|
|
@@ -10221,7 +10428,7 @@ function parse3(text2) {
|
|
|
10221
10428
|
requireValue(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
|
|
10222
10429
|
return root;
|
|
10223
10430
|
}
|
|
10224
|
-
function
|
|
10431
|
+
function canonical4(value) {
|
|
10225
10432
|
if (value.kind === "string")
|
|
10226
10433
|
return JSON.stringify(value.value);
|
|
10227
10434
|
if (value.kind === "number")
|
|
@@ -10229,19 +10436,19 @@ function canonical3(value) {
|
|
|
10229
10436
|
if (value.kind === "literal")
|
|
10230
10437
|
return value.value;
|
|
10231
10438
|
if (value.kind === "array")
|
|
10232
|
-
return "[" + value.items.map(
|
|
10233
|
-
return "{" + value.entries.map(([key, item]) => JSON.stringify(key) + ":" +
|
|
10439
|
+
return "[" + value.items.map(canonical4).join(",") + "]";
|
|
10440
|
+
return "{" + value.entries.map(([key, item]) => JSON.stringify(key) + ":" + canonical4(item)).join(",") + "}";
|
|
10234
10441
|
}
|
|
10235
10442
|
var DESCRIPTIVE_FIELDS = new Set(["description", "version", "author", "homepage", "repository", "license", "keywords"]);
|
|
10236
|
-
function validateText(value,
|
|
10237
|
-
requireValue(value?.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters, "has an invalid " +
|
|
10443
|
+
function validateText(value, field2) {
|
|
10444
|
+
requireValue(value?.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters, "has an invalid " + field2 + " field");
|
|
10238
10445
|
}
|
|
10239
10446
|
function validateDescriptiveFields(manifest) {
|
|
10240
10447
|
const values = new Map(manifest.entries);
|
|
10241
|
-
for (const
|
|
10242
|
-
const value = values.get(
|
|
10448
|
+
for (const field2 of ["description", "version", "homepage", "license"]) {
|
|
10449
|
+
const value = values.get(field2);
|
|
10243
10450
|
if (value !== undefined)
|
|
10244
|
-
validateText(value,
|
|
10451
|
+
validateText(value, field2);
|
|
10245
10452
|
}
|
|
10246
10453
|
const author = values.get("author");
|
|
10247
10454
|
if (author !== undefined) {
|
|
@@ -10266,10 +10473,10 @@ function hashClaudePluginManifest(text2) {
|
|
|
10266
10473
|
const manifest = parse3(text2);
|
|
10267
10474
|
validateDescriptiveFields(manifest);
|
|
10268
10475
|
const discovery = { kind: "object", entries: manifest.entries.filter(([key]) => !DESCRIPTIVE_FIELDS.has(key)) };
|
|
10269
|
-
return
|
|
10476
|
+
return createHash8("sha256").update(canonical4(discovery)).digest("hex");
|
|
10270
10477
|
}
|
|
10271
10478
|
function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
|
|
10272
|
-
if (!/^[a-f0-9]{64}$/.test(reviewedSha256) ||
|
|
10479
|
+
if (!/^[a-f0-9]{64}$/.test(reviewedSha256) || createHash8("sha256").update(text2).digest("hex") !== reviewedSha256) {
|
|
10273
10480
|
throw new Error("Reviewed Claude plugin manifest changed before semantic projection");
|
|
10274
10481
|
}
|
|
10275
10482
|
return hashClaudePluginManifest(text2);
|
|
@@ -10277,22 +10484,22 @@ function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
|
|
|
10277
10484
|
|
|
10278
10485
|
// src/lib/plugin-discovery.ts
|
|
10279
10486
|
import { closeSync as closeSync13, constants as constants13, fstatSync as fstatSync13, lstatSync as lstatSync23, openSync as openSync13, opendirSync as opendirSync5, readSync as readSync10 } from "fs";
|
|
10280
|
-
import { basename as
|
|
10487
|
+
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute19, join as join31, resolve as resolve18 } from "path";
|
|
10281
10488
|
|
|
10282
10489
|
// src/lib/plugin-admission.ts
|
|
10283
10490
|
import { homedir as homedir5 } from "os";
|
|
10284
|
-
import { isAbsolute as
|
|
10491
|
+
import { isAbsolute as isAbsolute18, join as join30, resolve as resolve17 } from "path";
|
|
10285
10492
|
|
|
10286
10493
|
// src/lib/selection-cache.ts
|
|
10287
|
-
import { createHash as
|
|
10494
|
+
import { createHash as createHash10, randomUUID } from "crypto";
|
|
10288
10495
|
import { spawnSync } from "child_process";
|
|
10289
10496
|
import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, fsyncSync, linkSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync8, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
10290
10497
|
import { hostname } from "os";
|
|
10291
|
-
import { dirname as
|
|
10498
|
+
import { dirname as dirname9, join as join12, parse as parse4, resolve as resolve9 } from "path";
|
|
10292
10499
|
import { isDeepStrictEqual } from "util";
|
|
10293
10500
|
|
|
10294
10501
|
// src/lib/skill-bundle.ts
|
|
10295
|
-
import { createHash as
|
|
10502
|
+
import { createHash as createHash9 } from "crypto";
|
|
10296
10503
|
import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
|
|
10297
10504
|
import { join as join11, relative as relative2 } from "path";
|
|
10298
10505
|
import { createGunzip } from "zlib";
|
|
@@ -10447,7 +10654,7 @@ function ownBytes(view) {
|
|
|
10447
10654
|
return out;
|
|
10448
10655
|
}
|
|
10449
10656
|
function sha256Hex(bytes) {
|
|
10450
|
-
return
|
|
10657
|
+
return createHash9("sha256").update(bytes).digest("hex");
|
|
10451
10658
|
}
|
|
10452
10659
|
function collectSkillBundleEntries(dir) {
|
|
10453
10660
|
const entries = [];
|
|
@@ -10643,12 +10850,12 @@ function inspectionLimits(options) {
|
|
|
10643
10850
|
for (const key of Object.keys(options.limits ?? {})) {
|
|
10644
10851
|
if (!Object.hasOwn(limits, key))
|
|
10645
10852
|
throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Unknown bundle limit");
|
|
10646
|
-
const
|
|
10647
|
-
const value = options.limits[
|
|
10648
|
-
if (!Number.isSafeInteger(value) || value <= 0 || value > limits[
|
|
10853
|
+
const field2 = key;
|
|
10854
|
+
const value = options.limits[field2];
|
|
10855
|
+
if (!Number.isSafeInteger(value) || value <= 0 || value > limits[field2]) {
|
|
10649
10856
|
throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Bundle limits must be positive integers within the hard ceilings");
|
|
10650
10857
|
}
|
|
10651
|
-
limits[
|
|
10858
|
+
limits[field2] = value;
|
|
10652
10859
|
}
|
|
10653
10860
|
return limits;
|
|
10654
10861
|
}
|
|
@@ -10678,7 +10885,14 @@ async function inspectSkillBundle(bundle, options = {}) {
|
|
|
10678
10885
|
decoder.destroy(terminalError);
|
|
10679
10886
|
};
|
|
10680
10887
|
const onAbort = () => stop("BUNDLE_ABORTED");
|
|
10681
|
-
const
|
|
10888
|
+
const expire = () => {
|
|
10889
|
+
const remaining = deadline - performance.now();
|
|
10890
|
+
if (remaining > 0)
|
|
10891
|
+
timer = setTimeout(expire, Math.max(1, Math.ceil(remaining)));
|
|
10892
|
+
else
|
|
10893
|
+
stop("BUNDLE_TIMEOUT");
|
|
10894
|
+
};
|
|
10895
|
+
let timer = setTimeout(expire, Math.max(1, Math.ceil(deadline - performance.now())));
|
|
10682
10896
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
10683
10897
|
let decompressedByteSize = 0;
|
|
10684
10898
|
let bytesSinceYield = 0;
|
|
@@ -10693,7 +10907,7 @@ async function inspectSkillBundle(bundle, options = {}) {
|
|
|
10693
10907
|
parser2.push(chunk);
|
|
10694
10908
|
bytesSinceYield += chunk.byteLength;
|
|
10695
10909
|
if (bytesSinceYield >= 256 * 1024) {
|
|
10696
|
-
await new Promise((
|
|
10910
|
+
await new Promise((resolve9) => setTimeout(resolve9, 0));
|
|
10697
10911
|
bytesSinceYield = 0;
|
|
10698
10912
|
check();
|
|
10699
10913
|
}
|
|
@@ -10830,8 +11044,8 @@ class BoundedTarReader {
|
|
|
10830
11044
|
}
|
|
10831
11045
|
}
|
|
10832
11046
|
}
|
|
10833
|
-
function tarOctal(
|
|
10834
|
-
const text2 = new TextDecoder().decode(
|
|
11047
|
+
function tarOctal(field2) {
|
|
11048
|
+
const text2 = new TextDecoder().decode(field2);
|
|
10835
11049
|
if (!/^[0-7]+[\0 ]*$/.test(text2))
|
|
10836
11050
|
invalidBundle("Invalid tar octal field");
|
|
10837
11051
|
const value = Number.parseInt(text2, 8);
|
|
@@ -10922,8 +11136,8 @@ function profileSelectionSnapshot(selections) {
|
|
|
10922
11136
|
// src/lib/selection-cache.ts
|
|
10923
11137
|
class SkillSelectionError extends Error {
|
|
10924
11138
|
code;
|
|
10925
|
-
constructor(code, message) {
|
|
10926
|
-
super(message);
|
|
11139
|
+
constructor(code, message, options) {
|
|
11140
|
+
super(message, options);
|
|
10927
11141
|
this.code = code;
|
|
10928
11142
|
this.name = "SkillSelectionError";
|
|
10929
11143
|
}
|
|
@@ -10931,14 +11145,14 @@ class SkillSelectionError extends Error {
|
|
|
10931
11145
|
var MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
10932
11146
|
var SELECTION_LOCK_FILE = "selection.lock.json";
|
|
10933
11147
|
function selectionCacheRoot(options = {}) {
|
|
10934
|
-
return
|
|
11148
|
+
return resolve9(options.cacheDir ?? join12(getDataDirReadOnly(), "selection-cache"));
|
|
10935
11149
|
}
|
|
10936
11150
|
function selectionKey(selection) {
|
|
10937
11151
|
validateSelection(selection);
|
|
10938
11152
|
return hash([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
|
|
10939
11153
|
}
|
|
10940
11154
|
function hash(value) {
|
|
10941
|
-
return
|
|
11155
|
+
return createHash10("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10942
11156
|
}
|
|
10943
11157
|
function validateSelection(selection) {
|
|
10944
11158
|
if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || selection.authorizationEpoch !== undefined && (typeof selection.authorizationEpoch !== "string" || !/^[a-f0-9]{32}$/.test(selection.authorizationEpoch)) || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
|
|
@@ -10978,7 +11192,7 @@ function selectionBundlePath(selection, options = {}) {
|
|
|
10978
11192
|
return join12(selectionCacheRoot(options), "objects", hash(selection.authority), hash(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
|
|
10979
11193
|
}
|
|
10980
11194
|
function assertRegularPath(path, createParents = false) {
|
|
10981
|
-
const absolute =
|
|
11195
|
+
const absolute = resolve9(path);
|
|
10982
11196
|
const root = parse4(absolute).root;
|
|
10983
11197
|
let cursor = root;
|
|
10984
11198
|
const segments = absolute.slice(root.length).split("/");
|
|
@@ -11035,7 +11249,7 @@ function writeSelectionJson(path, value) {
|
|
|
11035
11249
|
}
|
|
11036
11250
|
function atomicWrite(path, bytes, mode, durable = false) {
|
|
11037
11251
|
assertRegularPath(path, true);
|
|
11038
|
-
const temporary = join12(
|
|
11252
|
+
const temporary = join12(dirname9(path), `.selection-${randomUUID()}.tmp`);
|
|
11039
11253
|
try {
|
|
11040
11254
|
const fd = openSync3(temporary, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, mode);
|
|
11041
11255
|
try {
|
|
@@ -11047,7 +11261,7 @@ function atomicWrite(path, bytes, mode, durable = false) {
|
|
|
11047
11261
|
}
|
|
11048
11262
|
renameSync(temporary, path);
|
|
11049
11263
|
if (durable)
|
|
11050
|
-
syncDirectory(
|
|
11264
|
+
syncDirectory(dirname9(path));
|
|
11051
11265
|
} finally {
|
|
11052
11266
|
try {
|
|
11053
11267
|
unlinkSync(temporary);
|
|
@@ -11057,8 +11271,8 @@ function atomicWrite(path, bytes, mode, durable = false) {
|
|
|
11057
11271
|
}
|
|
11058
11272
|
}
|
|
11059
11273
|
}
|
|
11060
|
-
function readSelectionJson(path) {
|
|
11061
|
-
const bytes = readRegularFile(path, MAX_PROFILE_DOCUMENT_BYTES);
|
|
11274
|
+
function readSelectionJson(path, maximumBytes = MAX_PROFILE_DOCUMENT_BYTES) {
|
|
11275
|
+
const bytes = readRegularFile(path, Math.min(maximumBytes, MAX_PROFILE_DOCUMENT_BYTES));
|
|
11062
11276
|
if (!bytes)
|
|
11063
11277
|
return null;
|
|
11064
11278
|
try {
|
|
@@ -11070,10 +11284,10 @@ function readSelectionJson(path) {
|
|
|
11070
11284
|
async function verifiedEntries(selection, bytes, signal, deadline) {
|
|
11071
11285
|
if (`sha256:${sha256Hex(bytes)}` !== selection.bundleDigest)
|
|
11072
11286
|
throw new SkillSelectionError("BUNDLE_DIGEST_MISMATCH", "The skill bundle does not match its selected digest.");
|
|
11073
|
-
const remaining = deadline === undefined ? undefined : Math.
|
|
11287
|
+
const remaining = deadline === undefined ? undefined : Math.ceil(deadline - performance.now());
|
|
11074
11288
|
signal?.throwIfAborted();
|
|
11075
11289
|
if (remaining !== undefined && remaining <= 0)
|
|
11076
|
-
throw new
|
|
11290
|
+
throw new SkillBundleInspectionError("BUNDLE_TIMEOUT", "Bundle inspection deadline exceeded");
|
|
11077
11291
|
const bundle = await inspectSkillBundle(bytes, { signal, ...remaining === undefined ? {} : { limits: { timeoutMs: remaining } } });
|
|
11078
11292
|
return bundle.entries;
|
|
11079
11293
|
}
|
|
@@ -11179,7 +11393,7 @@ function assertFreshCachedProfile(receipt, options = {}) {
|
|
|
11179
11393
|
}
|
|
11180
11394
|
}
|
|
11181
11395
|
function projectSelectionLockPath(projectDir) {
|
|
11182
|
-
return join12(
|
|
11396
|
+
return join12(resolve9(projectDir), ".skills", SELECTION_LOCK_FILE);
|
|
11183
11397
|
}
|
|
11184
11398
|
function readProjectSelection(projectDir) {
|
|
11185
11399
|
const receipt = readSelectionJson(projectSelectionLockPath(projectDir));
|
|
@@ -11338,13 +11552,13 @@ function publishSessionLock(path, marker) {
|
|
|
11338
11552
|
fsyncSync(fd);
|
|
11339
11553
|
linkSync(stagingPath, path);
|
|
11340
11554
|
published = true;
|
|
11341
|
-
syncDirectory(
|
|
11555
|
+
syncDirectory(dirname9(path));
|
|
11342
11556
|
const staged = lstatSync8(stagingPath);
|
|
11343
11557
|
if (!staged.isFile() || staged.dev !== stat.dev || staged.ino !== stat.ino) {
|
|
11344
11558
|
throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "The initialized lock staging inode changed before publication completed.");
|
|
11345
11559
|
}
|
|
11346
11560
|
unlinkSync(stagingPath);
|
|
11347
|
-
syncDirectory(
|
|
11561
|
+
syncDirectory(dirname9(path));
|
|
11348
11562
|
handedOff = true;
|
|
11349
11563
|
return { path, stagingPath, fd, dev: stat.dev, ino: stat.ino };
|
|
11350
11564
|
} finally {
|
|
@@ -11389,14 +11603,14 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
11389
11603
|
writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
|
|
11390
11604
|
`);
|
|
11391
11605
|
fsyncSync(guardFd);
|
|
11392
|
-
const cacheOptions = { cacheDir:
|
|
11606
|
+
const cacheOptions = { cacheDir: dirname9(dirname9(lock.path)) };
|
|
11393
11607
|
const current = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
11394
11608
|
if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
|
|
11395
11609
|
return null;
|
|
11396
11610
|
const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
|
|
11397
11611
|
if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
|
|
11398
11612
|
return null;
|
|
11399
|
-
const directory = join12(
|
|
11613
|
+
const directory = join12(dirname9(dirname9(lock.path)), "session-lock-recoveries", randomUUID());
|
|
11400
11614
|
const archivePath = join12(directory, "original.write-lock");
|
|
11401
11615
|
const recordPath = join12(directory, "recovery.json");
|
|
11402
11616
|
assertRegularPath(archivePath, true);
|
|
@@ -11413,7 +11627,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
11413
11627
|
})}
|
|
11414
11628
|
`));
|
|
11415
11629
|
syncDirectory(directory);
|
|
11416
|
-
syncDirectory(
|
|
11630
|
+
syncDirectory(dirname9(directory));
|
|
11417
11631
|
const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
11418
11632
|
if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
|
|
11419
11633
|
return null;
|
|
@@ -11428,7 +11642,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
11428
11642
|
archivedPaths.push(publicationPath);
|
|
11429
11643
|
}
|
|
11430
11644
|
syncDirectory(directory);
|
|
11431
|
-
syncDirectory(
|
|
11645
|
+
syncDirectory(dirname9(lock.path));
|
|
11432
11646
|
for (const path of archivedPaths) {
|
|
11433
11647
|
const before = lstatSync8(path, { throwIfNoEntry: false });
|
|
11434
11648
|
const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
|
|
@@ -11598,7 +11812,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11598
11812
|
archiveBytes(receiptPath, Buffer.from(`${JSON.stringify(receipt)}
|
|
11599
11813
|
`));
|
|
11600
11814
|
syncDirectory(directory);
|
|
11601
|
-
syncDirectory(
|
|
11815
|
+
syncDirectory(dirname9(directory));
|
|
11602
11816
|
syncDirectory(selectionCacheRoot(options));
|
|
11603
11817
|
if (sha256Hex(readRegularFile(archivePath, MAX_PROFILE_DOCUMENT_BYTES)) !== before.sha256 || !sameSnapshot(readSkillSessionSnapshot(replacement.sessionId, options), expected)) {
|
|
11604
11818
|
throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The session or its preservation archive changed before replacement; retained evidence must be reviewed.");
|
|
@@ -11618,7 +11832,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11618
11832
|
// package.json
|
|
11619
11833
|
var package_default = {
|
|
11620
11834
|
name: "@hasna/skills",
|
|
11621
|
-
version: "0.10.
|
|
11835
|
+
version: "0.10.52",
|
|
11622
11836
|
description: "Skills library for AI coding agents",
|
|
11623
11837
|
type: "module",
|
|
11624
11838
|
bin: {
|
|
@@ -11755,12 +11969,12 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
11755
11969
|
import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
|
|
11756
11970
|
import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
|
|
11757
11971
|
import { hostname as osHostname } from "os";
|
|
11758
|
-
import { isAbsolute as
|
|
11972
|
+
import { isAbsolute as isAbsolute9, join as join22 } from "path";
|
|
11759
11973
|
import { createRequire } from "module";
|
|
11760
11974
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11761
11975
|
import { closeSync as closeSync4, fstatSync as fstatSync4, lstatSync as lstatSync9, openSync as openSync4, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync6 } from "fs";
|
|
11762
11976
|
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
11763
|
-
import { basename as
|
|
11977
|
+
import { basename as basename5, dirname as dirname10, join as join13, relative as relative3, sep as sep4 } from "path";
|
|
11764
11978
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
11765
11979
|
function envToken(name) {
|
|
11766
11980
|
return name.toUpperCase().replace(/-/g, "_");
|
|
@@ -11866,9 +12080,9 @@ function entryPoint(directory) {
|
|
|
11866
12080
|
return pathToFileURL(file).href;
|
|
11867
12081
|
}
|
|
11868
12082
|
function resolveInstalledSecrets(parentUrl) {
|
|
11869
|
-
let directory =
|
|
12083
|
+
let directory = dirname10(fileURLToPath(parentUrl));
|
|
11870
12084
|
for (;; ) {
|
|
11871
|
-
if (
|
|
12085
|
+
if (basename5(directory) !== "node_modules") {
|
|
11872
12086
|
const candidate = join13(directory, "node_modules", PACKAGE);
|
|
11873
12087
|
let absent = false;
|
|
11874
12088
|
try {
|
|
@@ -11881,7 +12095,7 @@ function resolveInstalledSecrets(parentUrl) {
|
|
|
11881
12095
|
if (!absent)
|
|
11882
12096
|
return entryPoint(candidate);
|
|
11883
12097
|
}
|
|
11884
|
-
const parent =
|
|
12098
|
+
const parent = dirname10(directory);
|
|
11885
12099
|
if (parent === directory)
|
|
11886
12100
|
throw new Error("Secrets SDK is not installed for this consumer");
|
|
11887
12101
|
directory = parent;
|
|
@@ -11901,8 +12115,8 @@ class CredentialResolutionError extends Error {
|
|
|
11901
12115
|
|
|
11902
12116
|
class CredentialFileUnsafeError extends Error {
|
|
11903
12117
|
path;
|
|
11904
|
-
constructor(path,
|
|
11905
|
-
super(`Refusing unsafe credential/config file ${path}: ${
|
|
12118
|
+
constructor(path, reason2) {
|
|
12119
|
+
super(`Refusing unsafe credential/config file ${path}: ${reason2}.`);
|
|
11906
12120
|
this.name = "CredentialFileUnsafeError";
|
|
11907
12121
|
this.path = path;
|
|
11908
12122
|
}
|
|
@@ -11928,7 +12142,7 @@ function homeDir(env) {
|
|
|
11928
12142
|
}
|
|
11929
12143
|
function absoluteOverride(env, key) {
|
|
11930
12144
|
const value = env[key]?.trim();
|
|
11931
|
-
return value &&
|
|
12145
|
+
return value && isAbsolute9(value) ? value : null;
|
|
11932
12146
|
}
|
|
11933
12147
|
function hasnaHomeDir(env) {
|
|
11934
12148
|
const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
|
|
@@ -11974,9 +12188,9 @@ function parseEnvFile(text2) {
|
|
|
11974
12188
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
11975
12189
|
continue;
|
|
11976
12190
|
let value = withoutExport.slice(equals + 1).trim();
|
|
11977
|
-
const
|
|
11978
|
-
if (
|
|
11979
|
-
if (value.length < 2 || !value.endsWith(
|
|
12191
|
+
const quote2 = value[0];
|
|
12192
|
+
if (quote2 === '"' || quote2 === "'") {
|
|
12193
|
+
if (value.length < 2 || !value.endsWith(quote2)) {
|
|
11980
12194
|
unusable.add(key);
|
|
11981
12195
|
continue;
|
|
11982
12196
|
}
|
|
@@ -12000,8 +12214,8 @@ function configFileReadsCoherent(before, after) {
|
|
|
12000
12214
|
return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs;
|
|
12001
12215
|
}
|
|
12002
12216
|
function readAppConfigFile(path) {
|
|
12003
|
-
const unsafe = (
|
|
12004
|
-
throw new CredentialFileUnsafeError(path,
|
|
12217
|
+
const unsafe = (reason2) => {
|
|
12218
|
+
throw new CredentialFileUnsafeError(path, reason2);
|
|
12005
12219
|
};
|
|
12006
12220
|
let fd = -1;
|
|
12007
12221
|
try {
|
|
@@ -12197,8 +12411,8 @@ function readKeychainItem(name, env, kind, options) {
|
|
|
12197
12411
|
try {
|
|
12198
12412
|
result = run(["find-generic-password", "-a", account, "-s", service, "-w"]);
|
|
12199
12413
|
} catch (error) {
|
|
12200
|
-
const
|
|
12201
|
-
throw new CredentialResolutionError(name, `The Keychain lookup for ${source} could not run${
|
|
12414
|
+
const reason2 = keychainFailureHint(error instanceof Error ? error.message : String(error));
|
|
12415
|
+
throw new CredentialResolutionError(name, `The Keychain lookup for ${source} could not run${reason2}. A Keychain failure is never resolved ` + `around: fix the keychain, or delete the item to fall through to the credential on disk.`, [source]);
|
|
12202
12416
|
}
|
|
12203
12417
|
if (result.status === KEYCHAIN_ITEM_NOT_FOUND_STATUS)
|
|
12204
12418
|
return null;
|
|
@@ -13016,7 +13230,7 @@ class MissingSkillsFleetError extends Error {
|
|
|
13016
13230
|
}
|
|
13017
13231
|
|
|
13018
13232
|
// src/lib/remote-files.ts
|
|
13019
|
-
import { createHash as
|
|
13233
|
+
import { createHash as createHash11 } from "crypto";
|
|
13020
13234
|
var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
13021
13235
|
function describeRemoteFiles(files) {
|
|
13022
13236
|
if (files.length > 10)
|
|
@@ -13034,7 +13248,7 @@ function describeRemoteFiles(files) {
|
|
|
13034
13248
|
});
|
|
13035
13249
|
}
|
|
13036
13250
|
function sha256(bytes) {
|
|
13037
|
-
return
|
|
13251
|
+
return createHash11("sha256").update(bytes).digest("hex");
|
|
13038
13252
|
}
|
|
13039
13253
|
async function readBoundedResponse(response, maximum, signal) {
|
|
13040
13254
|
if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
|
|
@@ -13092,6 +13306,10 @@ function identifier(value) {
|
|
|
13092
13306
|
function invalid() {
|
|
13093
13307
|
throw new Error("The Skills API returned an invalid profile response");
|
|
13094
13308
|
}
|
|
13309
|
+
function unavailable(error, message) {
|
|
13310
|
+
const name = error?.name;
|
|
13311
|
+
return new SkillSelectionError("SKILLS_API_UNAVAILABLE", message, name === "AbortError" || name === "TimeoutError" ? { cause: new DOMException("The Skills API request was cut short", name) } : undefined);
|
|
13312
|
+
}
|
|
13095
13313
|
function validateSelection2(value) {
|
|
13096
13314
|
if (!object3(value) || !identifier(value.slug) || typeof value.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(value.version) || value.version.includes("..") || typeof value.bundleDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.bundleDigest))
|
|
13097
13315
|
invalid();
|
|
@@ -13139,8 +13357,8 @@ class HttpProfileClient {
|
|
|
13139
13357
|
...init.headers
|
|
13140
13358
|
}
|
|
13141
13359
|
});
|
|
13142
|
-
} catch {
|
|
13143
|
-
throw
|
|
13360
|
+
} catch (error) {
|
|
13361
|
+
throw unavailable(error, "Unable to reach the configured Skills API");
|
|
13144
13362
|
}
|
|
13145
13363
|
if (!response.ok) {
|
|
13146
13364
|
response.body?.cancel().catch(() => {});
|
|
@@ -13157,7 +13375,9 @@ class HttpProfileClient {
|
|
|
13157
13375
|
async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
|
|
13158
13376
|
try {
|
|
13159
13377
|
return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit, this.signal)));
|
|
13160
|
-
} catch {
|
|
13378
|
+
} catch (error) {
|
|
13379
|
+
if (this.signal?.aborted)
|
|
13380
|
+
throw unavailable(this.signal.reason ?? error, "The Skills API response was cut short");
|
|
13161
13381
|
invalid();
|
|
13162
13382
|
}
|
|
13163
13383
|
}
|
|
@@ -13244,17 +13464,17 @@ import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSyn
|
|
|
13244
13464
|
import { join as join14 } from "path";
|
|
13245
13465
|
|
|
13246
13466
|
// src/lib/preparation-process.ts
|
|
13247
|
-
function capturePreparationProcess(
|
|
13467
|
+
function capturePreparationProcess(child2, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
|
|
13248
13468
|
let spawnedPid;
|
|
13249
13469
|
try {
|
|
13250
|
-
spawnedPid =
|
|
13470
|
+
spawnedPid = child2.pid;
|
|
13251
13471
|
} catch {}
|
|
13252
13472
|
const groupId = typeof spawnedPid === "number" && Number.isSafeInteger(spawnedPid) && spawnedPid > 1 ? spawnedPid : undefined;
|
|
13253
13473
|
const canSignalGroup = platform !== "win32" && groupId !== undefined;
|
|
13254
13474
|
const killLiveChild = () => {
|
|
13255
|
-
if (
|
|
13475
|
+
if (child2.exitCode === null && child2.signalCode === null) {
|
|
13256
13476
|
try {
|
|
13257
|
-
|
|
13477
|
+
child2.kill("SIGKILL");
|
|
13258
13478
|
} catch {}
|
|
13259
13479
|
}
|
|
13260
13480
|
};
|
|
@@ -13453,20 +13673,20 @@ import { join as join28 } from "path";
|
|
|
13453
13673
|
// src/lib/codex-corpus-write.ts
|
|
13454
13674
|
import { KernelLock } from "@hasna/contracts/kernel-lock";
|
|
13455
13675
|
import { execFileSync } from "child_process";
|
|
13456
|
-
import { createHash as
|
|
13676
|
+
import { createHash as createHash12 } from "crypto";
|
|
13457
13677
|
import { accessSync, constants as constants6, lstatSync as lstatSync12, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
|
|
13458
|
-
import { basename as
|
|
13678
|
+
import { basename as basename6, delimiter, dirname as dirname11, isAbsolute as isAbsolute10, join as join15, relative as relative4, resolve as resolve10, sep as sep5 } from "path";
|
|
13459
13679
|
var KEY = ".native-corpus-admission";
|
|
13460
13680
|
var fail = () => {
|
|
13461
13681
|
throw new Error("CODEX_CORPUS_ADMISSION_REQUIRED: verified native admission and its existing shared lock are required");
|
|
13462
13682
|
};
|
|
13463
13683
|
function physicalTarget(path) {
|
|
13464
13684
|
const suffix = [];
|
|
13465
|
-
for (let cursor = path;; cursor =
|
|
13685
|
+
for (let cursor = path;; cursor = dirname11(cursor)) {
|
|
13466
13686
|
try {
|
|
13467
13687
|
return join15(realpathSync3(cursor), ...suffix);
|
|
13468
13688
|
} catch (error) {
|
|
13469
|
-
if (error.code !== "ENOENT" || cursor ===
|
|
13689
|
+
if (error.code !== "ENOENT" || cursor === dirname11(cursor))
|
|
13470
13690
|
throw error;
|
|
13471
13691
|
try {
|
|
13472
13692
|
if (lstatSync12(cursor).isSymbolicLink())
|
|
@@ -13475,14 +13695,14 @@ function physicalTarget(path) {
|
|
|
13475
13695
|
if (statError.code !== "ENOENT")
|
|
13476
13696
|
throw statError;
|
|
13477
13697
|
}
|
|
13478
|
-
suffix.unshift(
|
|
13698
|
+
suffix.unshift(basename6(cursor));
|
|
13479
13699
|
}
|
|
13480
13700
|
}
|
|
13481
13701
|
}
|
|
13482
13702
|
function codexCorpusRootForPath(path) {
|
|
13483
|
-
const target =
|
|
13703
|
+
const target = resolve10(path), physical = physicalTarget(target);
|
|
13484
13704
|
const selected = process.env.CODEX_HOME;
|
|
13485
|
-
const selectedRoots = selected &&
|
|
13705
|
+
const selectedRoots = selected && isAbsolute10(selected) ? [resolve10(selected), physicalTarget(resolve10(selected))] : [];
|
|
13486
13706
|
const classify = (candidate) => {
|
|
13487
13707
|
const checked = (root2) => {
|
|
13488
13708
|
if (candidate === root2 || candidate === join15(root2, `${KEY}.flock-v1`))
|
|
@@ -13491,10 +13711,10 @@ function codexCorpusRootForPath(path) {
|
|
|
13491
13711
|
};
|
|
13492
13712
|
for (const root2 of selectedRoots) {
|
|
13493
13713
|
const distance = relative4(root2, candidate);
|
|
13494
|
-
if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !
|
|
13714
|
+
if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !isAbsolute10(distance))
|
|
13495
13715
|
return checked(root2);
|
|
13496
13716
|
}
|
|
13497
|
-
for (let cursor = candidate;cursor !==
|
|
13717
|
+
for (let cursor = candidate;cursor !== dirname11(cursor); cursor = dirname11(cursor)) {
|
|
13498
13718
|
if (cursor.endsWith(`${sep5}.codex`))
|
|
13499
13719
|
return checked(cursor);
|
|
13500
13720
|
}
|
|
@@ -13520,10 +13740,10 @@ function executable(options) {
|
|
|
13520
13740
|
if (typeof command !== "string" || !command || command.includes("\x00") || options.codexSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.codexSha256))
|
|
13521
13741
|
return fail();
|
|
13522
13742
|
let found;
|
|
13523
|
-
if (
|
|
13743
|
+
if (isAbsolute10(command))
|
|
13524
13744
|
found = command;
|
|
13525
13745
|
else if (!command.includes(sep5)) {
|
|
13526
|
-
found = (process.env.PATH ?? "").split(delimiter).filter(
|
|
13746
|
+
found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute10).map((dir) => join15(dir, command)).find((candidate) => {
|
|
13527
13747
|
try {
|
|
13528
13748
|
accessSync(candidate, constants6.X_OK);
|
|
13529
13749
|
return true;
|
|
@@ -13537,7 +13757,7 @@ function executable(options) {
|
|
|
13537
13757
|
const path = realpathSync3(found), initial = statSync7(path, { bigint: true });
|
|
13538
13758
|
if (!initial.isFile() || (initial.mode & 0o022n) !== 0n || (initial.mode & 0o111n) === 0n || initial.uid !== BigInt(process.getuid()) && initial.uid !== 0n)
|
|
13539
13759
|
return fail();
|
|
13540
|
-
const hash2 = () =>
|
|
13760
|
+
const hash2 = () => createHash12("sha256").update(readFileSync11(path)).digest("hex");
|
|
13541
13761
|
const digest = hash2();
|
|
13542
13762
|
if (options.codexSha256 && digest !== options.codexSha256)
|
|
13543
13763
|
return fail();
|
|
@@ -13561,7 +13781,7 @@ function acquireCodexCorpusWrite(roots, options) {
|
|
|
13561
13781
|
}
|
|
13562
13782
|
};
|
|
13563
13783
|
try {
|
|
13564
|
-
if (roots.some((root) => typeof root !== "string" || !
|
|
13784
|
+
if (roots.some((root) => typeof root !== "string" || !isAbsolute10(root) || root.includes("\x00")))
|
|
13565
13785
|
fail();
|
|
13566
13786
|
const inputs = [...new Set(roots)].map((input) => ({ input, root: realpathSync3(input) })).sort((a, b) => a.root.localeCompare(b.root));
|
|
13567
13787
|
for (const { input, root } of inputs) {
|
|
@@ -13631,10 +13851,10 @@ import {
|
|
|
13631
13851
|
writeFileSync as writeFileSync7
|
|
13632
13852
|
} from "fs";
|
|
13633
13853
|
import { homedir as homedir3 } from "os";
|
|
13634
|
-
import { basename as
|
|
13854
|
+
import { basename as basename8, dirname as dirname16, join as join25 } from "path";
|
|
13635
13855
|
|
|
13636
13856
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13637
|
-
import { createHash as
|
|
13857
|
+
import { createHash as createHash13 } from "crypto";
|
|
13638
13858
|
|
|
13639
13859
|
// src/lib/codex-local-mcp-controls.ts
|
|
13640
13860
|
function isCodexLocalStdioMcpServer(name, server) {
|
|
@@ -13703,24 +13923,24 @@ function isCodexLocalStdioMcpServer(name, server) {
|
|
|
13703
13923
|
}
|
|
13704
13924
|
|
|
13705
13925
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13706
|
-
import { basename as
|
|
13926
|
+
import { basename as basename7, dirname as dirname12, join as join17, relative as relative5, resolve as resolve11, sep as sep6 } from "path";
|
|
13707
13927
|
import { lstatSync as lstatSync13, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync6 } from "fs";
|
|
13708
13928
|
|
|
13709
13929
|
// src/lib/codex-native-skill-catalog.ts
|
|
13710
13930
|
import { realpathSync as realpathSync5 } from "fs";
|
|
13711
|
-
import { isAbsolute as
|
|
13931
|
+
import { isAbsolute as isAbsolute12, normalize as normalize2 } from "path";
|
|
13712
13932
|
|
|
13713
13933
|
// src/lib/codex-hook-rpc.ts
|
|
13714
13934
|
import { join as join16 } from "path";
|
|
13715
13935
|
import { execFile, spawn } from "child_process";
|
|
13716
13936
|
import { realpathSync as realpathSync4, statSync as statSync8 } from "fs";
|
|
13717
|
-
import { isAbsolute as
|
|
13937
|
+
import { isAbsolute as isAbsolute11 } from "path";
|
|
13718
13938
|
import { promisify } from "util";
|
|
13719
13939
|
async function connectCodexHookRpc(options) {
|
|
13720
13940
|
const timeoutMs = options.timeoutMs ?? 20000;
|
|
13721
13941
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 20000)
|
|
13722
13942
|
throw new Error("CODEX_HOOK_TRUST_INVALID_TIMEOUT");
|
|
13723
|
-
const found =
|
|
13943
|
+
const found = isAbsolute11(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
|
|
13724
13944
|
if (!found)
|
|
13725
13945
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE: install Codex or pass --codex-command");
|
|
13726
13946
|
const binary = realpathSync4(found), stat = statSync8(binary);
|
|
@@ -13744,16 +13964,16 @@ async function connectCodexHookRpc(options) {
|
|
|
13744
13964
|
else
|
|
13745
13965
|
env.CODEX_HOME = options.codexHome;
|
|
13746
13966
|
const corpusLease = acquireCodexCorpusWrite([options.codexHome ?? join16(options.home, ".codex")], { codexCommand: binary });
|
|
13747
|
-
const
|
|
13967
|
+
const child2 = spawn(binary, ["app-server", "--strict-config", "--stdio"], {
|
|
13748
13968
|
cwd: options.home,
|
|
13749
13969
|
env,
|
|
13750
13970
|
stdio: ["pipe", "pipe", "pipe"]
|
|
13751
13971
|
});
|
|
13752
13972
|
let childClosed = false;
|
|
13753
|
-
const actualClose = new Promise((
|
|
13973
|
+
const actualClose = new Promise((resolve11) => child2.once("close", () => {
|
|
13754
13974
|
childClosed = true;
|
|
13755
13975
|
corpusLease.close();
|
|
13756
|
-
|
|
13976
|
+
resolve11();
|
|
13757
13977
|
}));
|
|
13758
13978
|
const pending = new Map;
|
|
13759
13979
|
let nextId = 1, buffer = Buffer.alloc(0), stopped = false, diagnosticBytes = 0;
|
|
@@ -13765,20 +13985,20 @@ async function connectCodexHookRpc(options) {
|
|
|
13765
13985
|
}
|
|
13766
13986
|
pending.clear();
|
|
13767
13987
|
};
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13988
|
+
child2.on("error", fail2);
|
|
13989
|
+
child2.on("exit", fail2);
|
|
13990
|
+
child2.stdin.on("error", fail2);
|
|
13991
|
+
child2.stderr.on("data", (bytes) => {
|
|
13772
13992
|
diagnosticBytes += bytes.length;
|
|
13773
13993
|
if (diagnosticBytes > 2 * 1024 * 1024) {
|
|
13774
13994
|
fail2();
|
|
13775
|
-
|
|
13995
|
+
child2.kill();
|
|
13776
13996
|
}
|
|
13777
13997
|
});
|
|
13778
|
-
|
|
13998
|
+
child2.stdout.on("data", (bytes) => {
|
|
13779
13999
|
if (buffer.length + bytes.length > 8 * 1024 * 1024) {
|
|
13780
14000
|
fail2();
|
|
13781
|
-
|
|
14001
|
+
child2.kill();
|
|
13782
14002
|
return;
|
|
13783
14003
|
}
|
|
13784
14004
|
buffer = Buffer.concat([buffer, bytes]);
|
|
@@ -13793,12 +14013,12 @@ async function connectCodexHookRpc(options) {
|
|
|
13793
14013
|
value = JSON.parse(line.toString("utf8"));
|
|
13794
14014
|
} catch {
|
|
13795
14015
|
fail2();
|
|
13796
|
-
|
|
14016
|
+
child2.kill();
|
|
13797
14017
|
return;
|
|
13798
14018
|
}
|
|
13799
14019
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
13800
14020
|
fail2();
|
|
13801
|
-
|
|
14021
|
+
child2.kill();
|
|
13802
14022
|
return;
|
|
13803
14023
|
}
|
|
13804
14024
|
const waiting = pending.get(value.id);
|
|
@@ -13814,29 +14034,29 @@ async function connectCodexHookRpc(options) {
|
|
|
13814
14034
|
});
|
|
13815
14035
|
const rpc = {
|
|
13816
14036
|
version,
|
|
13817
|
-
processId:
|
|
14037
|
+
processId: child2.pid,
|
|
13818
14038
|
request(method, params) {
|
|
13819
14039
|
if (stopped)
|
|
13820
14040
|
return Promise.reject(new Error("CODEX_HOOK_TRUST_NATIVE_RPC_CLOSED"));
|
|
13821
|
-
return new Promise((
|
|
14041
|
+
return new Promise((resolve11, reject) => {
|
|
13822
14042
|
const id = nextId++;
|
|
13823
14043
|
const timer = setTimeout(() => {
|
|
13824
14044
|
fail2();
|
|
13825
|
-
|
|
14045
|
+
child2.kill();
|
|
13826
14046
|
}, timeoutMs);
|
|
13827
|
-
pending.set(id, { resolve:
|
|
13828
|
-
|
|
14047
|
+
pending.set(id, { resolve: resolve11, reject, timer });
|
|
14048
|
+
child2.stdin.write(JSON.stringify({ id, method, params }) + `
|
|
13829
14049
|
`);
|
|
13830
14050
|
});
|
|
13831
14051
|
},
|
|
13832
14052
|
async close() {
|
|
13833
14053
|
fail2();
|
|
13834
|
-
|
|
14054
|
+
child2.stdin.end();
|
|
13835
14055
|
if (childClosed)
|
|
13836
14056
|
return;
|
|
13837
|
-
await new Promise((
|
|
14057
|
+
await new Promise((resolve11, reject) => {
|
|
13838
14058
|
const terminate = setTimeout(() => {
|
|
13839
|
-
|
|
14059
|
+
child2.kill();
|
|
13840
14060
|
}, 1000);
|
|
13841
14061
|
const deadline = setTimeout(() => {
|
|
13842
14062
|
clearTimeout(terminate);
|
|
@@ -13845,7 +14065,7 @@ async function connectCodexHookRpc(options) {
|
|
|
13845
14065
|
actualClose.then(() => {
|
|
13846
14066
|
clearTimeout(terminate);
|
|
13847
14067
|
clearTimeout(deadline);
|
|
13848
|
-
|
|
14068
|
+
resolve11();
|
|
13849
14069
|
});
|
|
13850
14070
|
});
|
|
13851
14071
|
}
|
|
@@ -13855,7 +14075,7 @@ async function connectCodexHookRpc(options) {
|
|
|
13855
14075
|
const nativeVersion = version.slice("codex-cli ".length);
|
|
13856
14076
|
if (typeof initialized?.userAgent !== "string" || !(initialized.userAgent === `skills-native-hook-enrollment/${nativeVersion}` || initialized.userAgent.startsWith(`skills-native-hook-enrollment/${nativeVersion} `)))
|
|
13857
14077
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_VERSION_MISMATCH");
|
|
13858
|
-
|
|
14078
|
+
child2.stdin.write(`{"method":"initialized"}
|
|
13859
14079
|
`);
|
|
13860
14080
|
return rpc;
|
|
13861
14081
|
} catch {
|
|
@@ -13879,7 +14099,7 @@ function refuse() {
|
|
|
13879
14099
|
var object4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13880
14100
|
var scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value);
|
|
13881
14101
|
var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
|
|
13882
|
-
var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) &&
|
|
14102
|
+
var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute12(value);
|
|
13883
14103
|
function projectPluginSource(source) {
|
|
13884
14104
|
if (!object4(source))
|
|
13885
14105
|
return {};
|
|
@@ -14078,7 +14298,7 @@ function mcpControls(path, read2) {
|
|
|
14078
14298
|
function isBundledCleanupHooks(root, namespace, value) {
|
|
14079
14299
|
const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
14080
14300
|
const exactKeys = (v, keys) => Object.keys(v).length === keys.length && keys.every((key) => Object.hasOwn(v, key));
|
|
14081
|
-
if (
|
|
14301
|
+
if (basename7(dirname12(root)) !== namespace || basename7(dirname12(dirname12(root))) !== "openai-bundled")
|
|
14082
14302
|
return false;
|
|
14083
14303
|
const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
|
|
14084
14304
|
if (!server || !object5(value) || !exactKeys(value, ["hooks"]) || !object5(value.hooks))
|
|
@@ -14112,7 +14332,7 @@ function rootControls(root, read2) {
|
|
|
14112
14332
|
}
|
|
14113
14333
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
14114
14334
|
refuse2();
|
|
14115
|
-
const cacheVersion =
|
|
14335
|
+
const cacheVersion = basename7(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
14116
14336
|
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
14117
14337
|
refuse2();
|
|
14118
14338
|
if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync13(join17(root, "hooks"), { throwIfNoEntry: false }))
|
|
@@ -14157,7 +14377,7 @@ function documentIdentity(document2, cache, read2) {
|
|
|
14157
14377
|
}
|
|
14158
14378
|
if (!front || typeof front !== "object" || !identifier2(front.name) || typeof front.description !== "string" || !front.description.trim())
|
|
14159
14379
|
refuse2();
|
|
14160
|
-
for (let root =
|
|
14380
|
+
for (let root = dirname12(document2), depth = 0;root.startsWith(cache + sep6) && depth < 12; root = dirname12(root), depth++) {
|
|
14161
14381
|
const path = join17(root, ".codex-plugin", "plugin.json");
|
|
14162
14382
|
if (!lstatSync13(path, { throwIfNoEntry: false }))
|
|
14163
14383
|
continue;
|
|
@@ -14175,9 +14395,9 @@ function documentIdentity(document2, cache, read2) {
|
|
|
14175
14395
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
14176
14396
|
refuse2();
|
|
14177
14397
|
const declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
14178
|
-
if (declaredVersion === null || (declaredVersion ? declaredVersion !==
|
|
14398
|
+
if (declaredVersion === null || (declaredVersion ? declaredVersion !== basename7(root) : basename7(root) !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
14179
14399
|
refuse2();
|
|
14180
|
-
return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent:
|
|
14400
|
+
return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent: dirname12(root), root };
|
|
14181
14401
|
}
|
|
14182
14402
|
refuse2();
|
|
14183
14403
|
}
|
|
@@ -14206,13 +14426,13 @@ function classifyCodexPluginCacheDocument(document2, cache, read2) {
|
|
|
14206
14426
|
function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
14207
14427
|
try {
|
|
14208
14428
|
const parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
|
|
14209
|
-
if (
|
|
14429
|
+
if (resolve11(parent) !== parent || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !enrolled.length || !Array.isArray(rules))
|
|
14210
14430
|
return false;
|
|
14211
|
-
for (let cursor = parent;; cursor =
|
|
14431
|
+
for (let cursor = parent;; cursor = dirname12(cursor)) {
|
|
14212
14432
|
const stat = lstatSync13(cursor, { throwIfNoEntry: false });
|
|
14213
14433
|
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
14214
14434
|
return false;
|
|
14215
|
-
if (
|
|
14435
|
+
if (dirname12(cursor) === cursor)
|
|
14216
14436
|
break;
|
|
14217
14437
|
}
|
|
14218
14438
|
return enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path.startsWith(parent + sep6))) && rule.enabled !== false));
|
|
@@ -14225,14 +14445,14 @@ function absentDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
|
14225
14445
|
}
|
|
14226
14446
|
function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbsent = false) {
|
|
14227
14447
|
try {
|
|
14228
|
-
const parent =
|
|
14229
|
-
if (
|
|
14448
|
+
const parent = dirname12(root), parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
|
|
14449
|
+
if (resolve11(root) !== root || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !basename7(root) || basename7(root) === "latest" || !enrolled.length || !Array.isArray(rules))
|
|
14230
14450
|
return false;
|
|
14231
|
-
for (let cursor = root;; cursor =
|
|
14451
|
+
for (let cursor = root;; cursor = dirname12(cursor)) {
|
|
14232
14452
|
const stat = lstatSync13(cursor, { throwIfNoEntry: false });
|
|
14233
14453
|
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
14234
14454
|
return false;
|
|
14235
|
-
if (
|
|
14455
|
+
if (dirname12(cursor) === cursor)
|
|
14236
14456
|
break;
|
|
14237
14457
|
}
|
|
14238
14458
|
if (!enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path === root || rule.path.startsWith(root + sep6))) && rule.enabled !== false)))
|
|
@@ -14279,8 +14499,8 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
|
|
|
14279
14499
|
for (const name of names) {
|
|
14280
14500
|
let root = join17(parent, name), stat = lstatSync13(root);
|
|
14281
14501
|
if (stat.isSymbolicLink()) {
|
|
14282
|
-
const target =
|
|
14283
|
-
if (name !== "latest" ||
|
|
14502
|
+
const target = resolve11(parent, readlinkSync3(root));
|
|
14503
|
+
if (name !== "latest" || dirname12(target) !== parent || !lstatSync13(target).isDirectory() || realpathSync6(target) !== target)
|
|
14284
14504
|
return false;
|
|
14285
14505
|
root = target;
|
|
14286
14506
|
stat = lstatSync13(root);
|
|
@@ -14332,7 +14552,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14332
14552
|
if (listed.length > 1)
|
|
14333
14553
|
refuse2();
|
|
14334
14554
|
const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
|
|
14335
|
-
if (installedPluginReview && installed.length === 1 && installed[0].installed && installed[0].localVersion ===
|
|
14555
|
+
if (installedPluginReview && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename7(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
|
|
14336
14556
|
const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
|
|
14337
14557
|
if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache + sep6) && (lstatSync13(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep6)))) {
|
|
14338
14558
|
if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep6)) || !sourceInputUnchanged(input, cache, read2))
|
|
@@ -14364,7 +14584,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14364
14584
|
refuse2();
|
|
14365
14585
|
const plugin = installed[0];
|
|
14366
14586
|
if (plugin.localVersion !== null) {
|
|
14367
|
-
if (plugin.localVersion !==
|
|
14587
|
+
if (plugin.localVersion !== basename7(dirname12(dirname12(dirname12(document2)))))
|
|
14368
14588
|
continue;
|
|
14369
14589
|
} else {
|
|
14370
14590
|
if (plugin.sourceType !== "remote" || !plugin.remotePluginId || !remoteInstallationMatches(parsed.pluginParent, plugin.remotePluginId, read2) || !lstatSync13(document2).isFile() || realpathSync6(document2) !== document2)
|
|
@@ -14374,7 +14594,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14374
14594
|
const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
|
|
14375
14595
|
const nativeSkill = { name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true };
|
|
14376
14596
|
const denial = denials.find((item) => item.path === document2);
|
|
14377
|
-
const explicitDeny = denial !== undefined && remotePluginId !== undefined && installedPluginReview && denial.name === parsed.name && denial.sha256 ===
|
|
14597
|
+
const explicitDeny = denial !== undefined && remotePluginId !== undefined && installedPluginReview && denial.name === parsed.name && denial.sha256 === createHash13("sha256").update(read2(document2)).digest("hex") && Array.isArray(rules) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false);
|
|
14378
14598
|
if (denial && !explicitDeny)
|
|
14379
14599
|
refuse2();
|
|
14380
14600
|
if (explicitDeny)
|
|
@@ -14395,7 +14615,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14395
14615
|
function sourceInputUnchanged(input, cache, read2) {
|
|
14396
14616
|
try {
|
|
14397
14617
|
const parts = relative5(cache, input.pluginParent).split(sep6);
|
|
14398
|
-
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== input.namespace || input.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input.pluginId, sourceType: "local", sourceSha256: input.sourceSha256 }) || input.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input.sourceRoot })) ||
|
|
14618
|
+
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== input.namespace || input.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input.pluginId, sourceType: "local", sourceSha256: input.sourceSha256 }) || input.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input.sourceRoot })) || resolve11(input.sourceRoot) !== input.sourceRoot || !lstatSync13(input.sourceRoot).isDirectory() || realpathSync6(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache || input.sourceRoot.startsWith(cache + sep6) || cache.startsWith(input.sourceRoot + sep6))
|
|
14399
14619
|
return false;
|
|
14400
14620
|
const path = join17(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync13(path);
|
|
14401
14621
|
if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync6(path) !== path)
|
|
@@ -14461,9 +14681,9 @@ enabled = false
|
|
|
14461
14681
|
}
|
|
14462
14682
|
|
|
14463
14683
|
// src/lib/codex-native-skill-policy.ts
|
|
14464
|
-
import { createHash as
|
|
14684
|
+
import { createHash as createHash14, randomUUID as randomUUID2 } from "crypto";
|
|
14465
14685
|
import { closeSync as closeSync7, constants as constants7, fstatSync as fstatSync7, fsyncSync as fsyncSync2, lstatSync as lstatSync14, mkdirSync as mkdirSync5, openSync as openSync7, readFileSync as readFileSync12, readSync as readSync5, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
14466
|
-
import { dirname as
|
|
14686
|
+
import { dirname as dirname13, isAbsolute as isAbsolute13, join as join18, resolve as resolve12 } from "path";
|
|
14467
14687
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
14468
14688
|
var CODEX_NATIVE_POLICY_CAPABILITY = "host-path-allowlist-v1";
|
|
14469
14689
|
var CODEX_NATIVE_POLICY_RECEIPT_SCHEMA = "skills.codex-native-policy-acceptance/v1";
|
|
@@ -14486,9 +14706,9 @@ function refuse3(code, detail) {
|
|
|
14486
14706
|
throw new Error(`${code}: ${detail}`);
|
|
14487
14707
|
}
|
|
14488
14708
|
var object5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
14489
|
-
function boundedId(value,
|
|
14709
|
+
function boundedId(value, field2) {
|
|
14490
14710
|
if (typeof value !== "string" || !value || value.length > MAX_ENVELOPE_ID_LENGTH || value.trim() !== value || /[\u0000-\u001f\u007f]/.test(value))
|
|
14491
|
-
refuse3("NATIVE_SKILL_POLICY_INVALID", `${
|
|
14711
|
+
refuse3("NATIVE_SKILL_POLICY_INVALID", `${field2} must be a bounded non-empty string`);
|
|
14492
14712
|
return value;
|
|
14493
14713
|
}
|
|
14494
14714
|
function parseCodexNativePolicyTrust(value) {
|
|
@@ -14511,9 +14731,9 @@ function parseCodexNativeHookEnvelope(envelope, bridgeDocument) {
|
|
|
14511
14731
|
if (!object5(value))
|
|
14512
14732
|
refuse3("NATIVE_SKILL_POLICY_INVALID", "native_skill_policy must be an object");
|
|
14513
14733
|
const keys = Object.keys(value);
|
|
14514
|
-
for (const
|
|
14515
|
-
if (!Object.hasOwn(value,
|
|
14516
|
-
refuse3("NATIVE_SKILL_POLICY_INVALID", `native_skill_policy.${
|
|
14734
|
+
for (const field2 of POLICY_FIELDS)
|
|
14735
|
+
if (!Object.hasOwn(value, field2))
|
|
14736
|
+
refuse3("NATIVE_SKILL_POLICY_INVALID", `native_skill_policy.${field2} is missing`);
|
|
14517
14737
|
if (keys.length !== POLICY_FIELDS.length)
|
|
14518
14738
|
refuse3("NATIVE_SKILL_POLICY_INVALID", "native_skill_policy carries unknown fields");
|
|
14519
14739
|
if (value.capability !== CODEX_NATIVE_POLICY_CAPABILITY)
|
|
@@ -14577,19 +14797,19 @@ function readBounded(path, maximum, what) {
|
|
|
14577
14797
|
}
|
|
14578
14798
|
}
|
|
14579
14799
|
function verifyCodexNativeBridgeDocument(path, expectedContent, expectedSha256) {
|
|
14580
|
-
if (!
|
|
14800
|
+
if (!isAbsolute13(path) || resolve12(path) !== path || !path.endsWith("/SKILL.md"))
|
|
14581
14801
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document path is not an exact absolute SKILL.md path");
|
|
14582
|
-
for (let cursor = path;; cursor =
|
|
14802
|
+
for (let cursor = path;; cursor = dirname13(cursor)) {
|
|
14583
14803
|
const stat = lstatSync14(cursor, { throwIfNoEntry: false });
|
|
14584
14804
|
if (!stat || stat.isSymbolicLink() || (cursor === path ? !stat.isFile() : !stat.isDirectory()))
|
|
14585
14805
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", `the bridge path has a missing, linked or unexpected component: ${cursor}`);
|
|
14586
|
-
if (
|
|
14806
|
+
if (dirname13(cursor) === cursor)
|
|
14587
14807
|
break;
|
|
14588
14808
|
}
|
|
14589
14809
|
if (realpathSync7.native(path) !== path)
|
|
14590
14810
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document real path differs from its lexical path");
|
|
14591
14811
|
const bytes = readBounded(path, MAX_BRIDGE_DOCUMENT_BYTES, "the bridge document");
|
|
14592
|
-
const sha2562 =
|
|
14812
|
+
const sha2562 = createHash14("sha256").update(bytes).digest("hex");
|
|
14593
14813
|
if (!bytes.equals(Buffer.from(expectedContent, "utf8")) || sha2562 !== expectedSha256)
|
|
14594
14814
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document bytes differ from the managed bridge");
|
|
14595
14815
|
return { path, sha256: sha2562 };
|
|
@@ -14655,13 +14875,13 @@ function assertExecutableWitnessStat(stat, currentUid) {
|
|
|
14655
14875
|
return { dev: String(stat.dev), ino: String(stat.ino), size: String(stat.size), mtimeNs: String(stat.mtimeNs), ctimeNs: String(stat.ctimeNs), uid: String(stat.uid) };
|
|
14656
14876
|
}
|
|
14657
14877
|
function executableWitness(path) {
|
|
14658
|
-
if (!
|
|
14878
|
+
if (!isAbsolute13(path) || resolve12(path) !== path)
|
|
14659
14879
|
refuse3("NATIVE_SKILL_POLICY_PROCESS_UNBOUND", "the consumer executable path is not an exact absolute path");
|
|
14660
|
-
for (let cursor =
|
|
14880
|
+
for (let cursor = dirname13(path);; cursor = dirname13(cursor)) {
|
|
14661
14881
|
const stat2 = lstatSync14(cursor, { throwIfNoEntry: false });
|
|
14662
14882
|
if (!stat2 || stat2.isSymbolicLink() || !stat2.isDirectory())
|
|
14663
14883
|
refuse3("NATIVE_SKILL_POLICY_PROCESS_UNBOUND", `the consumer executable path has a missing, linked or unexpected component: ${cursor}`);
|
|
14664
|
-
if (
|
|
14884
|
+
if (dirname13(cursor) === cursor)
|
|
14665
14885
|
break;
|
|
14666
14886
|
}
|
|
14667
14887
|
const stat = lstatSync14(path, { throwIfNoEntry: false, bigint: true });
|
|
@@ -14672,7 +14892,7 @@ function executableWitness(path) {
|
|
|
14672
14892
|
var witnessKey = (path, witness) => JSON.stringify([path, witness.dev, witness.ino, witness.size, witness.mtimeNs, witness.ctimeNs, witness.uid]);
|
|
14673
14893
|
var executableDigestMemory = new Map;
|
|
14674
14894
|
function executableCachePath(dataDir2) {
|
|
14675
|
-
return join18(
|
|
14895
|
+
return join18(resolve12(dataDir2), "agent-hooks", "codex-native-policy-executable-cache.json");
|
|
14676
14896
|
}
|
|
14677
14897
|
var currentUid = () => process.getuid?.() ?? -1;
|
|
14678
14898
|
function readTrustedCacheBytes(path, ownerUid) {
|
|
@@ -14717,16 +14937,16 @@ function readExecutableCache(dataDir2, ownerUid = currentUid()) {
|
|
|
14717
14937
|
if (!object5(value) || value.schema !== CODEX_NATIVE_POLICY_EXECUTABLE_CACHE_SCHEMA || !Array.isArray(value.entries) || value.entries.length > 16)
|
|
14718
14938
|
return [];
|
|
14719
14939
|
const fields = ["path", "sha256", "dev", "ino", "size", "mtimeNs", "ctimeNs", "uid"];
|
|
14720
|
-
return value.entries.filter((entry) => object5(entry) && Object.keys(entry).length === fields.length && fields.every((
|
|
14940
|
+
return value.entries.filter((entry) => object5(entry) && Object.keys(entry).length === fields.length && fields.every((field2) => typeof entry[field2] === "string") && HEX_DIGEST.test(entry.sha256) && isAbsolute13(entry.path) && ["dev", "ino", "size", "mtimeNs", "ctimeNs", "uid"].every((field2) => /^\d{1,30}$/.test(entry[field2])));
|
|
14721
14941
|
} catch {
|
|
14722
14942
|
return [];
|
|
14723
14943
|
}
|
|
14724
14944
|
}
|
|
14725
14945
|
function writeExecutableCache(dataDir2, entries) {
|
|
14726
|
-
const path = executableCachePath(dataDir2), directory =
|
|
14946
|
+
const path = executableCachePath(dataDir2), directory = dirname13(path), temporary = `${path}.skills-${randomUUID2()}`;
|
|
14727
14947
|
let descriptor;
|
|
14728
14948
|
try {
|
|
14729
|
-
const parent = lstatSync14(
|
|
14949
|
+
const parent = lstatSync14(dirname13(directory), { throwIfNoEntry: false });
|
|
14730
14950
|
if (!parent || parent.isSymbolicLink() || !parent.isDirectory())
|
|
14731
14951
|
return;
|
|
14732
14952
|
const existing = lstatSync14(directory, { throwIfNoEntry: false });
|
|
@@ -14794,16 +15014,16 @@ function verifyCodexNativeExecutable(inspector, processId, trust, cache = {}, ex
|
|
|
14794
15014
|
}
|
|
14795
15015
|
function assertOperatorTrustRoot(dataDir2) {
|
|
14796
15016
|
const check = (path, what, required) => {
|
|
14797
|
-
if (!
|
|
15017
|
+
if (!isAbsolute13(path) || resolve12(path) !== path)
|
|
14798
15018
|
refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} path is not an exact absolute path`);
|
|
14799
|
-
for (let cursor =
|
|
15019
|
+
for (let cursor = dirname13(path);; cursor = dirname13(cursor)) {
|
|
14800
15020
|
const stat2 = lstatSync14(cursor, { throwIfNoEntry: false });
|
|
14801
15021
|
if (!stat2) {
|
|
14802
15022
|
if (required)
|
|
14803
15023
|
refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} has a missing path component: ${cursor}`);
|
|
14804
15024
|
} else if (stat2.isSymbolicLink() || !stat2.isDirectory())
|
|
14805
15025
|
refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} has a linked or unexpected path component: ${cursor}`);
|
|
14806
|
-
if (
|
|
15026
|
+
if (dirname13(cursor) === cursor)
|
|
14807
15027
|
break;
|
|
14808
15028
|
}
|
|
14809
15029
|
const stat = lstatSync14(path, { throwIfNoEntry: false, bigint: true });
|
|
@@ -14833,9 +15053,9 @@ function assertOperatorTrustRoot(dataDir2) {
|
|
|
14833
15053
|
if ((stat.mode & 0o022n) !== 0n)
|
|
14834
15054
|
refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} is group- or world-writable`);
|
|
14835
15055
|
};
|
|
14836
|
-
checkDirectory(
|
|
14837
|
-
checkDirectory(
|
|
14838
|
-
check(join18(
|
|
15056
|
+
checkDirectory(resolve12(dataDir2), "the Skills data directory", true);
|
|
15057
|
+
checkDirectory(dirname13(executableCachePath(dataDir2)), "the agent-hooks directory", false);
|
|
15058
|
+
check(join18(resolve12(dataDir2), "agent-policy.json"), "the managed policy", true);
|
|
14839
15059
|
check(executableCachePath(dataDir2), "the executable identity cache", false);
|
|
14840
15060
|
}
|
|
14841
15061
|
function runCodexNativePolicyHelper(request) {
|
|
@@ -15006,12 +15226,12 @@ function defaultProcessInspector() {
|
|
|
15006
15226
|
return { platform: process.platform, arch: process.arch, pid: process.pid, parentOf: () => null, startTime: () => null, executablePath: () => null };
|
|
15007
15227
|
}
|
|
15008
15228
|
function codexNativePolicyReceiptPath(dataDir2) {
|
|
15009
|
-
return join18(
|
|
15229
|
+
return join18(resolve12(dataDir2), "agent-hooks", "codex-native-policy-acceptance.json");
|
|
15010
15230
|
}
|
|
15011
15231
|
function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
|
|
15012
|
-
const path = codexNativePolicyReceiptPath(dataDir2), directory =
|
|
15232
|
+
const path = codexNativePolicyReceiptPath(dataDir2), directory = dirname13(path);
|
|
15013
15233
|
const acceptedCache = accepted.map((entry) => ({ path: entry.path, treeSha256: entry.treeSha256 })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
15014
|
-
if (acceptedCache.some((entry) => !
|
|
15234
|
+
if (acceptedCache.some((entry) => !isAbsolute13(entry.path) || !HEX_DIGEST.test(entry.treeSha256)) || new Set(acceptedCache.map((entry) => entry.path)).size !== acceptedCache.length)
|
|
15015
15235
|
refuse3("NATIVE_SKILL_POLICY_RECEIPT_FAILED", "accepted cache entries must be unique absolute paths with tree digests");
|
|
15016
15236
|
const value = {
|
|
15017
15237
|
schema: CODEX_NATIVE_POLICY_RECEIPT_SCHEMA,
|
|
@@ -15057,11 +15277,11 @@ function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
|
|
|
15057
15277
|
}
|
|
15058
15278
|
|
|
15059
15279
|
// src/lib/codex-settings-witness-v1.ts
|
|
15060
|
-
import { createHash as
|
|
15061
|
-
var sha = (text2) =>
|
|
15280
|
+
import { createHash as createHash15 } from "crypto";
|
|
15281
|
+
var sha = (text2) => createHash15("sha256").update(text2).digest("hex");
|
|
15062
15282
|
var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
15063
15283
|
var VERBOSITY = new Set(["low", "medium", "high"]);
|
|
15064
|
-
function
|
|
15284
|
+
function need4(value) {
|
|
15065
15285
|
if (!value)
|
|
15066
15286
|
throw new Error("Invalid Codex settings witness");
|
|
15067
15287
|
}
|
|
@@ -15076,14 +15296,14 @@ function numericTokens(text2) {
|
|
|
15076
15296
|
continue;
|
|
15077
15297
|
}
|
|
15078
15298
|
if (char === '"' || char === "'") {
|
|
15079
|
-
const
|
|
15299
|
+
const quote2 = char, triple = text2.slice(at, at + 3) === quote2.repeat(3);
|
|
15080
15300
|
at += triple ? 3 : 1;
|
|
15081
15301
|
while (at < text2.length) {
|
|
15082
|
-
if (
|
|
15302
|
+
if (quote2 === '"' && text2[at] === "\\") {
|
|
15083
15303
|
at += 2;
|
|
15084
15304
|
continue;
|
|
15085
15305
|
}
|
|
15086
|
-
if (text2.slice(at, at + (triple ? 3 : 1)) ===
|
|
15306
|
+
if (text2.slice(at, at + (triple ? 3 : 1)) === quote2.repeat(triple ? 3 : 1)) {
|
|
15087
15307
|
at += triple ? 3 : 1;
|
|
15088
15308
|
break;
|
|
15089
15309
|
}
|
|
@@ -15104,8 +15324,8 @@ function numericTokens(text2) {
|
|
|
15104
15324
|
}
|
|
15105
15325
|
return result;
|
|
15106
15326
|
}
|
|
15107
|
-
function
|
|
15108
|
-
|
|
15327
|
+
function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
15328
|
+
need4(depth <= 32 && --budget2.remaining >= 0);
|
|
15109
15329
|
if (value === null)
|
|
15110
15330
|
return ["null"];
|
|
15111
15331
|
if (typeof value === "string")
|
|
@@ -15113,20 +15333,20 @@ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
|
15113
15333
|
if (typeof value === "boolean")
|
|
15114
15334
|
return ["boolean", value];
|
|
15115
15335
|
if (typeof value === "number") {
|
|
15116
|
-
|
|
15336
|
+
need4(Number.isFinite(value));
|
|
15117
15337
|
return ["number", String(value)];
|
|
15118
15338
|
}
|
|
15119
15339
|
if (value instanceof Date) {
|
|
15120
|
-
|
|
15340
|
+
need4(Number.isFinite(value.getTime()));
|
|
15121
15341
|
return ["date", value.toISOString()];
|
|
15122
15342
|
}
|
|
15123
15343
|
if (Array.isArray(value))
|
|
15124
|
-
return ["array", value.map((
|
|
15125
|
-
|
|
15126
|
-
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key,
|
|
15344
|
+
return ["array", value.map((child2) => canonical5(child2, depth + 1, budget2))];
|
|
15345
|
+
need4(value && typeof value === "object");
|
|
15346
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child2]) => [key, canonical5(child2, depth + 1, budget2)])];
|
|
15127
15347
|
}
|
|
15128
15348
|
function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15129
|
-
|
|
15349
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15130
15350
|
budget2.remaining -= Buffer.byteLength(text2);
|
|
15131
15351
|
let config;
|
|
15132
15352
|
try {
|
|
@@ -15134,17 +15354,17 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
|
|
|
15134
15354
|
} catch {
|
|
15135
15355
|
throw new Error("Invalid Codex settings witness");
|
|
15136
15356
|
}
|
|
15137
|
-
|
|
15357
|
+
need4(config && typeof config === "object" && !Array.isArray(config));
|
|
15138
15358
|
if (Object.hasOwn(config, "model")) {
|
|
15139
|
-
|
|
15359
|
+
need4(typeof config.model === "string" && /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(config.model));
|
|
15140
15360
|
delete config.model;
|
|
15141
15361
|
}
|
|
15142
15362
|
for (const [key, values] of [["model_reasoning_effort", EFFORT], ["model_verbosity", VERBOSITY]])
|
|
15143
15363
|
if (Object.hasOwn(config, key)) {
|
|
15144
|
-
|
|
15364
|
+
need4(typeof config[key] === "string" && values.has(config[key]));
|
|
15145
15365
|
delete config[key];
|
|
15146
15366
|
}
|
|
15147
|
-
return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([
|
|
15367
|
+
return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical5(config), numericTokens(text2)])}`);
|
|
15148
15368
|
}
|
|
15149
15369
|
function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15150
15370
|
const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
|
|
@@ -15215,15 +15435,15 @@ function projectRegularCodexMcp(config, source) {
|
|
|
15215
15435
|
}
|
|
15216
15436
|
|
|
15217
15437
|
// src/lib/codex-settings-witness.ts
|
|
15218
|
-
import { createHash as
|
|
15438
|
+
import { createHash as createHash16 } from "crypto";
|
|
15219
15439
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
15220
|
-
var sha2 = (text2) =>
|
|
15440
|
+
var sha2 = (text2) => createHash16("sha256").update(text2).digest("hex");
|
|
15221
15441
|
var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
15222
15442
|
var VERBOSITY2 = new Set(["low", "medium", "high"]);
|
|
15223
15443
|
var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
15224
15444
|
var CODEX_DISCOVERY_PROJECTION_FIELDS = ["plugins", "marketplaces", "skills"];
|
|
15225
15445
|
var INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
|
|
15226
|
-
function
|
|
15446
|
+
function need5(value) {
|
|
15227
15447
|
if (!value)
|
|
15228
15448
|
throw new Error("Invalid Codex settings witness");
|
|
15229
15449
|
}
|
|
@@ -15244,14 +15464,14 @@ function numericTokens2(text2) {
|
|
|
15244
15464
|
continue;
|
|
15245
15465
|
}
|
|
15246
15466
|
if (char === '"' || char === "'") {
|
|
15247
|
-
const
|
|
15467
|
+
const quote2 = char, triple = text2.slice(at, at + 3) === quote2.repeat(3);
|
|
15248
15468
|
at += triple ? 3 : 1;
|
|
15249
15469
|
while (at < text2.length) {
|
|
15250
|
-
if (
|
|
15470
|
+
if (quote2 === '"' && text2[at] === "\\") {
|
|
15251
15471
|
at += 2;
|
|
15252
15472
|
continue;
|
|
15253
15473
|
}
|
|
15254
|
-
if (text2.slice(at, at + (triple ? 3 : 1)) ===
|
|
15474
|
+
if (text2.slice(at, at + (triple ? 3 : 1)) === quote2.repeat(triple ? 3 : 1)) {
|
|
15255
15475
|
at += triple ? 3 : 1;
|
|
15256
15476
|
break;
|
|
15257
15477
|
}
|
|
@@ -15272,8 +15492,8 @@ function numericTokens2(text2) {
|
|
|
15272
15492
|
}
|
|
15273
15493
|
return result.sort();
|
|
15274
15494
|
}
|
|
15275
|
-
function
|
|
15276
|
-
|
|
15495
|
+
function canonical6(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
15496
|
+
need5(depth <= 32 && --budget2.remaining >= 0);
|
|
15277
15497
|
if (value === null)
|
|
15278
15498
|
return ["null"];
|
|
15279
15499
|
if (typeof value === "string")
|
|
@@ -15281,17 +15501,17 @@ function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
|
15281
15501
|
if (typeof value === "boolean")
|
|
15282
15502
|
return ["boolean", value];
|
|
15283
15503
|
if (typeof value === "number") {
|
|
15284
|
-
|
|
15504
|
+
need5(Number.isFinite(value));
|
|
15285
15505
|
return ["number", String(value)];
|
|
15286
15506
|
}
|
|
15287
15507
|
if (isDate(value)) {
|
|
15288
|
-
|
|
15508
|
+
need5(Number.isFinite(value.getTime()));
|
|
15289
15509
|
return ["date", value.toISOString()];
|
|
15290
15510
|
}
|
|
15291
15511
|
if (Array.isArray(value))
|
|
15292
|
-
return ["array", value.map((
|
|
15293
|
-
|
|
15294
|
-
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key,
|
|
15512
|
+
return ["array", value.map((child2) => canonical6(child2, depth + 1, budget2))];
|
|
15513
|
+
need5(isRecord3(value));
|
|
15514
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child2]) => [key, canonical6(child2, depth + 1, budget2)])];
|
|
15295
15515
|
}
|
|
15296
15516
|
function normalize3(config, version = 2) {
|
|
15297
15517
|
if (isRecord3(config.hooks)) {
|
|
@@ -15333,15 +15553,15 @@ function inference(target, version) {
|
|
|
15333
15553
|
if (Object.hasOwn(target, key)) {
|
|
15334
15554
|
const value = target[key];
|
|
15335
15555
|
if (key === "model")
|
|
15336
|
-
|
|
15556
|
+
need5(typeof value === "string" && MODEL.test(value));
|
|
15337
15557
|
else if (key === "personality")
|
|
15338
|
-
|
|
15558
|
+
need5(typeof value === "string" && ["none", "friendly", "pragmatic"].includes(value));
|
|
15339
15559
|
else if (version >= 3 && key !== "model_verbosity") {
|
|
15340
|
-
|
|
15560
|
+
need5(typeof value === "string" && value.length <= 128 && !/[\x00-\x1f\x7f]/.test(value));
|
|
15341
15561
|
if (key !== "service_tier")
|
|
15342
|
-
|
|
15562
|
+
need5(value.length > 0);
|
|
15343
15563
|
} else
|
|
15344
|
-
|
|
15564
|
+
need5(typeof value === "string" && (key === "model_reasoning_effort" ? EFFORT2.has(value) : VERBOSITY2.has(value)));
|
|
15345
15565
|
delete target[key];
|
|
15346
15566
|
}
|
|
15347
15567
|
}
|
|
@@ -15374,14 +15594,14 @@ function projectModelAvailabilityNux(config, text2) {
|
|
|
15374
15594
|
continue;
|
|
15375
15595
|
}
|
|
15376
15596
|
if (char === '"' || char === "'") {
|
|
15377
|
-
const
|
|
15597
|
+
const quote2 = char;
|
|
15378
15598
|
at++;
|
|
15379
15599
|
while (at < span.length) {
|
|
15380
|
-
if (
|
|
15600
|
+
if (quote2 === '"' && span[at] === "\\") {
|
|
15381
15601
|
at += 2;
|
|
15382
15602
|
continue;
|
|
15383
15603
|
}
|
|
15384
|
-
if (span[at++] ===
|
|
15604
|
+
if (span[at++] === quote2)
|
|
15385
15605
|
break;
|
|
15386
15606
|
}
|
|
15387
15607
|
continue;
|
|
@@ -15461,14 +15681,14 @@ function settingsDigest2(text2, version = 2) {
|
|
|
15461
15681
|
} catch {
|
|
15462
15682
|
throw new Error("Invalid Codex settings witness");
|
|
15463
15683
|
}
|
|
15464
|
-
|
|
15684
|
+
need5(isRecord3(config));
|
|
15465
15685
|
const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
|
|
15466
15686
|
const projected = version === 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
|
|
15467
15687
|
normalize3(projected.config, version);
|
|
15468
|
-
return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([
|
|
15688
|
+
return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical6(projected.config), numericTokens2(projected.numericSource)])}`);
|
|
15469
15689
|
}
|
|
15470
15690
|
function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15471
|
-
|
|
15691
|
+
need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15472
15692
|
budget2.remaining -= Buffer.byteLength(text2);
|
|
15473
15693
|
return settingsDigest2(text2);
|
|
15474
15694
|
}
|
|
@@ -15480,17 +15700,17 @@ function readCodexSettingsPreimage(path, budget2 = { remaining: 2 * 1024 * 1024
|
|
|
15480
15700
|
return readNativeSettingsWitnessFile(path, budget2, "config.toml");
|
|
15481
15701
|
}
|
|
15482
15702
|
function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
|
|
15483
|
-
|
|
15703
|
+
need5(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));
|
|
15484
15704
|
const budget2 = { remaining: 2 * 1024 * 1024 };
|
|
15485
15705
|
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
|
|
15486
|
-
|
|
15706
|
+
need5((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
|
|
15487
15707
|
const sha2562 = settingsDigest2(current);
|
|
15488
15708
|
if (settingsDigest2(before) !== sha2562)
|
|
15489
15709
|
throw new Error("Codex non-preference settings changed; explicit discovery review required");
|
|
15490
15710
|
return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
|
|
15491
15711
|
}
|
|
15492
15712
|
function hashCodexSettingsReplacementV3(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15493
|
-
|
|
15713
|
+
need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15494
15714
|
budget2.remaining -= Buffer.byteLength(text2);
|
|
15495
15715
|
return settingsDigest2(text2, 3);
|
|
15496
15716
|
}
|
|
@@ -15498,18 +15718,18 @@ function captureCodexSettingsV3(path, budget2 = { remaining: 256 * 1024 * 1024 }
|
|
|
15498
15718
|
return { path, hashMode: "codex-settings-v3", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 3) };
|
|
15499
15719
|
}
|
|
15500
15720
|
function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
|
|
15501
|
-
|
|
15721
|
+
need5(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
|
|
15502
15722
|
const budget2 = { remaining: 2 * 1024 * 1024 };
|
|
15503
15723
|
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
|
|
15504
15724
|
const original = previous.hashMode === "codex-settings-v2" ? settingsDigest2(before) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
|
|
15505
|
-
|
|
15725
|
+
need5(original === previous.sha256);
|
|
15506
15726
|
const sha2562 = settingsDigest2(current, 3);
|
|
15507
15727
|
if (settingsDigest2(before, 3) !== sha2562)
|
|
15508
15728
|
throw new Error("Codex settings outside the reviewed V3 contract changed; explicit discovery review required");
|
|
15509
15729
|
return { path: previous.path, hashMode: "codex-settings-v3", sha256: sha2562 };
|
|
15510
15730
|
}
|
|
15511
15731
|
function hashCodexSettingsReplacementV4(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15512
|
-
|
|
15732
|
+
need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15513
15733
|
budget2.remaining -= Buffer.byteLength(text2);
|
|
15514
15734
|
return settingsDigest2(text2, 4);
|
|
15515
15735
|
}
|
|
@@ -15517,11 +15737,11 @@ function captureCodexSettingsV4(path, budget2 = { remaining: 256 * 1024 * 1024 }
|
|
|
15517
15737
|
return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 4) };
|
|
15518
15738
|
}
|
|
15519
15739
|
function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
15520
|
-
|
|
15740
|
+
need5(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
|
|
15521
15741
|
const budget2 = { remaining: 2 * 1024 * 1024 };
|
|
15522
15742
|
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
|
|
15523
15743
|
const original = previous.hashMode === "codex-settings-v3" ? settingsDigest2(before, 3) : previous.hashMode === "codex-settings-v2" ? settingsDigest2(before, 2) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
|
|
15524
|
-
|
|
15744
|
+
need5(original === previous.sha256);
|
|
15525
15745
|
const sha2562 = settingsDigest2(current, 4);
|
|
15526
15746
|
if (settingsDigest2(before, 4) !== sha2562)
|
|
15527
15747
|
throw new Error("Codex settings outside the reviewed V4 contract changed; explicit discovery review required");
|
|
@@ -15529,17 +15749,17 @@ function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
|
15529
15749
|
}
|
|
15530
15750
|
|
|
15531
15751
|
// src/lib/agent-integration.ts
|
|
15532
|
-
import { createHash as
|
|
15752
|
+
import { createHash as createHash20, randomUUID as randomUUID3 } from "crypto";
|
|
15533
15753
|
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
15534
|
-
import { existsSync as existsSync9, lstatSync as lstatSync20, statSync as statSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync15, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as
|
|
15535
|
-
import { dirname as
|
|
15754
|
+
import { existsSync as existsSync9, lstatSync as lstatSync20, statSync as statSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync15, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync7, realpathSync as realpathSync10, renameSync as renameSync3, rmdirSync as rmdirSync2, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync11, closeSync as closeSync11, fsyncSync as fsyncSync5, fstatSync as fstatSync11, readSync as readSync8, constants as constants11, linkSync as linkSync2 } from "fs";
|
|
15755
|
+
import { dirname as dirname15, join as join23, resolve as resolve15, relative as relative6, isAbsolute as isAbsolute16, sep as sep7 } from "path";
|
|
15536
15756
|
import { homedir as homedir2 } from "os";
|
|
15537
15757
|
|
|
15538
15758
|
// src/lib/managed-policy.ts
|
|
15539
15759
|
import { closeSync as closeSync8, constants as constants8, fstatSync as fstatSync8, lstatSync as lstatSync15, openSync as openSync8, readSync as readSync6 } from "fs";
|
|
15540
15760
|
import { join as join19 } from "path";
|
|
15541
|
-
function refusal(
|
|
15542
|
-
return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${
|
|
15761
|
+
function refusal(reason2 = "is unreadable") {
|
|
15762
|
+
return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason2}; refusing legacy fallback.`);
|
|
15543
15763
|
}
|
|
15544
15764
|
function parseManagedSkillPolicy(text2) {
|
|
15545
15765
|
if (Buffer.byteLength(text2) > AGENT_POLICY_LIMITS.bytes)
|
|
@@ -15616,7 +15836,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
|
|
|
15616
15836
|
}
|
|
15617
15837
|
|
|
15618
15838
|
// src/lib/agent-bridge.ts
|
|
15619
|
-
import { createHash as
|
|
15839
|
+
import { createHash as createHash17 } from "crypto";
|
|
15620
15840
|
import { lstatSync as lstatSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
|
|
15621
15841
|
import { join as join20 } from "path";
|
|
15622
15842
|
var CLI_BRIDGE_NAME = "skills-cli";
|
|
@@ -15635,7 +15855,7 @@ Use the Skills CLI as the skill authority for this station.
|
|
|
15635
15855
|
|
|
15636
15856
|
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.
|
|
15637
15857
|
`;
|
|
15638
|
-
var CLI_BRIDGE_DIGEST =
|
|
15858
|
+
var CLI_BRIDGE_DIGEST = createHash17("sha256").update(markdown).digest("hex");
|
|
15639
15859
|
var CLI_BRIDGE_FILES = Object.freeze({
|
|
15640
15860
|
"SKILL.md": markdown,
|
|
15641
15861
|
".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
|
|
@@ -15664,29 +15884,29 @@ var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^
|
|
|
15664
15884
|
var codexPathConfigRefusal = () => new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables or a native single-line inline array; preserve and review unsupported inline, dotted, or malformed definitions before running skills hook install");
|
|
15665
15885
|
function splitTopLevel(value, delimiter2) {
|
|
15666
15886
|
const result = [];
|
|
15667
|
-
let start = 0, square = 0, curly = 0,
|
|
15887
|
+
let start = 0, square = 0, curly = 0, quote2 = null, escaped = false;
|
|
15668
15888
|
for (let index = 0;index < value.length; index++) {
|
|
15669
15889
|
const char = value[index];
|
|
15670
|
-
if (
|
|
15890
|
+
if (quote2 === "double") {
|
|
15671
15891
|
if (escaped)
|
|
15672
15892
|
escaped = false;
|
|
15673
15893
|
else if (char === "\\")
|
|
15674
15894
|
escaped = true;
|
|
15675
15895
|
else if (char === '"')
|
|
15676
|
-
|
|
15896
|
+
quote2 = null;
|
|
15677
15897
|
continue;
|
|
15678
15898
|
}
|
|
15679
|
-
if (
|
|
15899
|
+
if (quote2 === "literal") {
|
|
15680
15900
|
if (char === "'")
|
|
15681
|
-
|
|
15901
|
+
quote2 = null;
|
|
15682
15902
|
continue;
|
|
15683
15903
|
}
|
|
15684
15904
|
if (char === '"') {
|
|
15685
|
-
|
|
15905
|
+
quote2 = "double";
|
|
15686
15906
|
continue;
|
|
15687
15907
|
}
|
|
15688
15908
|
if (char === "'") {
|
|
15689
|
-
|
|
15909
|
+
quote2 = "literal";
|
|
15690
15910
|
continue;
|
|
15691
15911
|
}
|
|
15692
15912
|
if (char === "[")
|
|
@@ -15704,7 +15924,7 @@ function splitTopLevel(value, delimiter2) {
|
|
|
15704
15924
|
start = index + 1;
|
|
15705
15925
|
}
|
|
15706
15926
|
}
|
|
15707
|
-
if (
|
|
15927
|
+
if (quote2 !== null || square !== 0 || curly !== 0)
|
|
15708
15928
|
return null;
|
|
15709
15929
|
result.push(value.slice(start));
|
|
15710
15930
|
return result;
|
|
@@ -15712,29 +15932,29 @@ function splitTopLevel(value, delimiter2) {
|
|
|
15712
15932
|
function inlineArrayEnd(value) {
|
|
15713
15933
|
if (!value.startsWith("["))
|
|
15714
15934
|
return null;
|
|
15715
|
-
let square = 0, curly = 0,
|
|
15935
|
+
let square = 0, curly = 0, quote2 = null, escaped = false;
|
|
15716
15936
|
for (let index = 0;index < value.length; index++) {
|
|
15717
15937
|
const char = value[index];
|
|
15718
|
-
if (
|
|
15938
|
+
if (quote2 === "double") {
|
|
15719
15939
|
if (escaped)
|
|
15720
15940
|
escaped = false;
|
|
15721
15941
|
else if (char === "\\")
|
|
15722
15942
|
escaped = true;
|
|
15723
15943
|
else if (char === '"')
|
|
15724
|
-
|
|
15944
|
+
quote2 = null;
|
|
15725
15945
|
continue;
|
|
15726
15946
|
}
|
|
15727
|
-
if (
|
|
15947
|
+
if (quote2 === "literal") {
|
|
15728
15948
|
if (char === "'")
|
|
15729
|
-
|
|
15949
|
+
quote2 = null;
|
|
15730
15950
|
continue;
|
|
15731
15951
|
}
|
|
15732
15952
|
if (char === '"') {
|
|
15733
|
-
|
|
15953
|
+
quote2 = "double";
|
|
15734
15954
|
continue;
|
|
15735
15955
|
}
|
|
15736
15956
|
if (char === "'") {
|
|
15737
|
-
|
|
15957
|
+
quote2 = "literal";
|
|
15738
15958
|
continue;
|
|
15739
15959
|
}
|
|
15740
15960
|
if (char === "[")
|
|
@@ -15771,7 +15991,7 @@ function inlineRowsAsTables(value, newline) {
|
|
|
15771
15991
|
const fields = splitTopLevel(row.slice(1, -1), ",");
|
|
15772
15992
|
if (!fields)
|
|
15773
15993
|
return null;
|
|
15774
|
-
const lines = fields.map((
|
|
15994
|
+
const lines = fields.map((field2) => field2.trim()).filter(Boolean);
|
|
15775
15995
|
if (lines.some((line) => !/^(?:path|name|enabled)\s*=/.test(line)))
|
|
15776
15996
|
return null;
|
|
15777
15997
|
const keys = lines.map((line) => line.slice(0, line.indexOf("=")).trim());
|
|
@@ -15796,27 +16016,27 @@ function normalizeCodexInlinePathConfig(text2) {
|
|
|
15796
16016
|
const matches = [];
|
|
15797
16017
|
for (const assignment of text2.matchAll(/^([ \t]*config[ \t]*=[ \t]*)(.*?)(\r?)$/gm)) {
|
|
15798
16018
|
const rawValue = assignment[2].trimEnd();
|
|
15799
|
-
let
|
|
16019
|
+
let quote2 = null, escaped = false, comment = "";
|
|
15800
16020
|
for (let index = 0;index < rawValue.length; index++) {
|
|
15801
16021
|
const char = rawValue[index];
|
|
15802
|
-
if (
|
|
16022
|
+
if (quote2 === "double") {
|
|
15803
16023
|
if (escaped)
|
|
15804
16024
|
escaped = false;
|
|
15805
16025
|
else if (char === "\\")
|
|
15806
16026
|
escaped = true;
|
|
15807
16027
|
else if (char === '"')
|
|
15808
|
-
|
|
16028
|
+
quote2 = null;
|
|
15809
16029
|
continue;
|
|
15810
16030
|
}
|
|
15811
|
-
if (
|
|
16031
|
+
if (quote2 === "literal") {
|
|
15812
16032
|
if (char === "'")
|
|
15813
|
-
|
|
16033
|
+
quote2 = null;
|
|
15814
16034
|
continue;
|
|
15815
16035
|
}
|
|
15816
16036
|
if (char === '"')
|
|
15817
|
-
|
|
16037
|
+
quote2 = "double";
|
|
15818
16038
|
else if (char === "'")
|
|
15819
|
-
|
|
16039
|
+
quote2 = "literal";
|
|
15820
16040
|
else if (char === "#") {
|
|
15821
16041
|
comment = rawValue.slice(index);
|
|
15822
16042
|
break;
|
|
@@ -15928,28 +16148,28 @@ function disableCodexBundledSkills(text2) {
|
|
|
15928
16148
|
|
|
15929
16149
|
// src/lib/agent-sumi.ts
|
|
15930
16150
|
import { existsSync as existsSync8, lstatSync as lstatSync17, realpathSync as realpathSync8 } from "fs";
|
|
15931
|
-
import { join as join21, resolve as
|
|
16151
|
+
import { join as join21, resolve as resolve13 } from "path";
|
|
15932
16152
|
function sumiConfigDirectory(home, env = process.env) {
|
|
15933
16153
|
for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
|
|
15934
16154
|
if (env[key] !== undefined)
|
|
15935
16155
|
throw new Error(`Sumi ${key} requires a dedicated discovery adapter; unset it before Skills integration`);
|
|
15936
16156
|
const selected = (key) => env[key]?.trim() ? env[key] : undefined;
|
|
15937
|
-
const expand = (value) =>
|
|
16157
|
+
const expand = (value) => resolve13(value === "~" ? home : value.startsWith("~/") ? join21(home, value.slice(2)) : value);
|
|
15938
16158
|
const explicit = selected("SUMI_CONFIG_DIR");
|
|
15939
16159
|
const xdg = selected("XDG_CONFIG_HOME");
|
|
15940
16160
|
const custom = selected("SUMI_HOME");
|
|
15941
|
-
const
|
|
16161
|
+
const canonical7 = explicit ? expand(explicit) : xdg ? join21(expand(xdg), "sumi") : join21(custom ? expand(custom) : join21(home, ".hasna-internal/sumi"), "config");
|
|
15942
16162
|
if (explicit || xdg || custom)
|
|
15943
|
-
return
|
|
16163
|
+
return canonical7;
|
|
15944
16164
|
const legacy = join21(home, ".config/sumi");
|
|
15945
|
-
const a = lstatSync17(
|
|
15946
|
-
if (a && b && realpathSync8(
|
|
16165
|
+
const a = lstatSync17(canonical7, { throwIfNoEntry: false }), b = lstatSync17(legacy, { throwIfNoEntry: false });
|
|
16166
|
+
if (a && b && realpathSync8(canonical7) !== realpathSync8(legacy))
|
|
15947
16167
|
throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
|
|
15948
|
-
const path = a ?
|
|
16168
|
+
const path = a ? canonical7 : b ? legacy : canonical7;
|
|
15949
16169
|
if (!existsSync8(path))
|
|
15950
16170
|
return path;
|
|
15951
16171
|
const physical = realpathSync8(path);
|
|
15952
|
-
if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![
|
|
16172
|
+
if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical7, legacy].includes(physical))
|
|
15953
16173
|
throw new Error("Sumi config alias must resolve to its canonical or legacy root");
|
|
15954
16174
|
return physical;
|
|
15955
16175
|
}
|
|
@@ -16049,33 +16269,49 @@ var SUMI_SKILL_PERMISSIONS = [
|
|
|
16049
16269
|
];
|
|
16050
16270
|
|
|
16051
16271
|
// src/lib/codex-hook-trust-files.ts
|
|
16052
|
-
import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, fsyncSync as fsyncSync3, lstatSync as lstatSync18, openSync as openSync9, readSync as readSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
16053
|
-
import { createHash as
|
|
16054
|
-
import { dirname as
|
|
16055
|
-
function
|
|
16272
|
+
import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, fsyncSync as fsyncSync3, lstatSync as lstatSync18, openSync as openSync9, readlinkSync as readlinkSync5, readSync as readSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
16273
|
+
import { createHash as createHash18 } from "crypto";
|
|
16274
|
+
import { dirname as dirname14, isAbsolute as isAbsolute14, resolve as resolve14 } from "path";
|
|
16275
|
+
function need6(value, code) {
|
|
16056
16276
|
if (!value)
|
|
16057
16277
|
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
16058
16278
|
}
|
|
16059
16279
|
var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
|
|
16280
|
+
function trustedAncestorOwner(uid) {
|
|
16281
|
+
return BigInt(uid) === 0n || BigInt(uid) === BigInt(process.getuid());
|
|
16282
|
+
}
|
|
16060
16283
|
function safeParents(file) {
|
|
16061
|
-
for (let p =
|
|
16284
|
+
for (let p = dirname14(file);; p = dirname14(p)) {
|
|
16062
16285
|
const s = lstatSync18(p);
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
if (
|
|
16286
|
+
need6(s.isDirectory() && !s.isSymbolicLink() && trustedAncestorOwner(s.uid), "UNSAFE_PARENT");
|
|
16287
|
+
need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
16288
|
+
if (dirname14(p) === p)
|
|
16066
16289
|
break;
|
|
16067
16290
|
}
|
|
16068
16291
|
}
|
|
16292
|
+
function directAliasTarget(link) {
|
|
16293
|
+
const text2 = readlinkSync5(link), parts = text2.split("/");
|
|
16294
|
+
let first = 0;
|
|
16295
|
+
if (isAbsolute14(text2))
|
|
16296
|
+
first = 1;
|
|
16297
|
+
else
|
|
16298
|
+
while (parts[first] === "..")
|
|
16299
|
+
first++;
|
|
16300
|
+
const names = parts.slice(first);
|
|
16301
|
+
if (!names.length || names.some((part) => part === "" || part === "." || part === ".."))
|
|
16302
|
+
return;
|
|
16303
|
+
return resolve14(dirname14(link), text2);
|
|
16304
|
+
}
|
|
16069
16305
|
function snapshot(file, privateFile = false, options = {}) {
|
|
16070
16306
|
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
16071
16307
|
safeParents(file);
|
|
16072
16308
|
const initial = lstatSync18(file, { bigint: true });
|
|
16073
|
-
|
|
16074
|
-
|
|
16309
|
+
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");
|
|
16310
|
+
need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
|
|
16075
16311
|
const fd = openSync9(file, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
|
|
16076
16312
|
try {
|
|
16077
16313
|
const before = fstatSync9(fd, { bigint: true });
|
|
16078
|
-
|
|
16314
|
+
need6(sameStat(initial, before), "INPUT_CHANGED");
|
|
16079
16315
|
const bytes = Buffer.alloc(Number(before.size) + 1);
|
|
16080
16316
|
let length = 0;
|
|
16081
16317
|
while (length < bytes.length) {
|
|
@@ -16084,22 +16320,22 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
16084
16320
|
break;
|
|
16085
16321
|
length += n2;
|
|
16086
16322
|
}
|
|
16087
|
-
|
|
16323
|
+
need6(length === Number(before.size) && sameStat(before, fstatSync9(fd, { bigint: true })) && sameStat(before, lstatSync18(file, { bigint: true })), "INPUT_CHANGED");
|
|
16088
16324
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
16089
|
-
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256:
|
|
16325
|
+
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash18("sha256").update(content).digest("hex") };
|
|
16090
16326
|
} finally {
|
|
16091
16327
|
closeSync9(fd);
|
|
16092
16328
|
}
|
|
16093
16329
|
}
|
|
16094
16330
|
function unchanged2(before) {
|
|
16095
16331
|
const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
16096
|
-
|
|
16332
|
+
need6(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
16097
16333
|
}
|
|
16098
16334
|
|
|
16099
16335
|
// src/cli/commands/runtime-launcher.ts
|
|
16100
|
-
import { createHash as
|
|
16101
|
-
import { closeSync as closeSync10, constants as constants10, fchmodSync, fstatSync as fstatSync10, fsyncSync as fsyncSync4, lstatSync as lstatSync19, openSync as openSync10, readFileSync as readFileSync14, readlinkSync as
|
|
16102
|
-
import { isAbsolute as
|
|
16336
|
+
import { createHash as createHash19 } from "crypto";
|
|
16337
|
+
import { closeSync as closeSync10, constants as constants10, fchmodSync, fstatSync as fstatSync10, fsyncSync as fsyncSync4, lstatSync as lstatSync19, openSync as openSync10, readFileSync as readFileSync14, readlinkSync as readlinkSync6, realpathSync as realpathSync9, symlinkSync, writeSync as writeSync2 } from "fs";
|
|
16338
|
+
import { isAbsolute as isAbsolute15 } from "path";
|
|
16103
16339
|
var PINNED_LAUNCHER_SCHEMA = "hasna-skills-pinned-launcher-v1";
|
|
16104
16340
|
var LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
|
|
16105
16341
|
var PINNED_LAUNCHER_BUN_FLAGS = ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install"];
|
|
@@ -16140,10 +16376,10 @@ var PINNED_LAUNCHER_ENV_PREFIXES = ["HASNA", "SKILLS", "SKILL", "MCP", "XDG", "L
|
|
|
16140
16376
|
var ENV_PREFIX = new RegExp(`^(${PINNED_LAUNCHER_ENV_PREFIXES.join("|")})_`);
|
|
16141
16377
|
var ENV_EXACT = new Set(PINNED_LAUNCHER_ENV_NAMES);
|
|
16142
16378
|
function sha2562(bytes) {
|
|
16143
|
-
return
|
|
16379
|
+
return createHash19("sha256").update(bytes).digest("hex");
|
|
16144
16380
|
}
|
|
16145
16381
|
function assertLauncherPath(value, code) {
|
|
16146
|
-
if (typeof value !== "string" || !
|
|
16382
|
+
if (typeof value !== "string" || !isAbsolute15(value) || /['\x00-\x1f\x7f]/.test(value))
|
|
16147
16383
|
throw new Error(code);
|
|
16148
16384
|
}
|
|
16149
16385
|
function renderPinnedLauncher(binding) {
|
|
@@ -16233,7 +16469,7 @@ function assertPinnedLauncherRuntimeSafe(runtime) {
|
|
|
16233
16469
|
}
|
|
16234
16470
|
|
|
16235
16471
|
// src/lib/agent-integration.ts
|
|
16236
|
-
var sha3 = (data) =>
|
|
16472
|
+
var sha3 = (data) => createHash20("sha256").update(data).digest("hex");
|
|
16237
16473
|
var ROOTS = NATIVE_SKILL_ROOTS;
|
|
16238
16474
|
function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
|
|
16239
16475
|
try {
|
|
@@ -16283,9 +16519,10 @@ function isSystemRootAlias(path) {
|
|
|
16283
16519
|
return false;
|
|
16284
16520
|
const target = `/private${path}`, link = `private${path}`;
|
|
16285
16521
|
const identity5 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}`;
|
|
16522
|
+
const directoryIdentity = (stat) => `${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}`;
|
|
16286
16523
|
try {
|
|
16287
16524
|
const before = lstatSync20(path, { bigint: true });
|
|
16288
|
-
if (!before.isSymbolicLink() || before.uid !== 0n ||
|
|
16525
|
+
if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync7(path) !== link)
|
|
16289
16526
|
return false;
|
|
16290
16527
|
const directories = ["/", "/private", target].map((directory) => {
|
|
16291
16528
|
const stat = lstatSync20(directory, { bigint: true });
|
|
@@ -16298,20 +16535,20 @@ function isSystemRootAlias(path) {
|
|
|
16298
16535
|
throw new Error("Unprotected system temporary directory");
|
|
16299
16536
|
const fd = openSync11(directory, constants11.O_RDONLY | constants11.O_DIRECTORY | constants11.O_NOFOLLOW);
|
|
16300
16537
|
try {
|
|
16301
|
-
if (
|
|
16538
|
+
if (directoryIdentity(fstatSync11(fd, { bigint: true })) !== directoryIdentity(stat) || !hasTrustedDarwinAcl(fd) || directoryIdentity(fstatSync11(fd, { bigint: true })) !== directoryIdentity(stat))
|
|
16302
16539
|
throw new Error("Unverified system alias directory ACL");
|
|
16303
16540
|
} finally {
|
|
16304
16541
|
closeSync11(fd);
|
|
16305
16542
|
}
|
|
16306
|
-
return { directory, identity:
|
|
16543
|
+
return { directory, identity: directoryIdentity(stat) };
|
|
16307
16544
|
});
|
|
16308
|
-
return realpathSync10(path) === target &&
|
|
16545
|
+
return realpathSync10(path) === target && readlinkSync7(path) === link && identity5(lstatSync20(path, { bigint: true })) === identity5(before) && directories.every((item) => directoryIdentity(lstatSync20(item.directory, { bigint: true })) === item.identity);
|
|
16309
16546
|
} catch {
|
|
16310
16547
|
return false;
|
|
16311
16548
|
}
|
|
16312
16549
|
}
|
|
16313
16550
|
function canonicalSystemPath(path) {
|
|
16314
|
-
const absolute =
|
|
16551
|
+
const absolute = resolve15(path), root = `/${absolute.split(sep7)[1]}`;
|
|
16315
16552
|
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync20(root, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
16316
16553
|
return absolute;
|
|
16317
16554
|
if (!isSystemRootAlias(root))
|
|
@@ -16319,11 +16556,11 @@ function canonicalSystemPath(path) {
|
|
|
16319
16556
|
return `/private${absolute}`;
|
|
16320
16557
|
}
|
|
16321
16558
|
function assertSafePath(path) {
|
|
16322
|
-
let cursor =
|
|
16559
|
+
let cursor = resolve15(path);
|
|
16323
16560
|
while (true) {
|
|
16324
16561
|
if (lstatSync20(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
|
|
16325
16562
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
16326
|
-
const parent =
|
|
16563
|
+
const parent = dirname15(cursor);
|
|
16327
16564
|
if (parent === cursor)
|
|
16328
16565
|
return;
|
|
16329
16566
|
cursor = parent;
|
|
@@ -16335,13 +16572,13 @@ function readRootAlias(home, agent) {
|
|
|
16335
16572
|
const stat = lstatSync20(alias, { throwIfNoEntry: false, bigint: true });
|
|
16336
16573
|
if (!stat?.isSymbolicLink())
|
|
16337
16574
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
16338
|
-
const link =
|
|
16575
|
+
const link = readlinkSync7(alias), targetPath = resolve15(dirname15(alias), link);
|
|
16339
16576
|
assertSafePath(targetPath);
|
|
16340
16577
|
const targetStat = lstatSync20(targetPath, { throwIfNoEntry: false });
|
|
16341
16578
|
if (!targetStat?.isDirectory())
|
|
16342
16579
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
16343
16580
|
const target = realpathSync10(targetPath), inside = relative6(realHome, target);
|
|
16344
|
-
if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) ||
|
|
16581
|
+
if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) || isAbsolute16(inside))
|
|
16345
16582
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
16346
16583
|
return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
16347
16584
|
}
|
|
@@ -16367,13 +16604,13 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
16367
16604
|
const rows = skills.config;
|
|
16368
16605
|
if (!Array.isArray(rows))
|
|
16369
16606
|
return [];
|
|
16370
|
-
if (rows.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.enabled !== "boolean" || entry.path !== undefined === (entry.name !== undefined) || entry.path !== undefined && (typeof entry.path !== "string" || !
|
|
16607
|
+
if (rows.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.enabled !== "boolean" || entry.path !== undefined === (entry.name !== undefined) || entry.path !== undefined && (typeof entry.path !== "string" || !isAbsolute16(entry.path)) || entry.name !== undefined && (typeof entry.name !== "string" || !entry.name.trim())))
|
|
16371
16608
|
return [];
|
|
16372
16609
|
if (rows.some((entry) => entry.name !== undefined && entry.enabled !== false))
|
|
16373
16610
|
return [];
|
|
16374
16611
|
const settings = new Map;
|
|
16375
16612
|
for (const entry of rows) {
|
|
16376
|
-
if (typeof entry.path !== "string" || !
|
|
16613
|
+
if (typeof entry.path !== "string" || !isAbsolute16(entry.path))
|
|
16377
16614
|
continue;
|
|
16378
16615
|
let document2;
|
|
16379
16616
|
try {
|
|
@@ -16385,7 +16622,7 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
16385
16622
|
continue;
|
|
16386
16623
|
settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
|
|
16387
16624
|
}
|
|
16388
|
-
return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) =>
|
|
16625
|
+
return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname15(document2));
|
|
16389
16626
|
}
|
|
16390
16627
|
function recheckRootAliases(aliases) {
|
|
16391
16628
|
for (const binding of aliases) {
|
|
@@ -16427,7 +16664,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
16427
16664
|
}
|
|
16428
16665
|
}
|
|
16429
16666
|
function treeHash(root) {
|
|
16430
|
-
const hash2 =
|
|
16667
|
+
const hash2 = createHash20("sha256");
|
|
16431
16668
|
let bytes = 0, files = 0;
|
|
16432
16669
|
function visit2(path) {
|
|
16433
16670
|
const stat = lstatSync20(path);
|
|
@@ -16459,20 +16696,20 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
16459
16696
|
const identities = new Set;
|
|
16460
16697
|
const selected = [];
|
|
16461
16698
|
for (const target of manifest.targets) {
|
|
16462
|
-
if (!
|
|
16699
|
+
if (!isAbsolute16(target.projectRoot) || resolve15(target.projectRoot) !== target.projectRoot)
|
|
16463
16700
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
16464
|
-
const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative6(projectRoot,
|
|
16701
|
+
const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative6(projectRoot, resolve15(projectRoot, target.path));
|
|
16465
16702
|
if (!existsSync9(projectRoot) || !lstatSync20(projectRoot).isDirectory())
|
|
16466
16703
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
16467
16704
|
assertSafePath(projectRoot);
|
|
16468
|
-
if (
|
|
16705
|
+
if (isAbsolute16(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep7}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
|
|
16469
16706
|
throw new Error(`Invalid native migration target: ${target.path}`);
|
|
16470
16707
|
const identity5 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
|
|
16471
16708
|
if (identities.has(identity5))
|
|
16472
16709
|
throw new Error(`Duplicate native migration target: ${target.path}`);
|
|
16473
16710
|
identities.add(identity5);
|
|
16474
|
-
const expected = canonicalSystemPath(
|
|
16475
|
-
const matches = inventory.filter((entry2) => entry2.agent === target.agent &&
|
|
16711
|
+
const expected = canonicalSystemPath(resolve15(target.projectRoot, target.path));
|
|
16712
|
+
const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve15(entry2.path) === expected);
|
|
16476
16713
|
if (matches.length !== 1)
|
|
16477
16714
|
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
16478
16715
|
const entry = matches[0];
|
|
@@ -16491,7 +16728,7 @@ function projectAncestorDirectories(projects) {
|
|
|
16491
16728
|
if (depth >= 100)
|
|
16492
16729
|
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
16493
16730
|
directories.add(path);
|
|
16494
|
-
const parent =
|
|
16731
|
+
const parent = dirname15(path);
|
|
16495
16732
|
if (parent === path)
|
|
16496
16733
|
break;
|
|
16497
16734
|
path = parent;
|
|
@@ -16500,7 +16737,7 @@ function projectAncestorDirectories(projects) {
|
|
|
16500
16737
|
return [...directories];
|
|
16501
16738
|
}
|
|
16502
16739
|
function isCodexSkillDiscoveryInput(path) {
|
|
16503
|
-
for (let cursor = canonicalSystemPath(path);cursor !==
|
|
16740
|
+
for (let cursor = canonicalSystemPath(path);cursor !== dirname15(cursor); cursor = dirname15(cursor)) {
|
|
16504
16741
|
if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(join23(sep7, root))))
|
|
16505
16742
|
return true;
|
|
16506
16743
|
}
|
|
@@ -16508,9 +16745,9 @@ function isCodexSkillDiscoveryInput(path) {
|
|
|
16508
16745
|
}
|
|
16509
16746
|
function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
16510
16747
|
home = canonicalSystemPath(home);
|
|
16511
|
-
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !
|
|
16748
|
+
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute16(options.reviewedCacheAlias) || resolve15(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
|
|
16512
16749
|
throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
|
|
16513
|
-
if (options.disabledVendorPaths?.some((path) => !
|
|
16750
|
+
if (options.disabledVendorPaths?.some((path) => !isAbsolute16(path) || resolve15(path) !== path))
|
|
16514
16751
|
throw new Error("Disabled vendor paths must be exact absolute paths");
|
|
16515
16752
|
const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
|
|
16516
16753
|
const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
|
|
@@ -16529,7 +16766,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16529
16766
|
roots.push(["sumi", canonicalAgentPath(join23(project, directory, name), aliases)]);
|
|
16530
16767
|
}
|
|
16531
16768
|
const installationInputs = options.codexInstallationInputRoots ?? [];
|
|
16532
|
-
if (installationInputs.some((input) =>
|
|
16769
|
+
if (installationInputs.some((input) => resolve15(input) !== input || realpathSync10(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep7) || input.startsWith(path + sep7)))))
|
|
16533
16770
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
16534
16771
|
const entries = [], seen = new Set;
|
|
16535
16772
|
const cacheScans = new Map, maxAliasProofEntries = 1e4;
|
|
@@ -16543,11 +16780,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16543
16780
|
}
|
|
16544
16781
|
let reviewedCacheAliasFound = false;
|
|
16545
16782
|
function verifiedSiblingCacheAlias(agent, path, parent) {
|
|
16546
|
-
const link =
|
|
16547
|
-
if (
|
|
16783
|
+
const link = readlinkSync7(path), lexicalTarget = resolve15(parent, link);
|
|
16784
|
+
if (isAbsolute16(link) ? link !== lexicalTarget : dirname15(link) !== ".")
|
|
16548
16785
|
return false;
|
|
16549
16786
|
const target = canonicalSystemPath(lexicalTarget);
|
|
16550
|
-
if (
|
|
16787
|
+
if (dirname15(target) !== parent)
|
|
16551
16788
|
return false;
|
|
16552
16789
|
if (!lstatSync20(target, { throwIfNoEntry: false })?.isDirectory())
|
|
16553
16790
|
return false;
|
|
@@ -16596,17 +16833,17 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16596
16833
|
}
|
|
16597
16834
|
const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
|
|
16598
16835
|
const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep7));
|
|
16599
|
-
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ?? join23(home, ".codex") } : {}, ...agent === "codex" && path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome:
|
|
16836
|
+
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ?? join23(home, ".codex") } : {}, ...agent === "codex" && path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve15(home) } : {}, ...rootAlias ? { rootAlias } : {} });
|
|
16600
16837
|
return scan;
|
|
16601
16838
|
}
|
|
16602
16839
|
if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
|
|
16603
16840
|
return { ...scan, complete: false };
|
|
16604
16841
|
const children = [], directory = opendirSync3(path);
|
|
16605
16842
|
try {
|
|
16606
|
-
let
|
|
16607
|
-
while ((
|
|
16608
|
-
admit(join23(path,
|
|
16609
|
-
children.push(
|
|
16843
|
+
let child2;
|
|
16844
|
+
while ((child2 = directory.readSync()) !== null) {
|
|
16845
|
+
admit(join23(path, child2.name));
|
|
16846
|
+
children.push(child2);
|
|
16610
16847
|
}
|
|
16611
16848
|
} finally {
|
|
16612
16849
|
directory.closeSync();
|
|
@@ -16622,15 +16859,15 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16622
16859
|
const isVendor = vendor || name === ".system";
|
|
16623
16860
|
if (isVendor && !options.includeVendor)
|
|
16624
16861
|
continue;
|
|
16625
|
-
const
|
|
16626
|
-
if (isVendor && lstatSync20(
|
|
16627
|
-
const target = statSync9(
|
|
16628
|
-
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent,
|
|
16862
|
+
const child2 = join23(path, name);
|
|
16863
|
+
if (isVendor && lstatSync20(child2, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
16864
|
+
const target = statSync9(child2, { throwIfNoEntry: false });
|
|
16865
|
+
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child2, path)) {
|
|
16629
16866
|
scan.entries++;
|
|
16630
16867
|
continue;
|
|
16631
16868
|
}
|
|
16632
16869
|
}
|
|
16633
|
-
const childScan = visit2(agent,
|
|
16870
|
+
const childScan = visit2(agent, child2, isVendor, depth + 1, pluginCache, true, configuredRoot);
|
|
16634
16871
|
scan.complete &&= childScan.complete;
|
|
16635
16872
|
scan.hasSkills ||= childScan.hasSkills;
|
|
16636
16873
|
scan.entries += childScan.entries;
|
|
@@ -16766,7 +17003,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
16766
17003
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
16767
17004
|
const bundled = disableCodexBundledSkills(text2);
|
|
16768
17005
|
let result = normalizeCodexInlinePathConfig(bundled);
|
|
16769
|
-
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [
|
|
17006
|
+
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve15(bridgePath), resolve15(join23(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
|
|
16770
17007
|
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
16771
17008
|
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
16772
17009
|
throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
|
|
@@ -16778,7 +17015,7 @@ function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
|
16778
17015
|
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
16779
17016
|
const parsed = Bun.TOML.parse(section);
|
|
16780
17017
|
const declared = parsed.skills?.config?.[0];
|
|
16781
|
-
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [
|
|
17018
|
+
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve15(path), resolve15(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
|
|
16782
17019
|
return section;
|
|
16783
17020
|
found = true;
|
|
16784
17021
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -16799,7 +17036,7 @@ enabled = false
|
|
|
16799
17036
|
return result;
|
|
16800
17037
|
}
|
|
16801
17038
|
function assertCodexInstallationInputs(home, projectDirs, inputs) {
|
|
16802
|
-
const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) =>
|
|
17039
|
+
const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve15(directory, path)));
|
|
16803
17040
|
if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep7) || input.startsWith(root + sep7))))
|
|
16804
17041
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
16805
17042
|
}
|
|
@@ -16815,9 +17052,9 @@ function planAgentIntegration(options) {
|
|
|
16815
17052
|
throw new Error("Invalid existing Skills bridge policy");
|
|
16816
17053
|
if (policy.bridge?.agents !== undefined && (!Array.isArray(policy.bridge.agents) || policy.bridge.agents.some((agent) => !INTEGRATION_AGENTS.includes(agent))))
|
|
16817
17054
|
throw new Error("Invalid existing bridge agent inventory");
|
|
16818
|
-
for (const
|
|
16819
|
-
if (policy.bridge?.[
|
|
16820
|
-
throw new Error(`Invalid existing bridge ${
|
|
17055
|
+
for (const field2 of ["commands", "profiles"])
|
|
17056
|
+
if (policy.bridge?.[field2] !== undefined && (!policy.bridge[field2] || typeof policy.bridge[field2] !== "object" || Array.isArray(policy.bridge[field2]) || Object.entries(policy.bridge[field2]).some(([key, value]) => !INTEGRATION_AGENTS.includes(key) || typeof value !== "string" || !value || value.includes("\x00"))))
|
|
17057
|
+
throw new Error(`Invalid existing bridge ${field2} binding`);
|
|
16821
17058
|
const priorAgents = policy.bridge?.agents ?? [];
|
|
16822
17059
|
const profileId = options.profileId ?? policy.profileId ?? "default";
|
|
16823
17060
|
const validateProfile = (value) => {
|
|
@@ -16832,13 +17069,18 @@ function planAgentIntegration(options) {
|
|
|
16832
17069
|
return [agent, { command, profileId: profileId2 }];
|
|
16833
17070
|
}));
|
|
16834
17071
|
const retainedAgents = [];
|
|
17072
|
+
const priorExecutables = policy.bridge?.discoveryExecutables ?? {};
|
|
17073
|
+
const executables = new Map;
|
|
16835
17074
|
const discoveries = [...new Set(options.agents)].map((agent) => {
|
|
17075
|
+
const prior = priorExecutables[agent];
|
|
17076
|
+
const executable2 = discoveryExecutable(agent) ?? (prior ? recordedDiscoveryExecutable(agent, prior) : null);
|
|
17077
|
+
executables.set(agent, executable2);
|
|
16836
17078
|
const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? { ...policy.bridge.discovery[agent], ...agent === "codex" ? { codexDisabledPluginSkills: policy.bridge.codexPluginSkills ?? [] } : {} } : undefined;
|
|
16837
17079
|
if (retainedReview) {
|
|
16838
17080
|
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
|
|
16839
17081
|
retainedAgents.push(agent);
|
|
16840
17082
|
}
|
|
16841
|
-
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
17083
|
+
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases), executable: executable2 });
|
|
16842
17084
|
});
|
|
16843
17085
|
const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases)) : null;
|
|
16844
17086
|
const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
|
|
@@ -16846,7 +17088,7 @@ function planAgentIntegration(options) {
|
|
|
16846
17088
|
const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join23(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
|
|
16847
17089
|
const plannedCodexRules = codexPathConfig === undefined ? undefined : Bun.TOML.parse(codexPathConfig).skills?.config ?? [];
|
|
16848
17090
|
const observedNativeSources = [];
|
|
16849
|
-
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join23(entry.path, "SKILL.md")), canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases),
|
|
17091
|
+
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join23(entry.path, "SKILL.md")), canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), resolve15(options.projectDir ?? home), (path) => {
|
|
16850
17092
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
16851
17093
|
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
16852
17094
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
@@ -16975,7 +17217,7 @@ function planAgentIntegration(options) {
|
|
|
16975
17217
|
version: CLI_BRIDGE_VERSION,
|
|
16976
17218
|
digest: CLI_BRIDGE_DIGEST,
|
|
16977
17219
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
16978
|
-
home:
|
|
17220
|
+
home: resolve15(home),
|
|
16979
17221
|
includeVendor: true,
|
|
16980
17222
|
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join23(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
|
|
16981
17223
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
@@ -16985,19 +17227,30 @@ function planAgentIntegration(options) {
|
|
|
16985
17227
|
...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
|
|
16986
17228
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
16987
17229
|
} };
|
|
17230
|
+
const discoveryExecutables = { ...priorExecutables };
|
|
17231
|
+
for (const [agent, executable2] of executables) {
|
|
17232
|
+
if (executable2)
|
|
17233
|
+
discoveryExecutables[agent] = executable2;
|
|
17234
|
+
else
|
|
17235
|
+
delete discoveryExecutables[agent];
|
|
17236
|
+
}
|
|
17237
|
+
if (Object.keys(discoveryExecutables).length)
|
|
17238
|
+
nextPolicy.bridge.discoveryExecutables = discoveryExecutables;
|
|
17239
|
+
else
|
|
17240
|
+
delete nextPolicy.bridge.discoveryExecutables;
|
|
16988
17241
|
const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
|
|
16989
17242
|
if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
|
|
16990
17243
|
changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
|
|
16991
17244
|
recheckRootAliases(aliases);
|
|
16992
17245
|
return { dataDir: dataDir2, profileId, changes, nativeSkills, ...observedSettings ? { observedSettings } : {}, ...observedNativeSources.length ? { observedNativeSources } : {}, ...nextPolicy.bridge.codexPluginSkillReview ? { codexPluginSkillReview: nextPolicy.bridge.codexPluginSkillReview } : {}, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
|
|
16993
17246
|
}
|
|
16994
|
-
function hookProjectionRefusal(
|
|
16995
|
-
throw new Error(`CLAUDE_MANAGED_HOOK_${
|
|
17247
|
+
function hookProjectionRefusal(reason2) {
|
|
17248
|
+
throw new Error(`CLAUDE_MANAGED_HOOK_${reason2}`);
|
|
16996
17249
|
}
|
|
16997
17250
|
function projectedHookAcl(path, observed) {
|
|
16998
17251
|
if (process.platform !== "darwin")
|
|
16999
17252
|
return;
|
|
17000
|
-
const identity5 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}:${stat.nlink}:${stat.size}:${stat.mtimeNs}`;
|
|
17253
|
+
const identity5 = observed.isDirectory() ? (stat) => `${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}` : (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}:${stat.nlink}:${stat.size}:${stat.mtimeNs}`;
|
|
17001
17254
|
const fd = openSync11(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK | (observed.isDirectory() ? constants11.O_DIRECTORY : 0));
|
|
17002
17255
|
try {
|
|
17003
17256
|
if (identity5(fstatSync11(fd, { bigint: true })) !== identity5(observed) || !hasTrustedDarwinAcl(fd, true) || identity5(fstatSync11(fd, { bigint: true })) !== identity5(observed) || identity5(lstatSync20(path, { bigint: true })) !== identity5(observed))
|
|
@@ -17008,18 +17261,18 @@ function projectedHookAcl(path, observed) {
|
|
|
17008
17261
|
}
|
|
17009
17262
|
function projectedHookExecutable(command, expectedResolved) {
|
|
17010
17263
|
try {
|
|
17011
|
-
if (!
|
|
17264
|
+
if (!isAbsolute16(command) || /[\0\r\n]/.test(command) || resolve15(command) !== command)
|
|
17012
17265
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17013
17266
|
command = canonicalSystemPath(command);
|
|
17014
17267
|
const parents2 = (file = command) => {
|
|
17015
17268
|
const result = [];
|
|
17016
|
-
for (let path =
|
|
17269
|
+
for (let path = dirname15(file);; path = dirname15(path)) {
|
|
17017
17270
|
const stat = lstatSync20(path, { bigint: true });
|
|
17018
|
-
if (!stat.isDirectory() || (stat.mode & 0o022n) !== 0n && !(stat.uid === 0n && (stat.mode & 0o1000n) !== 0n))
|
|
17271
|
+
if (!stat.isDirectory() || !trustedAncestorOwner(stat.uid) || (stat.mode & 0o022n) !== 0n && !(stat.uid === 0n && (stat.mode & 0o1000n) !== 0n))
|
|
17019
17272
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17020
17273
|
projectedHookAcl(path, stat);
|
|
17021
17274
|
result.push(`${path}:${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}`);
|
|
17022
|
-
if (
|
|
17275
|
+
if (dirname15(path) === path)
|
|
17023
17276
|
break;
|
|
17024
17277
|
}
|
|
17025
17278
|
return result;
|
|
@@ -17029,22 +17282,33 @@ function projectedHookExecutable(command, expectedResolved) {
|
|
|
17029
17282
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17030
17283
|
const linkIdentity = (stat) => `${stat.dev}:${stat.ino}:${stat.mode}:${stat.uid}:${stat.ctimeNs}`;
|
|
17031
17284
|
const target = resolveLauncherCommand(command), resolved = target.entry;
|
|
17285
|
+
const direct = () => link.isSymbolicLink() ? directAliasTarget(command) === target.physical : target.physical === command;
|
|
17286
|
+
if (!direct())
|
|
17287
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17032
17288
|
if (expectedResolved !== undefined && resolved !== expectedResolved)
|
|
17033
17289
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17034
17290
|
let launcher, launcherParentIdentity;
|
|
17291
|
+
let runtime;
|
|
17035
17292
|
if (target.pinned) {
|
|
17036
17293
|
launcherParentIdentity = parents2(target.physical);
|
|
17037
17294
|
launcher = snapshot(target.physical, false);
|
|
17038
17295
|
projectedHookAcl(launcher.file, launcher.stat);
|
|
17039
17296
|
if (launcher.sha256 !== target.pinned.sha256 || (launcher.stat.mode & 0o100n) === 0n)
|
|
17040
17297
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17041
|
-
|
|
17298
|
+
const path = target.pinned.runtime;
|
|
17299
|
+
assertPinnedLauncherRuntimeSafe(path);
|
|
17300
|
+
if (resolve15(path) !== path)
|
|
17301
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17302
|
+
runtime = { path, parents: parents2(path), stat: lstatSync20(path, { bigint: true }) };
|
|
17303
|
+
if (!runtime.stat.isFile())
|
|
17304
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17305
|
+
projectedHookAcl(path, runtime.stat);
|
|
17042
17306
|
}
|
|
17043
17307
|
const resolvedParentIdentity = parents2(resolved);
|
|
17044
|
-
const manifest = snapshot(join23(
|
|
17308
|
+
const manifest = snapshot(join23(dirname15(dirname15(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
17045
17309
|
projectedHookAcl(manifest.file, manifest.stat);
|
|
17046
17310
|
const pkg = JSON.parse(manifest.text);
|
|
17047
|
-
if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join23(
|
|
17311
|
+
if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join23(dirname15(manifest.file), "bin/index.js"))
|
|
17048
17312
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17049
17313
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
17050
17314
|
projectedHookAcl(cli.file, cli.stat);
|
|
@@ -17053,7 +17317,7 @@ function projectedHookExecutable(command, expectedResolved) {
|
|
|
17053
17317
|
const recheck = () => {
|
|
17054
17318
|
try {
|
|
17055
17319
|
const again = resolveLauncherCommand(command);
|
|
17056
|
-
if (again.entry !== resolved || again.physical !== target.physical || linkIdentity(lstatSync20(command, { bigint: true })) !== linkIdentity(link) || !isDeepStrictEqual6(parents2(), parentIdentity) || !isDeepStrictEqual6(parents2(resolved), resolvedParentIdentity))
|
|
17320
|
+
if (again.entry !== resolved || again.physical !== target.physical || linkIdentity(lstatSync20(command, { bigint: true })) !== linkIdentity(link) || !direct() || !isDeepStrictEqual6(parents2(), parentIdentity) || !isDeepStrictEqual6(parents2(resolved), resolvedParentIdentity))
|
|
17057
17321
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17058
17322
|
if (launcher) {
|
|
17059
17323
|
if (!isDeepStrictEqual6(parents2(target.physical), launcherParentIdentity))
|
|
@@ -17061,6 +17325,12 @@ function projectedHookExecutable(command, expectedResolved) {
|
|
|
17061
17325
|
projectedHookAcl(launcher.file, launcher.stat);
|
|
17062
17326
|
unchanged2(launcher);
|
|
17063
17327
|
}
|
|
17328
|
+
if (runtime) {
|
|
17329
|
+
assertPinnedLauncherRuntimeSafe(runtime.path);
|
|
17330
|
+
if (!isDeepStrictEqual6(parents2(runtime.path), runtime.parents))
|
|
17331
|
+
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17332
|
+
projectedHookAcl(runtime.path, runtime.stat);
|
|
17333
|
+
}
|
|
17064
17334
|
projectedHookAcl(cli.file, cli.stat);
|
|
17065
17335
|
projectedHookAcl(manifest.file, manifest.stat);
|
|
17066
17336
|
unchanged2(cli);
|
|
@@ -17087,7 +17357,7 @@ function planClaudeManagedHookProjection(options) {
|
|
|
17087
17357
|
} catch {
|
|
17088
17358
|
hookProjectionRefusal("TARGET_INVALID");
|
|
17089
17359
|
}
|
|
17090
|
-
const home =
|
|
17360
|
+
const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17091
17361
|
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
17092
17362
|
let policyBytes, settingsBytes, settingsPath, command, profile;
|
|
17093
17363
|
try {
|
|
@@ -17179,29 +17449,28 @@ function planClaudePreToolUseHookUpdate(options) {
|
|
|
17179
17449
|
return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
|
|
17180
17450
|
}
|
|
17181
17451
|
function claudeSettingsConsumers(home, path, discoveries, aliases) {
|
|
17182
|
-
const
|
|
17452
|
+
const canonical7 = (value) => canonicalAgentPath(value, aliases);
|
|
17183
17453
|
const containsSettings = (root) => {
|
|
17184
|
-
const distance = relative6(
|
|
17185
|
-
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !
|
|
17454
|
+
const distance = relative6(canonical7(root), path);
|
|
17455
|
+
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !isAbsolute16(distance);
|
|
17186
17456
|
};
|
|
17187
|
-
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" ||
|
|
17457
|
+
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical7(join23(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical7(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
|
|
17188
17458
|
}
|
|
17189
|
-
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
17459
|
+
function verifyCoordinatedDiscovery(binding, home, aliases, executable2) {
|
|
17190
17460
|
try {
|
|
17191
17461
|
if (binding.method !== "automatic" && binding.method !== "reviewed")
|
|
17192
17462
|
throw new Error("Invalid managed discovery method");
|
|
17193
17463
|
verifyAgentDiscovery(binding);
|
|
17194
|
-
if (binding.method === "automatic")
|
|
17195
|
-
|
|
17196
|
-
if (JSON.stringify(current) !== JSON.stringify(binding))
|
|
17197
|
-
throw new Error("Configured native discovery roots changed during Claude update");
|
|
17198
|
-
}
|
|
17464
|
+
if (binding.method === "automatic")
|
|
17465
|
+
verifyAutomaticDiscoveryClosure(binding, { home, canonical: (path) => canonicalAgentPath(path, aliases), change: "Configured native discovery roots changed during Claude update", executable: executable2 });
|
|
17199
17466
|
} catch (error) {
|
|
17467
|
+
if (isDiscoveryRootUnresolved(error))
|
|
17468
|
+
throw error;
|
|
17200
17469
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
17201
17470
|
}
|
|
17202
17471
|
}
|
|
17203
17472
|
function planClaudeHookEventsUpdate(options) {
|
|
17204
|
-
const home =
|
|
17473
|
+
const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17205
17474
|
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
17206
17475
|
assertSafePath(policyPath);
|
|
17207
17476
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
@@ -17227,8 +17496,9 @@ function planClaudeHookEventsUpdate(options) {
|
|
|
17227
17496
|
});
|
|
17228
17497
|
if (discoveries.length !== agents.length || !agents.includes("claude"))
|
|
17229
17498
|
throw new Error("Missing managed Claude discovery coverage");
|
|
17499
|
+
const discoveryExecutables = bridge.discoveryExecutables;
|
|
17230
17500
|
for (const binding of discoveries)
|
|
17231
|
-
verifyCoordinatedDiscovery(binding, home, aliases);
|
|
17501
|
+
verifyCoordinatedDiscovery(binding, home, aliases, discoveryExecutables?.[binding.agent]);
|
|
17232
17502
|
const consumers = claudeSettingsConsumers(home, path, discoveries, aliases);
|
|
17233
17503
|
for (const agent of consumers)
|
|
17234
17504
|
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: home });
|
|
@@ -17263,13 +17533,14 @@ function planClaudeHookEventsUpdate(options) {
|
|
|
17263
17533
|
observedPolicy: { path: policyPath, before: snapshot2.text },
|
|
17264
17534
|
discoveryBefore: discoveries,
|
|
17265
17535
|
discoveryAfter,
|
|
17536
|
+
...discoveryExecutables ? { discoveryExecutables } : {},
|
|
17266
17537
|
rootAliases: aliases,
|
|
17267
17538
|
managedAgentChecks: { home, agents: consumers }
|
|
17268
17539
|
};
|
|
17269
17540
|
}
|
|
17270
17541
|
function atomicWrite2(path, content) {
|
|
17271
17542
|
assertSafePath(path);
|
|
17272
|
-
mkdirSync6(
|
|
17543
|
+
mkdirSync6(dirname15(path), { recursive: true, mode: 448 });
|
|
17273
17544
|
const temporary = `${path}.skills-${randomUUID3()}`;
|
|
17274
17545
|
try {
|
|
17275
17546
|
writeFileSync6(temporary, content, { mode: 384, flag: "wx" });
|
|
@@ -17284,7 +17555,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
17284
17555
|
throw new Error("Invalid Codex witness target version");
|
|
17285
17556
|
if (options.targetClaudeVersion !== undefined && (options.agent !== "claude" || ![3, 4].includes(options.targetClaudeVersion)))
|
|
17286
17557
|
throw new Error("Invalid Claude witness target version");
|
|
17287
|
-
const home =
|
|
17558
|
+
const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17288
17559
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
17289
17560
|
if (!snapshot2 || sha3(snapshot2.text) !== options.expectedPolicySha256)
|
|
17290
17561
|
throw new Error("Managed policy preimage changed");
|
|
@@ -17344,7 +17615,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
17344
17615
|
const replacement = { ...binding, sources: rebound };
|
|
17345
17616
|
verifyAgentDiscovery(replacement);
|
|
17346
17617
|
const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
17347
|
-
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join23(dataDir2, "agent-policy.json"), before: snapshot2.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 })) } };
|
|
17618
|
+
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {}, 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 })) } };
|
|
17348
17619
|
}
|
|
17349
17620
|
function applyAgentIntegration(plan, options = {}) {
|
|
17350
17621
|
const roots = plan.changes.flatMap((change) => {
|
|
@@ -17362,10 +17633,10 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17362
17633
|
throw new Error("Native settings changed after witness planning");
|
|
17363
17634
|
const policyPath = join23(plan.dataDir, "agent-policy.json");
|
|
17364
17635
|
assertSafePath(policyPath);
|
|
17365
|
-
const currentText = (path) =>
|
|
17636
|
+
const currentText = (path) => resolve15(path) === resolve15(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
|
|
17366
17637
|
readManagedSkillPolicySnapshot(plan.dataDir);
|
|
17367
17638
|
for (const change of plan.changes)
|
|
17368
|
-
if (
|
|
17639
|
+
if (resolve15(change.path) === resolve15(policyPath))
|
|
17369
17640
|
parseManagedSkillPolicy(change.after);
|
|
17370
17641
|
const aliases = plan.rootAliases ?? [];
|
|
17371
17642
|
assertCurrent();
|
|
@@ -17386,7 +17657,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17386
17657
|
assertManagedAgentBridgeWithDiscovery(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home }, provenDiscovery?.agent === agent ? provenDiscovery : undefined);
|
|
17387
17658
|
for (const binding of plan.discoveryBefore ?? []) {
|
|
17388
17659
|
if (plan.managedAgentChecks)
|
|
17389
|
-
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
|
|
17660
|
+
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases, plan.discoveryExecutables?.[binding.agent]);
|
|
17390
17661
|
else
|
|
17391
17662
|
verifyAgentDiscovery(binding);
|
|
17392
17663
|
}
|
|
@@ -17418,7 +17689,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17418
17689
|
recheckRootAliases(aliases);
|
|
17419
17690
|
if (currentText(change.path) !== change.before)
|
|
17420
17691
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
17421
|
-
for (let directory =
|
|
17692
|
+
for (let directory = dirname15(change.path);!existsSync9(directory); directory = dirname15(directory))
|
|
17422
17693
|
createdDirectories.add(directory);
|
|
17423
17694
|
const after = change.after;
|
|
17424
17695
|
atomicWrite2(change.path, after);
|
|
@@ -17433,7 +17704,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17433
17704
|
throw new Error("Native settings changed during witness application");
|
|
17434
17705
|
for (const binding of plan.discoveryAfter ?? []) {
|
|
17435
17706
|
if (plan.managedAgentChecks)
|
|
17436
|
-
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
|
|
17707
|
+
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases, plan.discoveryExecutables?.[binding.agent]);
|
|
17437
17708
|
else
|
|
17438
17709
|
verifyAgentDiscovery(binding);
|
|
17439
17710
|
}
|
|
@@ -17490,7 +17761,7 @@ function writeArchiveJournal(path, value) {
|
|
|
17490
17761
|
closeSync11(descriptor);
|
|
17491
17762
|
descriptor = undefined;
|
|
17492
17763
|
renameSync3(temporary, path);
|
|
17493
|
-
syncArchiveDirectory(
|
|
17764
|
+
syncArchiveDirectory(dirname15(path));
|
|
17494
17765
|
} finally {
|
|
17495
17766
|
if (descriptor !== undefined)
|
|
17496
17767
|
closeSync11(descriptor);
|
|
@@ -17548,14 +17819,14 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17548
17819
|
};
|
|
17549
17820
|
assertSafePath(archiveRoot);
|
|
17550
17821
|
const created = [];
|
|
17551
|
-
for (let path = archiveRoot;!existsSync9(path); path =
|
|
17822
|
+
for (let path = archiveRoot;!existsSync9(path); path = dirname15(path))
|
|
17552
17823
|
created.push(path);
|
|
17553
17824
|
mkdirSync6(archiveRoot, { recursive: true, mode: 448 });
|
|
17554
17825
|
try {
|
|
17555
17826
|
for (const directory of created)
|
|
17556
17827
|
syncArchiveDirectory(directory);
|
|
17557
17828
|
if (created.length)
|
|
17558
|
-
syncArchiveDirectory(
|
|
17829
|
+
syncArchiveDirectory(dirname15(created.at(-1)));
|
|
17559
17830
|
writeArchiveJournal(receiptPath, journal);
|
|
17560
17831
|
for (const [index, entry] of selected.entries()) {
|
|
17561
17832
|
assertCurrent();
|
|
@@ -17568,7 +17839,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17568
17839
|
writeArchiveJournal(receiptPath, journal);
|
|
17569
17840
|
renameSync3(move.source, move.archive);
|
|
17570
17841
|
moved.push(move);
|
|
17571
|
-
syncArchiveDirectory(
|
|
17842
|
+
syncArchiveDirectory(dirname15(move.source));
|
|
17572
17843
|
syncArchiveDirectory(archiveRoot);
|
|
17573
17844
|
verifyArchive(move);
|
|
17574
17845
|
move.status = "archived";
|
|
@@ -17605,7 +17876,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17605
17876
|
}
|
|
17606
17877
|
if (entry.discoveryOnly) {
|
|
17607
17878
|
linkSync2(entry.archive, entry.source);
|
|
17608
|
-
syncArchiveDirectory(
|
|
17879
|
+
syncArchiveDirectory(dirname15(entry.source));
|
|
17609
17880
|
unlinkSync4(entry.archive);
|
|
17610
17881
|
} else {
|
|
17611
17882
|
if (lstatSync20(entry.source, { throwIfNoEntry: false })) {
|
|
@@ -17616,7 +17887,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17616
17887
|
renameSync3(entry.archive, entry.source);
|
|
17617
17888
|
}
|
|
17618
17889
|
entry.status = "restored";
|
|
17619
|
-
syncArchiveDirectory(
|
|
17890
|
+
syncArchiveDirectory(dirname15(entry.source));
|
|
17620
17891
|
syncArchiveDirectory(archiveRoot);
|
|
17621
17892
|
} catch {
|
|
17622
17893
|
entry.status = "recovery-required";
|
|
@@ -17641,7 +17912,7 @@ function isInertCodexPluginCacheCopy(entry, cache, allowedHostPaths, read2) {
|
|
|
17641
17912
|
return false;
|
|
17642
17913
|
const document2 = join23(entry.path, "SKILL.md");
|
|
17643
17914
|
const inside = relative6(cache, entry.path);
|
|
17644
|
-
if (!inside ||
|
|
17915
|
+
if (!inside || isAbsolute16(inside) || inside.split(sep7).some((segment) => segment === ".."))
|
|
17645
17916
|
return false;
|
|
17646
17917
|
assertSafePath(entry.path);
|
|
17647
17918
|
if (!lstatSync20(cache).isDirectory() || !lstatSync20(entry.path).isDirectory() || !lstatSync20(document2).isFile())
|
|
@@ -17661,7 +17932,7 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
17661
17932
|
assertManagedAgentBridgeWithDiscovery(agent, options);
|
|
17662
17933
|
}
|
|
17663
17934
|
function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
|
|
17664
|
-
const home =
|
|
17935
|
+
const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17665
17936
|
assertSafePath(join23(dataDir2, "agent-policy.json"));
|
|
17666
17937
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
17667
17938
|
if (!snapshot2)
|
|
@@ -17759,12 +18030,11 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
17759
18030
|
verifyAgentDiscovery(agent === "codex" ? { ...discovery, codexDisabledPluginSkills: binding.codexPluginSkills ?? [] } : discovery, options.codexDiscoveryRecovery);
|
|
17760
18031
|
if (agent === "sumi" && discovery.method === "reviewed")
|
|
17761
18032
|
resolveAgentDiscovery({ home, agent, retainedReview: discovery, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
17762
|
-
if (discovery.method === "automatic")
|
|
17763
|
-
|
|
17764
|
-
if (JSON.stringify(current) !== JSON.stringify(discovery))
|
|
17765
|
-
throw new Error("Configured native discovery roots changed");
|
|
17766
|
-
}
|
|
18033
|
+
if (discovery.method === "automatic")
|
|
18034
|
+
verifyAutomaticDiscoveryClosure(discovery, { home, canonical: (path) => canonicalAgentPath(path, aliases), change: "Configured native discovery roots changed", executable: binding.discoveryExecutables?.[agent] });
|
|
17767
18035
|
} catch (error) {
|
|
18036
|
+
if (isDiscoveryRootUnresolved(error))
|
|
18037
|
+
throw error;
|
|
17768
18038
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
17769
18039
|
}
|
|
17770
18040
|
let installationInputRoots = [];
|
|
@@ -18057,9 +18327,9 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
|
|
|
18057
18327
|
const action = dirExists ? "update" : "create";
|
|
18058
18328
|
if (options.dryRun)
|
|
18059
18329
|
return { action, path: skillMdPath };
|
|
18060
|
-
const parentDir =
|
|
18330
|
+
const parentDir = dirname16(dir);
|
|
18061
18331
|
mkdirSync7(parentDir, { recursive: true });
|
|
18062
|
-
const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${
|
|
18332
|
+
const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${basename8(dir)}-`));
|
|
18063
18333
|
const candidateDir = join25(transactionDir, "candidate");
|
|
18064
18334
|
const backupDir = join25(transactionDir, "backup");
|
|
18065
18335
|
const candidateSkillMdPath = join25(candidateDir, "SKILL.md");
|
|
@@ -19051,7 +19321,7 @@ function exactProfileSelection(spec, profile) {
|
|
|
19051
19321
|
}
|
|
19052
19322
|
|
|
19053
19323
|
// src/lib/plugin-projection.ts
|
|
19054
|
-
import { createHash as
|
|
19324
|
+
import { createHash as createHash21 } from "crypto";
|
|
19055
19325
|
import { posix } from "path";
|
|
19056
19326
|
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
|
|
19057
19327
|
var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -19062,7 +19332,7 @@ function pluginNeed(value, message) {
|
|
|
19062
19332
|
if (!value)
|
|
19063
19333
|
pluginRefusal(message);
|
|
19064
19334
|
}
|
|
19065
|
-
var pluginHash = (bytes) =>
|
|
19335
|
+
var pluginHash = (bytes) => createHash21("sha256").update(bytes).digest("hex");
|
|
19066
19336
|
function pluginObject(value) {
|
|
19067
19337
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
19068
19338
|
}
|
|
@@ -19250,9 +19520,9 @@ function buildPluginProjection(entries) {
|
|
|
19250
19520
|
}
|
|
19251
19521
|
|
|
19252
19522
|
// src/lib/plugin-projection-store.ts
|
|
19253
|
-
import { createHash as
|
|
19523
|
+
import { createHash as createHash22, randomUUID as randomUUID4 } from "crypto";
|
|
19254
19524
|
import { constants as constants12, closeSync as closeSync12, fchmodSync as fchmodSync2, fstatSync as fstatSync12, fsyncSync as fsyncSync6, linkSync as linkSync3, lstatSync as lstatSync22, mkdirSync as mkdirSync9, openSync as openSync12, opendirSync as opendirSync4, readSync as readSync9, renameSync as renameSync5, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync5, writeSync as writeSync3 } from "fs";
|
|
19255
|
-
import { dirname as
|
|
19525
|
+
import { dirname as dirname17, isAbsolute as isAbsolute17, join as join29, parse as parse6, resolve as resolve16 } from "path";
|
|
19256
19526
|
var READ_FLAGS = constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
|
|
19257
19527
|
var WRITE_FLAGS = constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
|
|
19258
19528
|
var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
|
|
@@ -19266,8 +19536,8 @@ function stat(path) {
|
|
|
19266
19536
|
return pluginRefusal("Cannot inspect plugin filesystem entry");
|
|
19267
19537
|
}
|
|
19268
19538
|
}
|
|
19269
|
-
function
|
|
19270
|
-
pluginNeed(typeof path === "string" && path.length <= 8192 &&
|
|
19539
|
+
function canonical7(path) {
|
|
19540
|
+
pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute17(path) && resolve16(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
|
|
19271
19541
|
}
|
|
19272
19542
|
function owner(stats, system = false) {
|
|
19273
19543
|
const current = uid();
|
|
@@ -19277,8 +19547,8 @@ function same3(left, right, identityOnly = false) {
|
|
|
19277
19547
|
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");
|
|
19278
19548
|
}
|
|
19279
19549
|
function ancestors(path, create = false) {
|
|
19280
|
-
|
|
19281
|
-
const result = [], parent =
|
|
19550
|
+
canonical7(path);
|
|
19551
|
+
const result = [], parent = dirname17(path), filesystemRoot = parse6(parent).root;
|
|
19282
19552
|
let current = filesystemRoot;
|
|
19283
19553
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
19284
19554
|
if (segment)
|
|
@@ -19306,7 +19576,7 @@ function verifyAncestors(chain, identityOnly = true) {
|
|
|
19306
19576
|
}
|
|
19307
19577
|
function privateParent(path) {
|
|
19308
19578
|
const chain = ancestors(path, true), parent = chain.at(-1);
|
|
19309
|
-
pluginNeed(parent && parent.path ===
|
|
19579
|
+
pluginNeed(parent && parent.path === dirname17(path), "Plugin storage parent is unavailable");
|
|
19310
19580
|
owner(parent.stat);
|
|
19311
19581
|
pluginNeed((parent.stat.mode & 0o7777n) === 0o700n, "Plugin storage parent must have mode 0700");
|
|
19312
19582
|
return chain;
|
|
@@ -19592,7 +19862,7 @@ function pluginExecutableDigest(path) {
|
|
|
19592
19862
|
const opened = fstatSync12(fd, { bigint: true });
|
|
19593
19863
|
same3(before, opened);
|
|
19594
19864
|
fileShape(opened, maximum, "executable");
|
|
19595
|
-
const hash2 =
|
|
19865
|
+
const hash2 = createHash22("sha256"), buffer = new Uint8Array(64 * 1024);
|
|
19596
19866
|
let remaining = Number(opened.size);
|
|
19597
19867
|
while (remaining > 0) {
|
|
19598
19868
|
const count = readSync9(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
|
|
@@ -19650,7 +19920,7 @@ function validatePluginTarget(value) {
|
|
|
19650
19920
|
for (const registration of value.registrations) {
|
|
19651
19921
|
pluginKeys(registration, ["scope", "projectPath"]);
|
|
19652
19922
|
pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
|
|
19653
|
-
pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" &&
|
|
19923
|
+
pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute18(registration.projectPath) && resolve17(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
|
|
19654
19924
|
const key = JSON.stringify([registration.scope, registration.projectPath]);
|
|
19655
19925
|
pluginNeed(!scopes.has(key), "Duplicate plugin registration scope");
|
|
19656
19926
|
scopes.add(key);
|
|
@@ -19658,7 +19928,7 @@ function validatePluginTarget(value) {
|
|
|
19658
19928
|
for (const key of ["native", "resolver"]) {
|
|
19659
19929
|
pluginKeys(value[key], key === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
|
|
19660
19930
|
pluginText(value[key].executable);
|
|
19661
|
-
pluginNeed(
|
|
19931
|
+
pluginNeed(isAbsolute18(value[key].executable) && resolve17(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
|
|
19662
19932
|
pluginDigest(value[key].digest);
|
|
19663
19933
|
}
|
|
19664
19934
|
pluginNeed(["2.1.274", "2.1.276"].includes(value.native.version), "Plugin admission requires a certified Claude command-source runtime (2.1.274 or 2.1.276)");
|
|
@@ -19803,14 +20073,14 @@ function validatePlan(value) {
|
|
|
19803
20073
|
pluginNeed(observedEvidenceDigest(plan.observation) === plan.evidenceDigest, "Plugin receipt observed evidence changed");
|
|
19804
20074
|
pluginNeed(identityDigest(plan) === plan.planDigest, "Plugin receipt immutable identity changed");
|
|
19805
20075
|
}
|
|
19806
|
-
var
|
|
20076
|
+
var quote2 = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
19807
20077
|
var resolverGuard = 'exec 9<"$1"; actual=$(/usr/bin/sha256sum /proc/self/fd/9); actual=${actual%% *}; [ "sha256:$actual" = "$2" ]; exec /proc/self/fd/9 integration plugin resolve --binding "$3"';
|
|
19808
20078
|
function pluginResolverCommand(binding) {
|
|
19809
|
-
return `${
|
|
20079
|
+
return `${quote2("/bin/sh")} -eu -c ${quote2(resolverGuard)} plugin-resolver ${quote2(binding.target.resolver.executable)} ${quote2(binding.target.resolver.digest)} ${quote2(bindingId(binding))}`;
|
|
19810
20080
|
}
|
|
19811
20081
|
function root(options) {
|
|
19812
20082
|
const path = options.storeRoot ?? pluginAdmissionRoot();
|
|
19813
|
-
pluginNeed(
|
|
20083
|
+
pluginNeed(isAbsolute18(path) && resolve17(path) === path, "Invalid plugin admission store root");
|
|
19814
20084
|
return path;
|
|
19815
20085
|
}
|
|
19816
20086
|
function pluginBindingPath(storeRoot, id) {
|
|
@@ -20015,15 +20285,15 @@ function validateManagedPluginWitnesses(value) {
|
|
|
20015
20285
|
pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
|
|
20016
20286
|
seen.add(item.bindingId);
|
|
20017
20287
|
pluginText(item.storeRoot);
|
|
20018
|
-
pluginNeed(
|
|
20288
|
+
pluginNeed(isAbsolute19(item.storeRoot) && resolve18(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
|
|
20019
20289
|
}
|
|
20020
20290
|
}
|
|
20021
20291
|
function safe3(path) {
|
|
20022
20292
|
pluginText(path);
|
|
20023
|
-
pluginNeed(
|
|
20024
|
-
for (let at = path;; at =
|
|
20293
|
+
pluginNeed(isAbsolute19(path) && resolve18(path) === path, "Invalid plugin registry path");
|
|
20294
|
+
for (let at = path;; at = dirname18(at)) {
|
|
20025
20295
|
pluginNeed(!lstatSync23(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
|
|
20026
|
-
if (at ===
|
|
20296
|
+
if (at === dirname18(at))
|
|
20027
20297
|
break;
|
|
20028
20298
|
}
|
|
20029
20299
|
}
|
|
@@ -20073,7 +20343,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
20073
20343
|
seen.add(target.pluginId);
|
|
20074
20344
|
const rows = plugins[target.pluginId];
|
|
20075
20345
|
pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
|
|
20076
|
-
const [name, market] = target.pluginId.split("@"), cacheRoot = join31(
|
|
20346
|
+
const [name, market] = target.pluginId.split("@"), cacheRoot = join31(dirname18(path), "cache", market, name);
|
|
20077
20347
|
const scopes = new Set, approvals = new Map, currentFiles = new Map;
|
|
20078
20348
|
plugins[target.pluginId] = rows.map((row) => {
|
|
20079
20349
|
pluginNeed(pluginObject(row), "Invalid managed plugin registration");
|
|
@@ -20089,7 +20359,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
20089
20359
|
pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join31(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
|
|
20090
20360
|
const approval = (producer) => {
|
|
20091
20361
|
pluginText(producer);
|
|
20092
|
-
const digest = `sha256:${
|
|
20362
|
+
const digest = `sha256:${basename9(producer)}`;
|
|
20093
20363
|
const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest);
|
|
20094
20364
|
pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
|
|
20095
20365
|
if (!verified.has(producer)) {
|
|
@@ -20151,39 +20421,39 @@ function captureManagedPluginRegistry(path, managedPlugins) {
|
|
|
20151
20421
|
}
|
|
20152
20422
|
|
|
20153
20423
|
// src/lib/claude-marketplace-registry.ts
|
|
20154
|
-
import { createHash as
|
|
20424
|
+
import { createHash as createHash23 } from "crypto";
|
|
20155
20425
|
import { closeSync as closeSync14, constants as constants14, fstatSync as fstatSync14, lstatSync as lstatSync24, openSync as openSync14, readSync as readSync11 } from "fs";
|
|
20156
|
-
import { basename as
|
|
20426
|
+
import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute20, resolve as resolve19 } from "path";
|
|
20157
20427
|
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
20158
|
-
function
|
|
20428
|
+
function need7(value, reason2) {
|
|
20159
20429
|
if (!value)
|
|
20160
|
-
throw new Error(`Claude marketplace registry ${
|
|
20430
|
+
throw new Error(`Claude marketplace registry ${reason2}`);
|
|
20161
20431
|
}
|
|
20162
20432
|
function absolutePath3(value) {
|
|
20163
|
-
|
|
20433
|
+
need7(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute20(value) && resolve19(value) === value, "requires a normalized absolute path");
|
|
20164
20434
|
}
|
|
20165
20435
|
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;
|
|
20166
20436
|
function parents2(path) {
|
|
20167
20437
|
const result = [];
|
|
20168
|
-
for (let at =
|
|
20438
|
+
for (let at = dirname19(path);; at = dirname19(at)) {
|
|
20169
20439
|
const stat2 = lstatSync24(at, { bigint: true });
|
|
20170
|
-
|
|
20440
|
+
need7(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
20171
20441
|
result.push([at, stat2]);
|
|
20172
|
-
if (at ===
|
|
20442
|
+
if (at === dirname19(at))
|
|
20173
20443
|
return result;
|
|
20174
20444
|
}
|
|
20175
20445
|
}
|
|
20176
20446
|
function readRegistry2(path, budget2) {
|
|
20177
20447
|
absolutePath3(path);
|
|
20178
|
-
|
|
20179
|
-
|
|
20448
|
+
need7(basename10(path) === "known_marketplaces.json", "requires known_marketplaces.json");
|
|
20449
|
+
need7(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
|
|
20180
20450
|
const ancestors2 = parents2(path), initial = lstatSync24(path, { bigint: true });
|
|
20181
|
-
|
|
20182
|
-
|
|
20451
|
+
need7(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
|
|
20452
|
+
need7(initial.size <= BigInt(budget2.remaining), "exceeds its aggregate byte limit");
|
|
20183
20453
|
const fd = openSync14(path, constants14.O_RDONLY | constants14.O_NOFOLLOW | constants14.O_NONBLOCK);
|
|
20184
20454
|
try {
|
|
20185
20455
|
const opened = fstatSync14(fd, { bigint: true });
|
|
20186
|
-
|
|
20456
|
+
need7(opened.isFile() && sameFile2(initial, opened), "changed during open");
|
|
20187
20457
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
20188
20458
|
let length = 0;
|
|
20189
20459
|
while (length < bytes.length) {
|
|
@@ -20193,10 +20463,10 @@ function readRegistry2(path, budget2) {
|
|
|
20193
20463
|
length += count;
|
|
20194
20464
|
}
|
|
20195
20465
|
const after = fstatSync14(fd, { bigint: true }), current = lstatSync24(path, { bigint: true });
|
|
20196
|
-
|
|
20466
|
+
need7(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
20197
20467
|
for (const [ancestor, before] of ancestors2) {
|
|
20198
20468
|
const now = lstatSync24(ancestor, { bigint: true });
|
|
20199
|
-
|
|
20469
|
+
need7(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
20200
20470
|
}
|
|
20201
20471
|
budget2.remaining -= length;
|
|
20202
20472
|
try {
|
|
@@ -20216,7 +20486,7 @@ function parse7(text2) {
|
|
|
20216
20486
|
at++;
|
|
20217
20487
|
};
|
|
20218
20488
|
function string() {
|
|
20219
|
-
|
|
20489
|
+
need7(text2[at] === '"', "contains invalid JSON");
|
|
20220
20490
|
const start = at++;
|
|
20221
20491
|
while (at < text2.length) {
|
|
20222
20492
|
const character = text2[at++];
|
|
@@ -20227,7 +20497,7 @@ function parse7(text2) {
|
|
|
20227
20497
|
} catch {
|
|
20228
20498
|
throw new Error("Claude marketplace registry contains invalid JSON string");
|
|
20229
20499
|
}
|
|
20230
|
-
|
|
20500
|
+
need7(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
|
|
20231
20501
|
return value2;
|
|
20232
20502
|
}
|
|
20233
20503
|
if (character === "\\")
|
|
@@ -20236,7 +20506,7 @@ function parse7(text2) {
|
|
|
20236
20506
|
throw new Error("Claude marketplace registry contains an unterminated JSON string");
|
|
20237
20507
|
}
|
|
20238
20508
|
function value(depth) {
|
|
20239
|
-
|
|
20509
|
+
need7(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
20240
20510
|
space();
|
|
20241
20511
|
const character = text2[at];
|
|
20242
20512
|
if (character === '"')
|
|
@@ -20250,17 +20520,17 @@ function parse7(text2) {
|
|
|
20250
20520
|
if (object6) {
|
|
20251
20521
|
space();
|
|
20252
20522
|
const key = string();
|
|
20253
|
-
|
|
20523
|
+
need7(!keys.has(key), "contains a duplicate JSON key");
|
|
20254
20524
|
keys.add(key);
|
|
20255
20525
|
space();
|
|
20256
|
-
|
|
20526
|
+
need7(text2[at++] === ":", "contains invalid JSON");
|
|
20257
20527
|
entries.push([key, value(depth + 1)]);
|
|
20258
20528
|
} else
|
|
20259
20529
|
items.push(value(depth + 1));
|
|
20260
20530
|
space();
|
|
20261
20531
|
if (text2[at] === end)
|
|
20262
20532
|
break;
|
|
20263
|
-
|
|
20533
|
+
need7(text2[at++] === ",", "contains invalid JSON");
|
|
20264
20534
|
}
|
|
20265
20535
|
at++;
|
|
20266
20536
|
return object6 ? { kind: "object", entries } : { kind: "array", items };
|
|
@@ -20272,67 +20542,67 @@ function parse7(text2) {
|
|
|
20272
20542
|
}
|
|
20273
20543
|
numberPattern.lastIndex = at;
|
|
20274
20544
|
const number = numberPattern.exec(text2);
|
|
20275
|
-
|
|
20545
|
+
need7(number, "contains invalid JSON");
|
|
20276
20546
|
at += number[0].length;
|
|
20277
20547
|
return { kind: "number", value: number[0] };
|
|
20278
20548
|
}
|
|
20279
20549
|
const root2 = value(0);
|
|
20280
20550
|
space();
|
|
20281
|
-
|
|
20282
|
-
|
|
20551
|
+
need7(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
|
|
20552
|
+
need7(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
|
|
20283
20553
|
return root2;
|
|
20284
20554
|
}
|
|
20285
20555
|
function exactKeys(value, keys) {
|
|
20286
20556
|
return value?.kind === "object" && value.entries.length === keys.length && value.entries.every(([key]) => keys.includes(key));
|
|
20287
20557
|
}
|
|
20288
|
-
var
|
|
20558
|
+
var field2 = (object6, key) => object6.entries.find(([name]) => name === key)?.[1];
|
|
20289
20559
|
function textField(value) {
|
|
20290
|
-
|
|
20560
|
+
need7(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
|
|
20291
20561
|
return value.value;
|
|
20292
20562
|
}
|
|
20293
20563
|
function normalizeRow(row, version = 1) {
|
|
20294
20564
|
if (!exactKeys(row, ["source", "installLocation", "lastUpdated"])) {
|
|
20295
20565
|
if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
20296
20566
|
return;
|
|
20297
|
-
const autoUpdate =
|
|
20298
|
-
|
|
20567
|
+
const autoUpdate = field2(row, "autoUpdate");
|
|
20568
|
+
need7(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
20299
20569
|
}
|
|
20300
|
-
const source =
|
|
20570
|
+
const source = field2(row, "source");
|
|
20301
20571
|
if (source?.kind !== "object")
|
|
20302
20572
|
return;
|
|
20303
|
-
const type =
|
|
20573
|
+
const type = field2(source, "source");
|
|
20304
20574
|
if (type?.kind !== "string" || !["github", "directory"].includes(type.value))
|
|
20305
20575
|
return;
|
|
20306
20576
|
const location = type.value === "github" ? "repo" : "path";
|
|
20307
20577
|
if (!exactKeys(source, ["source", location]))
|
|
20308
20578
|
return;
|
|
20309
|
-
const sourceLocation = textField(
|
|
20579
|
+
const sourceLocation = textField(field2(source, location));
|
|
20310
20580
|
if (type.value === "directory")
|
|
20311
20581
|
absolutePath3(sourceLocation);
|
|
20312
|
-
absolutePath3(textField(
|
|
20313
|
-
const updated = textField(
|
|
20314
|
-
|
|
20582
|
+
absolutePath3(textField(field2(row, "installLocation")));
|
|
20583
|
+
const updated = textField(field2(row, "lastUpdated"));
|
|
20584
|
+
need7(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
|
|
20315
20585
|
const millis = Date.parse(updated);
|
|
20316
|
-
|
|
20586
|
+
need7(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
20317
20587
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
20318
20588
|
}
|
|
20319
20589
|
function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
20320
20590
|
const value = parse7(readRegistry2(path, budget2));
|
|
20321
20591
|
for (const [, row] of value.entries)
|
|
20322
20592
|
normalizeRow(row);
|
|
20323
|
-
const sha2563 =
|
|
20593
|
+
const sha2563 = createHash23("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
20324
20594
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2563 };
|
|
20325
20595
|
}
|
|
20326
20596
|
function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
20327
20597
|
const value = parse7(readRegistry2(path, budget2));
|
|
20328
20598
|
for (const [, row] of value.entries)
|
|
20329
20599
|
normalizeRow(row, 2);
|
|
20330
|
-
const sha2563 =
|
|
20600
|
+
const sha2563 = createHash23("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
20331
20601
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2563 };
|
|
20332
20602
|
}
|
|
20333
20603
|
|
|
20334
20604
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
20335
|
-
import { createHash as
|
|
20605
|
+
import { createHash as createHash24 } from "crypto";
|
|
20336
20606
|
|
|
20337
20607
|
// src/lib/codex-hook-additions-review.ts
|
|
20338
20608
|
var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
@@ -20340,12 +20610,12 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
|
20340
20610
|
var proofs = new WeakMap;
|
|
20341
20611
|
|
|
20342
20612
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
20343
|
-
var sha4 = (text2) =>
|
|
20613
|
+
var sha4 = (text2) => createHash24("sha256").update(text2).digest("hex");
|
|
20344
20614
|
var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
|
|
20345
20615
|
var proofs2 = new WeakMap;
|
|
20346
20616
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
20347
20617
|
const proof = proofs2.get(witness);
|
|
20348
|
-
|
|
20618
|
+
need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
20349
20619
|
unchanged2(proof.before);
|
|
20350
20620
|
unchanged2(proof.current);
|
|
20351
20621
|
proof.recheckAdditions();
|
|
@@ -20354,12 +20624,43 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
20354
20624
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
20355
20625
|
return false;
|
|
20356
20626
|
assertCodexHookDiscoveryRecovery(binding, witness);
|
|
20357
|
-
|
|
20627
|
+
need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
20358
20628
|
return true;
|
|
20359
20629
|
}
|
|
20360
20630
|
|
|
20361
20631
|
// src/lib/agent-discovery.ts
|
|
20362
|
-
var
|
|
20632
|
+
var DISCOVERY_RUNTIME_COMMANDS = Object.freeze({ gemini: "gemini" });
|
|
20633
|
+
var DISCOVERY_ROOT_UNRESOLVED = "DISCOVERY_ROOT_UNRESOLVED";
|
|
20634
|
+
function discoveryRootUnresolved(agent, command, detail) {
|
|
20635
|
+
return new Error(`${DISCOVERY_ROOT_UNRESOLVED}: ${agent} discovery cannot resolve its "${command}" executable: ${detail}. The reviewed discovery is unchanged; this is an environment gap, not drift.`);
|
|
20636
|
+
}
|
|
20637
|
+
function isDiscoveryRootUnresolved(error) {
|
|
20638
|
+
return error instanceof Error && error.message.startsWith(`${DISCOVERY_ROOT_UNRESOLVED}: `);
|
|
20639
|
+
}
|
|
20640
|
+
var quotedPath = (path) => JSON.stringify(path).replace(/[^\x20-\x7e]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
20641
|
+
var realpathOrNull = (path) => {
|
|
20642
|
+
try {
|
|
20643
|
+
return realpathSync11(path);
|
|
20644
|
+
} catch {
|
|
20645
|
+
return null;
|
|
20646
|
+
}
|
|
20647
|
+
};
|
|
20648
|
+
function discoveryExecutable(agent) {
|
|
20649
|
+
const command = DISCOVERY_RUNTIME_COMMANDS[agent];
|
|
20650
|
+
const found = command ? Bun.which(command) : null;
|
|
20651
|
+
if (!command || !found)
|
|
20652
|
+
return null;
|
|
20653
|
+
const path = resolve20(found), target = realpathOrNull(path);
|
|
20654
|
+
return target === null ? null : { command, path, target };
|
|
20655
|
+
}
|
|
20656
|
+
function recordedDiscoveryExecutable(agent, recorded) {
|
|
20657
|
+
const command = DISCOVERY_RUNTIME_COMMANDS[agent];
|
|
20658
|
+
if (!command || recorded.command !== command)
|
|
20659
|
+
throw new Error(`Recorded ${agent} discovery executable names another command`);
|
|
20660
|
+
const target = realpathOrNull(recorded.path);
|
|
20661
|
+
return target === null ? null : { command, path: recorded.path, target };
|
|
20662
|
+
}
|
|
20663
|
+
var digest = (text2) => createHash25("sha256").update(text2).digest("hex");
|
|
20363
20664
|
function parseConfig(text2, path, toml = false) {
|
|
20364
20665
|
try {
|
|
20365
20666
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -20371,12 +20672,12 @@ function parseConfig(text2, path, toml = false) {
|
|
|
20371
20672
|
}
|
|
20372
20673
|
}
|
|
20373
20674
|
function safe4(path) {
|
|
20374
|
-
if (!
|
|
20675
|
+
if (!isAbsolute21(path) || path.includes("\x00"))
|
|
20375
20676
|
throw new Error("Expected an absolute native discovery path");
|
|
20376
|
-
for (let cursor = path;; cursor =
|
|
20677
|
+
for (let cursor = path;; cursor = dirname20(cursor)) {
|
|
20377
20678
|
if (lstatSync25(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
20378
20679
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
20379
|
-
if (
|
|
20680
|
+
if (dirname20(cursor) === cursor)
|
|
20380
20681
|
break;
|
|
20381
20682
|
}
|
|
20382
20683
|
}
|
|
@@ -20395,7 +20696,7 @@ function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
|
20395
20696
|
const object6 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
|
|
20396
20697
|
if (!object6 || typeof object6 !== "object" || Array.isArray(object6))
|
|
20397
20698
|
throw new Error("Expected native discovery configuration object");
|
|
20398
|
-
return digest(JSON.stringify(Object.fromEntries(fields2.map((
|
|
20699
|
+
return digest(JSON.stringify(Object.fromEntries(fields2.map((field3) => [field3, object6[field3] ?? null]))));
|
|
20399
20700
|
}
|
|
20400
20701
|
function isSupersededSettingsProjection(agent, configPath, source) {
|
|
20401
20702
|
if (agent === "sumi")
|
|
@@ -20440,6 +20741,14 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
|
20440
20741
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
20441
20742
|
return sha2563;
|
|
20442
20743
|
}
|
|
20744
|
+
if (source.hashMode === "claude-marketplace-entry-v1") {
|
|
20745
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path) || !claudeMarketplaceEntrySourceValid(source))
|
|
20746
|
+
throw new Error("Claude marketplace entry witnesses require an exact reviewed entry without synthetic changes");
|
|
20747
|
+
const sha2563 = captureClaudeMarketplaceEntry(source.path, source.marketplace, source.plugin, budget2).sha256;
|
|
20748
|
+
if (sha2563 !== source.sha256)
|
|
20749
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
20750
|
+
return sha2563;
|
|
20751
|
+
}
|
|
20443
20752
|
if (source.hashMode === "claude-plugin-registry") {
|
|
20444
20753
|
if (source.format !== undefined || source.fields !== undefined || !source.managedPlugins || changes?.has(source.path))
|
|
20445
20754
|
throw new Error("Managed plugin registry witnesses require verified native registrations");
|
|
@@ -20477,7 +20786,7 @@ function absentCodexSkillRoots(binding) {
|
|
|
20477
20786
|
throw new Error("Invalid disabled Codex discovery proof");
|
|
20478
20787
|
if (binding.method !== "reviewed" || controls.every((control) => lstatSync25(control.pluginParent, { throwIfNoEntry: false })))
|
|
20479
20788
|
return [];
|
|
20480
|
-
const cache =
|
|
20789
|
+
const cache = dirname20(dirname20(controls[0].pluginParent)), config = join32(dirname20(dirname20(cache)), "config.toml");
|
|
20481
20790
|
const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
20482
20791
|
if (!source || projected(source) !== source.sha256)
|
|
20483
20792
|
throw new Error("Missing current disabled Codex configuration witness");
|
|
@@ -20485,7 +20794,7 @@ function absentCodexSkillRoots(binding) {
|
|
|
20485
20794
|
return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
|
|
20486
20795
|
}
|
|
20487
20796
|
function codexDirectHookSelects(cache, root2) {
|
|
20488
|
-
const codex =
|
|
20797
|
+
const codex = dirname20(dirname20(cache)), config = Bun.TOML.parse(read2(join32(codex, "config.toml")) ?? "");
|
|
20489
20798
|
const hooks = read2(join32(codex, "hooks.json"));
|
|
20490
20799
|
if (hooks !== null)
|
|
20491
20800
|
hashNativeJsonControls(hooks);
|
|
@@ -20498,7 +20807,7 @@ function retiredCodexRoots(binding) {
|
|
|
20498
20807
|
const controls = binding.codexDisabledPluginSkills;
|
|
20499
20808
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !Array.isArray(controls) || !controls.length || !Array.isArray(proof.roots) || proof.roots.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.roots).size !== proof.roots.length || !Array.isArray(proof.directories) || proof.parents !== undefined && (!Array.isArray(proof.parents) || proof.parents.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.parents).size !== proof.parents.length) || !proof.roots.length && !proof.parents?.length)
|
|
20500
20809
|
throw new Error("Invalid retired Codex materialization proof");
|
|
20501
|
-
const cache =
|
|
20810
|
+
const cache = dirname20(dirname20(controls[0].pluginParent)), config = join32(dirname20(dirname20(cache)), "config.toml");
|
|
20502
20811
|
const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
20503
20812
|
if (!source || projected(source) !== source.sha256)
|
|
20504
20813
|
throw new Error("Missing current retired Codex configuration witness");
|
|
@@ -20515,8 +20824,8 @@ function retiredCodexRoots(binding) {
|
|
|
20515
20824
|
function captureRetiredCodexDiscovery(binding, controls, rules) {
|
|
20516
20825
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !controls.length)
|
|
20517
20826
|
return binding;
|
|
20518
|
-
const cache =
|
|
20519
|
-
const candidates = binding.sources.filter((source) => source.path.endsWith(sep8 + ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode)).map((source) =>
|
|
20827
|
+
const cache = dirname20(dirname20(controls[0].pluginParent));
|
|
20828
|
+
const candidates = binding.sources.filter((source) => source.path.endsWith(sep8 + ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode)).map((source) => dirname20(dirname20(source.path)));
|
|
20520
20829
|
const roots = [...new Set(candidates)].filter((root2) => !codexDirectHookSelects(cache, root2) && reviewedRetiredCodexRoot(cache, root2, controls, rules, (path) => read2(path), binding.codexRetiredMaterializations?.roots.includes(root2) ?? false)).sort();
|
|
20521
20830
|
const parents3 = reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read2(path), rules) ? [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && reviewedDisabledCodexPluginParent(cache, parent, controls, rules)).sort() : [];
|
|
20522
20831
|
const { codexRetiredMaterializations: previous, ...current } = binding;
|
|
@@ -20545,7 +20854,7 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
20545
20854
|
safe4(root2);
|
|
20546
20855
|
if (lstatSync25(root2, { throwIfNoEntry: false }))
|
|
20547
20856
|
continue;
|
|
20548
|
-
const claude =
|
|
20857
|
+
const claude = dirname20(dirname20(cache)), settings = join32(claude, "settings.json"), registry = join32(claude, "plugins/installed_plugins.json");
|
|
20549
20858
|
const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
|
|
20550
20859
|
const configSource = full(settings), registrySource = full(registry);
|
|
20551
20860
|
if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
|
|
@@ -20562,7 +20871,7 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
20562
20871
|
if (!Array.isArray(rows) || rows.length !== 1 || rows[0]?.scope !== "user" || typeof rows[0]?.installPath !== "string")
|
|
20563
20872
|
continue;
|
|
20564
20873
|
const active = rows[0].installPath;
|
|
20565
|
-
if (rows[0].version !==
|
|
20874
|
+
if (rows[0].version !== basename11(active) || resolve20(active) !== active || dirname20(active) !== dirname20(root2) || active === root2 || !lstatSync25(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync11(active) !== active)
|
|
20566
20875
|
continue;
|
|
20567
20876
|
const activeManifest = full(join32(active, ".claude-plugin/plugin.json"));
|
|
20568
20877
|
if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
|
|
@@ -20613,7 +20922,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
20613
20922
|
assertMarketplaceBinding(binding, source);
|
|
20614
20923
|
if (source.hashMode === "claude-plugin-registry" && binding.agent !== "claude")
|
|
20615
20924
|
throw new Error("Managed Claude registry witnesses cannot apply to another agent");
|
|
20616
|
-
if (source.format !== undefined && (!["json", "toml", "yaml"].includes(source.format) || !Array.isArray(source.fields) || !source.fields.length || source.fields.length > 64 || source.fields.some((
|
|
20925
|
+
if (source.format !== undefined && (!["json", "toml", "yaml"].includes(source.format) || !Array.isArray(source.fields) || !source.fields.length || source.fields.length > 64 || source.fields.some((field3) => typeof field3 !== "string" || !field3)))
|
|
20617
20926
|
throw new Error("Invalid native discovery projection");
|
|
20618
20927
|
if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
20619
20928
|
throw new Error("Invalid native discovery digest");
|
|
@@ -20643,13 +20952,13 @@ function codexInstallationRoots(binding) {
|
|
|
20643
20952
|
return [];
|
|
20644
20953
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !supportsCodexNativeCapability(proof.version, "installed-plugin-review") || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
|
|
20645
20954
|
throw new Error("Invalid native installation input proof");
|
|
20646
|
-
const cache =
|
|
20647
|
-
const config = join32(
|
|
20955
|
+
const cache = dirname20(dirname20(proof.plugins[0].pluginParent));
|
|
20956
|
+
const config = join32(dirname20(dirname20(cache)), "config.toml"), home = dirname20(dirname20(dirname20(cache)));
|
|
20648
20957
|
const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
|
|
20649
20958
|
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join32(home, path));
|
|
20650
|
-
if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep8) || input.sourceRoot.startsWith(root2 + sep8)) || binding.sources.some((source) =>
|
|
20959
|
+
if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep8) || input.sourceRoot.startsWith(root2 + sep8)) || binding.sources.some((source) => basename11(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep8)))))
|
|
20651
20960
|
throw new Error("Native installation input overlaps configuration or loading roots");
|
|
20652
|
-
if (proof.plugins.some((input) =>
|
|
20961
|
+
if (proof.plugins.some((input) => dirname20(dirname20(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join32(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
|
|
20653
20962
|
throw new Error("Missing native installation input source witness");
|
|
20654
20963
|
return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
|
|
20655
20964
|
const text2 = read2(path);
|
|
@@ -20659,16 +20968,18 @@ function codexInstallationRoots(binding) {
|
|
|
20659
20968
|
});
|
|
20660
20969
|
}
|
|
20661
20970
|
function assertMarketplaceBinding(binding, source) {
|
|
20662
|
-
if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" ||
|
|
20971
|
+
if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename11(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json"))))
|
|
20663
20972
|
throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
|
|
20664
|
-
if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" ||
|
|
20973
|
+
if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename11(source.path) !== "sumi.json"))
|
|
20665
20974
|
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
20666
|
-
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" ||
|
|
20975
|
+
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename11(source.path) !== "config.toml"))
|
|
20667
20976
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
20668
|
-
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" ||
|
|
20977
|
+
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename11(source.path) !== "settings.json"))
|
|
20669
20978
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
20670
20979
|
if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
20671
20980
|
throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
|
|
20981
|
+
if (source.hashMode === "claude-marketplace-entry-v1" && (binding.agent !== "claude" || binding.method !== "reviewed" || !claudeMarketplaceEntrySourceValid(source)))
|
|
20982
|
+
throw new Error("Claude marketplace entry witnesses require explicit reviewed Claude discovery of one named entry");
|
|
20672
20983
|
}
|
|
20673
20984
|
function rebindAgentDiscovery(binding, changes) {
|
|
20674
20985
|
const budget2 = discoveryByteBudget();
|
|
@@ -20701,10 +21012,10 @@ function captureDiscoveryPathSources(paths) {
|
|
|
20701
21012
|
const budget2 = discoveryByteBudget();
|
|
20702
21013
|
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget2) }));
|
|
20703
21014
|
}
|
|
20704
|
-
function assertProjectDiscovery(agent, directories, home,
|
|
20705
|
-
const homeConfigPath =
|
|
21015
|
+
function assertProjectDiscovery(agent, directories, home, canonical8 = resolve20, reviewed) {
|
|
21016
|
+
const homeConfigPath = canonical8(agentDiscoveryConfigPath(home, agent));
|
|
20706
21017
|
for (const directory of directories) {
|
|
20707
|
-
const isHome =
|
|
21018
|
+
const isHome = resolve20(directory) === resolve20(home);
|
|
20708
21019
|
if (agent === "hermes") {
|
|
20709
21020
|
assertHermesEnvironment(home);
|
|
20710
21021
|
continue;
|
|
@@ -20713,7 +21024,7 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
|
|
|
20713
21024
|
continue;
|
|
20714
21025
|
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"] : agent === "sumi" ? ["sumi.json", "sumi.jsonc", ".sumi/sumi.json", ".sumi/sumi.jsonc"] : [".cursor/hooks.json"];
|
|
20715
21026
|
for (const suffix of paths) {
|
|
20716
|
-
const path =
|
|
21027
|
+
const path = canonical8(join32(directory, suffix)), raw = read2(path);
|
|
20717
21028
|
if (raw === null)
|
|
20718
21029
|
continue;
|
|
20719
21030
|
const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
|
|
@@ -20729,7 +21040,7 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
|
|
|
20729
21040
|
const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
|
|
20730
21041
|
if (exact && projected(exact) === exact.sha256 && managed.length) {
|
|
20731
21042
|
const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
|
|
20732
|
-
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 ===
|
|
21043
|
+
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 === canonical8(directory))));
|
|
20733
21044
|
}
|
|
20734
21045
|
}
|
|
20735
21046
|
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)))
|
|
@@ -20737,13 +21048,13 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
|
|
|
20737
21048
|
}
|
|
20738
21049
|
if (agent === "sumi")
|
|
20739
21050
|
for (const name of ["plugin", "plugins"]) {
|
|
20740
|
-
const path =
|
|
21051
|
+
const path = canonical8(join32(directory, ".sumi", name));
|
|
20741
21052
|
safe4(path);
|
|
20742
21053
|
if (lstatSync25(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(path).length)
|
|
20743
21054
|
throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
|
|
20744
21055
|
}
|
|
20745
21056
|
if (agent === "claude") {
|
|
20746
|
-
const commands =
|
|
21057
|
+
const commands = canonical8(join32(directory, ".claude/commands"));
|
|
20747
21058
|
safe4(commands);
|
|
20748
21059
|
if (lstatSync25(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
20749
21060
|
throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
|
|
@@ -20755,11 +21066,37 @@ function agentDiscoveryConfigPath(home, agent) {
|
|
|
20755
21066
|
return sumiConfigPath(home);
|
|
20756
21067
|
return join32(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
20757
21068
|
}
|
|
21069
|
+
function verifyAutomaticDiscoveryClosure(binding, options) {
|
|
21070
|
+
const command = DISCOVERY_RUNTIME_COMMANDS[binding.agent], recorded = options.executable;
|
|
21071
|
+
const onPath = discoveryExecutable(binding.agent);
|
|
21072
|
+
let runtime, gap;
|
|
21073
|
+
if (recorded) {
|
|
21074
|
+
runtime = recordedDiscoveryExecutable(binding.agent, recorded);
|
|
21075
|
+
if (runtime && runtime.target !== recorded.target)
|
|
21076
|
+
throw new Error(`${options.change}: the recorded ${binding.agent} executable now resolves to ${quotedPath(runtime.target)}, not the reviewed ${quotedPath(recorded.target)}`);
|
|
21077
|
+
if (onPath && onPath.target !== recorded.target)
|
|
21078
|
+
throw new Error(`${options.change}: the "${command}" on this process's PATH resolves to ${quotedPath(onPath.target)}, not the reviewed ${quotedPath(recorded.target)}`);
|
|
21079
|
+
if (!runtime)
|
|
21080
|
+
gap = `the recorded executable ${quotedPath(recorded.path)} no longer resolves. Restore it, or review the change with skills hook install`;
|
|
21081
|
+
} else {
|
|
21082
|
+
runtime = onPath;
|
|
21083
|
+
if (!runtime)
|
|
21084
|
+
gap = "it is not on this process's PATH, and the reviewed policy has no recorded executable path. Run with the PATH used for the review, or run skills hook install there to record it";
|
|
21085
|
+
}
|
|
21086
|
+
const current = resolveAgentDiscovery({ home: options.home, agent: binding.agent, canonical: options.canonical, executable: runtime });
|
|
21087
|
+
if (JSON.stringify(current) === JSON.stringify(binding))
|
|
21088
|
+
return;
|
|
21089
|
+
const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename11(source.path) === "package.json");
|
|
21090
|
+
const otherwiseCurrent = current.method === binding.method && JSON.stringify(current.roots) === JSON.stringify(binding.roots) && JSON.stringify(current.directories) === JSON.stringify(binding.directories) && current.sources.every((source) => binding.sources.some((saved) => isDeepStrictEqual7(saved, source)));
|
|
21091
|
+
if (command && gap && runtime === null && reviewedWithRuntime && otherwiseCurrent)
|
|
21092
|
+
throw discoveryRootUnresolved(binding.agent, command, gap);
|
|
21093
|
+
throw new Error(options.change);
|
|
21094
|
+
}
|
|
20758
21095
|
function resolveAgentDiscovery(options) {
|
|
20759
|
-
const
|
|
21096
|
+
const canonical8 = options.canonical ?? resolve20, home = resolve20(options.home), agent = options.agent;
|
|
20760
21097
|
const sources = [], roots = new Set, builtinNames = [];
|
|
20761
21098
|
const witness = (path, format, fields2, hashMode) => {
|
|
20762
|
-
const source = { path:
|
|
21099
|
+
const source = { path: canonical8(path), sha256: null, ...format ? { format, fields: fields2 } : {}, ...hashMode ? { hashMode } : {} };
|
|
20763
21100
|
source.sha256 = projected(source);
|
|
20764
21101
|
sources.push(source);
|
|
20765
21102
|
return read2(source.path);
|
|
@@ -20781,13 +21118,13 @@ function resolveAgentDiscovery(options) {
|
|
|
20781
21118
|
unresolved("JSONC configuration requires a supported format adapter");
|
|
20782
21119
|
for (const root2 of [directory, join32(home, ".claude"), join32(home, ".agents")])
|
|
20783
21120
|
for (const name of ["skill", "skills"])
|
|
20784
|
-
roots.add(
|
|
21121
|
+
roots.add(canonical8(join32(root2, name)));
|
|
20785
21122
|
if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
|
|
20786
21123
|
unresolved("skills must be a string array");
|
|
20787
21124
|
for (const value of config.skills ?? []) {
|
|
20788
|
-
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(
|
|
21125
|
+
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute21(value) || value.startsWith("~/")))
|
|
20789
21126
|
unresolved("remote or relative skill sources need a dedicated discovery adapter");
|
|
20790
|
-
roots.add(
|
|
21127
|
+
roots.add(canonical8(value.startsWith("~/") ? join32(home, value.slice(2)) : value));
|
|
20791
21128
|
}
|
|
20792
21129
|
for (const key of ["plugin", "plugins"])
|
|
20793
21130
|
if (config[key] !== undefined && !Array.isArray(config[key]))
|
|
@@ -20826,23 +21163,23 @@ function resolveAgentDiscovery(options) {
|
|
|
20826
21163
|
continue;
|
|
20827
21164
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
20828
21165
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
20829
|
-
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join32(home, normalized.slice(2)) :
|
|
21166
|
+
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join32(home, normalized.slice(2)) : resolve20(home, ".hermes", normalized);
|
|
20830
21167
|
safe4(path);
|
|
20831
21168
|
roots.add(path);
|
|
20832
21169
|
}
|
|
20833
21170
|
}
|
|
20834
21171
|
if (agent === "gemini") {
|
|
20835
|
-
const executable2 =
|
|
21172
|
+
const executable2 = options.executable === undefined ? discoveryExecutable(agent) : options.executable;
|
|
20836
21173
|
if (executable2) {
|
|
20837
21174
|
let packageRoot;
|
|
20838
|
-
for (let directory =
|
|
21175
|
+
for (let directory = dirname20(executable2.target), depth = 0;depth < 8; directory = dirname20(directory), depth++) {
|
|
20839
21176
|
const path = join32(directory, "package.json"), raw = read2(path);
|
|
20840
21177
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
20841
21178
|
packageRoot = directory;
|
|
20842
21179
|
witness(path);
|
|
20843
21180
|
break;
|
|
20844
21181
|
}
|
|
20845
|
-
if (
|
|
21182
|
+
if (dirname20(directory) === directory)
|
|
20846
21183
|
break;
|
|
20847
21184
|
}
|
|
20848
21185
|
if (!packageRoot)
|
|
@@ -20865,11 +21202,11 @@ function resolveAgentDiscovery(options) {
|
|
|
20865
21202
|
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
20866
21203
|
unresolved("invalid retained review");
|
|
20867
21204
|
verifyAgentDiscovery(retained);
|
|
20868
|
-
if (!retained.sources.some((source) => source.path ===
|
|
21205
|
+
if (!retained.sources.some((source) => source.path === canonical8(configPath) && source.format === undefined && source.fields === undefined))
|
|
20869
21206
|
unresolved("retained review is missing the full agent configuration source");
|
|
20870
|
-
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !==
|
|
21207
|
+
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical8(configPath)))
|
|
20871
21208
|
unresolved("retained settings witness names another configuration source");
|
|
20872
|
-
const covered = supersedesSettingsProjection(agent, retained.sources,
|
|
21209
|
+
const covered = supersedesSettingsProjection(agent, retained.sources, canonical8(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical8(configPath), source)) : sources;
|
|
20873
21210
|
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual7(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
20874
21211
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
20875
21212
|
return retained;
|
|
@@ -20881,15 +21218,15 @@ function resolveAgentDiscovery(options) {
|
|
|
20881
21218
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
20882
21219
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
20883
21220
|
verifyAgentDiscovery(supplied);
|
|
20884
|
-
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !==
|
|
21221
|
+
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical8(configPath)))
|
|
20885
21222
|
throw new Error("Semantic settings witnesses must name the configured agent configuration source");
|
|
20886
|
-
if (!review.sources.some((source) => source.path ===
|
|
21223
|
+
if (!review.sources.some((source) => source.path === canonical8(configPath)))
|
|
20887
21224
|
throw new Error("Discovery review must include the agent configuration source");
|
|
20888
|
-
const reviewedSources = supersedesSettingsProjection(agent, review.sources,
|
|
21225
|
+
const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical8(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical8(configPath), source)) : sources;
|
|
20889
21226
|
const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
|
|
20890
|
-
if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" ||
|
|
21227
|
+
if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename11(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json")))
|
|
20891
21228
|
return source;
|
|
20892
|
-
const pluginRoot =
|
|
21229
|
+
const pluginRoot = dirname20(dirname20(source.path)), hooksPath = join32(pluginRoot, "hooks/hooks.json");
|
|
20893
21230
|
const hookStat = lstatSync25(hooksPath, { throwIfNoEntry: false });
|
|
20894
21231
|
const text2 = read2(source.path);
|
|
20895
21232
|
if (text2 === null)
|
|
@@ -20904,13 +21241,13 @@ function resolveAgentDiscovery(options) {
|
|
|
20904
21241
|
if (manifest.hooks !== undefined) {
|
|
20905
21242
|
if (typeof manifest.hooks !== "string")
|
|
20906
21243
|
throw new Error("Reviewed Claude plugin manifest has an unsupported hooks target");
|
|
20907
|
-
const declaredHookPath =
|
|
21244
|
+
const declaredHookPath = resolve20(pluginRoot, manifest.hooks);
|
|
20908
21245
|
if (!declaredHookPath.startsWith(pluginRoot + sep8))
|
|
20909
21246
|
throw new Error("Reviewed Claude plugin hooks target escapes its root");
|
|
20910
21247
|
reviewedHookPaths.add(declaredHookPath);
|
|
20911
21248
|
}
|
|
20912
21249
|
for (const hooksFile of reviewedHookPaths)
|
|
20913
|
-
if (!review.sources.some((hook) => hook.path ===
|
|
21250
|
+
if (!review.sources.some((hook) => hook.path === canonical8(hooksFile) && hook.sha256 !== null && hook.format === undefined && hook.fields === undefined && hook.managedPlugins === undefined && (hook.hashMode === undefined || hook.hashMode === "bytes")))
|
|
20914
21251
|
throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
|
|
20915
21252
|
return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
|
|
20916
21253
|
});
|
|
@@ -20919,7 +21256,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20919
21256
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
|
20920
21257
|
}
|
|
20921
21258
|
function plugin(root2) {
|
|
20922
|
-
root2 =
|
|
21259
|
+
root2 = canonical8(root2);
|
|
20923
21260
|
safe4(root2);
|
|
20924
21261
|
const manifestPath = join32(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
20925
21262
|
const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
|
|
@@ -20942,16 +21279,16 @@ function resolveAgentDiscovery(options) {
|
|
|
20942
21279
|
if (agent === "claude")
|
|
20943
21280
|
roots.add(join32(root2, "skills"));
|
|
20944
21281
|
for (const value of declared) {
|
|
20945
|
-
const path =
|
|
21282
|
+
const path = resolve20(root2, value);
|
|
20946
21283
|
if (path !== root2 && !path.startsWith(root2 + sep8))
|
|
20947
21284
|
unresolved("plugin skill path escapes its root");
|
|
20948
|
-
roots.add(path.endsWith(`${sep8}SKILL.md`) ?
|
|
21285
|
+
roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname20(path) : path);
|
|
20949
21286
|
}
|
|
20950
21287
|
}
|
|
20951
21288
|
if (agent === "claude") {
|
|
20952
21289
|
const registrations = witness(join32(home, ".claude/plugins/installed_plugins.json"));
|
|
20953
21290
|
const installed = registrations === null ? {} : parseConfig(registrations, join32(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
20954
|
-
const commands =
|
|
21291
|
+
const commands = canonical8(join32(home, ".claude/commands"));
|
|
20955
21292
|
safe4(commands);
|
|
20956
21293
|
if (lstatSync25(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
20957
21294
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
@@ -20973,7 +21310,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20973
21310
|
unresolved("unsupported plugin enablement");
|
|
20974
21311
|
const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
|
|
20975
21312
|
const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
20976
|
-
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !
|
|
21313
|
+
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute21(market.source))
|
|
20977
21314
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
20978
21315
|
const catalogText = witness(join32(market.source, ".agents/plugins/marketplace.json"));
|
|
20979
21316
|
if (catalogText === null)
|
|
@@ -20981,7 +21318,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20981
21318
|
const catalog = parseConfig(catalogText, join32(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
20982
21319
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
20983
21320
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
20984
|
-
plugin(
|
|
21321
|
+
plugin(resolve20(market.source, matches[0].source.path));
|
|
20985
21322
|
}
|
|
20986
21323
|
} else if (agent === "opencode") {
|
|
20987
21324
|
if (witness(join32(home, ".config/opencode/opencode.jsonc")) !== null || witness(join32(home, ".config/opencode/config.json")) !== null)
|
|
@@ -20989,7 +21326,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20989
21326
|
if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
|
|
20990
21327
|
unresolved("external plugin or remote skill source");
|
|
20991
21328
|
for (const path of config.skills?.paths ?? []) {
|
|
20992
|
-
if (typeof path !== "string" || !
|
|
21329
|
+
if (typeof path !== "string" || !isAbsolute21(path))
|
|
20993
21330
|
unresolved("relative or malformed added skill root");
|
|
20994
21331
|
roots.add(path);
|
|
20995
21332
|
}
|
|
@@ -20998,7 +21335,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20998
21335
|
if (lstatSync25(localPlugins, { throwIfNoEntry: false }) && readdirSync10(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
20999
21336
|
unresolved("additional local plugin hooks require review");
|
|
21000
21337
|
} else if (agent === "sumi") {
|
|
21001
|
-
if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !==
|
|
21338
|
+
if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical8(configPath) && source.path !== canonical8(join32(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
|
|
21002
21339
|
unresolved("external or foreign local plugins require review");
|
|
21003
21340
|
} else if (agent === "hermes") {
|
|
21004
21341
|
for (const directory of [join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")]) {
|
|
@@ -21028,13 +21365,13 @@ function resolveAgentDiscovery(options) {
|
|
|
21028
21365
|
roots.add(join32(root2, "skills"));
|
|
21029
21366
|
}
|
|
21030
21367
|
}
|
|
21031
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")].map((path) =>
|
|
21368
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")].map((path) => canonical8(path))) } : {} };
|
|
21032
21369
|
}
|
|
21033
21370
|
|
|
21034
21371
|
// src/lib/authoring-path.ts
|
|
21035
21372
|
var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
|
|
21036
21373
|
function pathParts(path) {
|
|
21037
|
-
return
|
|
21374
|
+
return resolve21(path).split(sep9).filter(Boolean);
|
|
21038
21375
|
}
|
|
21039
21376
|
function nativeRootMatch(path) {
|
|
21040
21377
|
const parts = pathParts(path);
|
|
@@ -21048,16 +21385,16 @@ function nativeRootMatch(path) {
|
|
|
21048
21385
|
return;
|
|
21049
21386
|
}
|
|
21050
21387
|
function physicalPath(path) {
|
|
21051
|
-
let cursor =
|
|
21388
|
+
let cursor = resolve21(path);
|
|
21052
21389
|
const suffix = [];
|
|
21053
21390
|
while (!lstatSync26(cursor, { throwIfNoEntry: false })) {
|
|
21054
|
-
suffix.unshift(
|
|
21055
|
-
const parent =
|
|
21391
|
+
suffix.unshift(basename12(cursor));
|
|
21392
|
+
const parent = dirname21(cursor);
|
|
21056
21393
|
if (parent === cursor)
|
|
21057
|
-
return
|
|
21394
|
+
return resolve21(path);
|
|
21058
21395
|
cursor = parent;
|
|
21059
21396
|
}
|
|
21060
|
-
return
|
|
21397
|
+
return resolve21(realpathSync12(cursor), ...suffix);
|
|
21061
21398
|
}
|
|
21062
21399
|
function contains(root2, path) {
|
|
21063
21400
|
return path === root2 || path.startsWith(root2 + sep9);
|
|
@@ -21065,7 +21402,7 @@ function contains(root2, path) {
|
|
|
21065
21402
|
function configuredRoots(home, explicitHome) {
|
|
21066
21403
|
const homeData = join33(home, ".hasna", "skills");
|
|
21067
21404
|
const homePolicy = readManagedSkillPolicySnapshot(homeData);
|
|
21068
|
-
const configuredPolicy = !explicitHome &&
|
|
21405
|
+
const configuredPolicy = !explicitHome && resolve21(getDataDirReadOnly()) !== resolve21(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
|
|
21069
21406
|
const policy = homePolicy ?? configuredPolicy;
|
|
21070
21407
|
const bridge = policy?.value.bridge;
|
|
21071
21408
|
if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
|
|
@@ -21104,22 +21441,22 @@ function configuredRoots(home, explicitHome) {
|
|
|
21104
21441
|
function assertPortableAuthoringPath(path, options = {}) {
|
|
21105
21442
|
if (typeof path !== "string" || !path || path.includes("\x00"))
|
|
21106
21443
|
throw new Error("Invalid portable authoring path");
|
|
21107
|
-
const lexical =
|
|
21444
|
+
const lexical = resolve21(path);
|
|
21108
21445
|
const physical = physicalPath(lexical);
|
|
21109
21446
|
const candidates = [lexical, physical];
|
|
21110
21447
|
let native = candidates.map(nativeRootMatch).find(Boolean);
|
|
21111
|
-
const home =
|
|
21448
|
+
const home = resolve21(options.homeDir ?? homedir6());
|
|
21112
21449
|
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join33(home, ...parts));
|
|
21113
21450
|
defaults.push(join33(home, ".codex/plugins/cache"), join33(home, ".claude/plugins/cache"), join33(home, ".claude/plugins/synced"), join33(home, ".gemini/extensions"));
|
|
21114
21451
|
for (const root2 of defaults) {
|
|
21115
|
-
if (candidates.some((candidate2) => contains(
|
|
21452
|
+
if (candidates.some((candidate2) => contains(resolve21(root2), candidate2) || contains(physicalPath(root2), candidate2)))
|
|
21116
21453
|
native ??= root2;
|
|
21117
21454
|
}
|
|
21118
21455
|
if (native) {
|
|
21119
21456
|
throw new Error(`Refusing portable Skills authoring below native discovery root '${native}': ${lexical}. Set HASNA_SKILLS_DIR to a private authoring corpus outside native roots.`);
|
|
21120
21457
|
}
|
|
21121
21458
|
for (const root2 of configuredRoots(home, options.homeDir !== undefined)) {
|
|
21122
|
-
if (candidates.some((candidate2) => contains(
|
|
21459
|
+
if (candidates.some((candidate2) => contains(resolve21(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
|
|
21123
21460
|
throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
|
|
21124
21461
|
}
|
|
21125
21462
|
}
|
|
@@ -21267,20 +21604,20 @@ function isSkillCandidate(dir) {
|
|
|
21267
21604
|
return existsSync14(join34(dir, "SKILL.md")) || existsSync14(join34(dir, "skill.json")) || existsSync14(join34(dir, "package.json"));
|
|
21268
21605
|
}
|
|
21269
21606
|
function physicalImportPath(path) {
|
|
21270
|
-
let ancestor =
|
|
21607
|
+
let ancestor = resolve22(path);
|
|
21271
21608
|
const suffix = [];
|
|
21272
21609
|
while (!existsSync14(ancestor)) {
|
|
21273
|
-
suffix.unshift(
|
|
21274
|
-
ancestor =
|
|
21610
|
+
suffix.unshift(basename13(ancestor));
|
|
21611
|
+
ancestor = dirname22(ancestor);
|
|
21275
21612
|
}
|
|
21276
21613
|
return join34(realpathSync13(ancestor), ...suffix);
|
|
21277
21614
|
}
|
|
21278
21615
|
function assertDisjointImportPaths(source, destination) {
|
|
21279
21616
|
const sourcePath = realpathSync13(source);
|
|
21280
21617
|
const destinationPath = physicalImportPath(destination);
|
|
21281
|
-
const contains2 = (parent,
|
|
21282
|
-
const path = relative7(parent,
|
|
21283
|
-
return path === "" || !
|
|
21618
|
+
const contains2 = (parent, child2) => {
|
|
21619
|
+
const path = relative7(parent, child2);
|
|
21620
|
+
return path === "" || !isAbsolute22(path) && path !== ".." && !path.startsWith(`..${sep10}`);
|
|
21284
21621
|
};
|
|
21285
21622
|
if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
|
|
21286
21623
|
throw new Error("Source and destination skill directories must not overlap");
|
|
@@ -21295,8 +21632,8 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
21295
21632
|
const explicitName = options.name != null;
|
|
21296
21633
|
const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
|
|
21297
21634
|
if (isOfficialSkillName(skillName) && !options.allowShadow) {
|
|
21298
|
-
const sourceSlug = safeNormalizeName(
|
|
21299
|
-
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${
|
|
21635
|
+
const sourceSlug = safeNormalizeName(basename13(absoluteSource));
|
|
21636
|
+
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename13(absoluteSource)}')` : ""} matches a bundled official skill.`;
|
|
21300
21637
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
21301
21638
|
}
|
|
21302
21639
|
const readOnlyDestination = join34(resolvePortableSkillsRoot(options, false), skillName);
|
|
@@ -21311,7 +21648,7 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
21311
21648
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
21312
21649
|
rmSync4(destination, { recursive: true, force: true });
|
|
21313
21650
|
}
|
|
21314
|
-
mkdirSync10(
|
|
21651
|
+
mkdirSync10(dirname22(destination), { recursive: true });
|
|
21315
21652
|
copySkillDirectory(absoluteSource, destination);
|
|
21316
21653
|
const base = {
|
|
21317
21654
|
...inferred,
|
|
@@ -21561,7 +21898,7 @@ function safeIsDirectory(path) {
|
|
|
21561
21898
|
}
|
|
21562
21899
|
}
|
|
21563
21900
|
function isSafeRelativePath2(value) {
|
|
21564
|
-
if (!value.trim() ||
|
|
21901
|
+
if (!value.trim() || isAbsolute22(value))
|
|
21565
21902
|
return false;
|
|
21566
21903
|
const normalized = normalize4(value).replace(/\\/g, "/");
|
|
21567
21904
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
@@ -21877,7 +22214,7 @@ function getAllTags(catalog) {
|
|
|
21877
22214
|
return Array.from(tagSet).sort();
|
|
21878
22215
|
}
|
|
21879
22216
|
// src/lib/run-state.ts
|
|
21880
|
-
import { createHash as
|
|
22217
|
+
import { createHash as createHash26, randomBytes } from "crypto";
|
|
21881
22218
|
import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync23, readdirSync as readdirSync13, statSync as statSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
21882
22219
|
import { extname, join as join36, relative as relative8 } from "path";
|
|
21883
22220
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
@@ -22004,7 +22341,7 @@ function collectRunArtifacts(context) {
|
|
|
22004
22341
|
artifacts.push({
|
|
22005
22342
|
path: toProjectRelative(context.targetDir, path),
|
|
22006
22343
|
mime: mimeForPath(path),
|
|
22007
|
-
sha256:
|
|
22344
|
+
sha256: createHash26("sha256").update(bytes).digest("hex"),
|
|
22008
22345
|
sizeBytes: stat2.size
|
|
22009
22346
|
});
|
|
22010
22347
|
}
|
|
@@ -25533,14 +25870,14 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
25533
25870
|
const shape = zod.def.shape;
|
|
25534
25871
|
const propValues = {};
|
|
25535
25872
|
for (const key in shape) {
|
|
25536
|
-
const
|
|
25537
|
-
if (
|
|
25873
|
+
const field3 = shape[key]._zod;
|
|
25874
|
+
if (field3.values) {
|
|
25538
25875
|
if (!Object.prototype.hasOwnProperty.call(propValues, key)) {
|
|
25539
25876
|
assignProp(propValues, key, new Set);
|
|
25540
25877
|
}
|
|
25541
|
-
for (const v of
|
|
25878
|
+
for (const v of field3.values)
|
|
25542
25879
|
propValues[key].add(v);
|
|
25543
|
-
if (
|
|
25880
|
+
if (field3.optin !== undefined)
|
|
25544
25881
|
propValues[key].add(undefined);
|
|
25545
25882
|
}
|
|
25546
25883
|
}
|
|
@@ -26953,8 +27290,8 @@ function isRecursive(inst, stack) {
|
|
|
26953
27290
|
return true;
|
|
26954
27291
|
stack.add(inst);
|
|
26955
27292
|
let result = false;
|
|
26956
|
-
const check = (
|
|
26957
|
-
if (!result &&
|
|
27293
|
+
const check = (child2) => {
|
|
27294
|
+
if (!result && child2?._zod && isRecursive(child2, stack))
|
|
26958
27295
|
result = true;
|
|
26959
27296
|
};
|
|
26960
27297
|
const def = inst._zod.def;
|
|
@@ -37733,11 +38070,11 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
37733
38070
|
}
|
|
37734
38071
|
const allKeys = new Set(Object.keys(shape));
|
|
37735
38072
|
const requiredKeys = new Set([...allKeys].filter((key) => {
|
|
37736
|
-
const
|
|
38073
|
+
const field3 = def.shape[key];
|
|
37737
38074
|
if (ctx.io === "input") {
|
|
37738
|
-
return inputOptin(
|
|
38075
|
+
return inputOptin(field3) === undefined;
|
|
37739
38076
|
} else {
|
|
37740
|
-
return
|
|
38077
|
+
return field3._zod.optout === undefined;
|
|
37741
38078
|
}
|
|
37742
38079
|
}));
|
|
37743
38080
|
if (requiredKeys.size > 0) {
|
|
@@ -41423,7 +41760,7 @@ function clone2(value) {
|
|
|
41423
41760
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
41424
41761
|
}
|
|
41425
41762
|
// src/lib/canonical-json.ts
|
|
41426
|
-
import { createHash as
|
|
41763
|
+
import { createHash as createHash27 } from "crypto";
|
|
41427
41764
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
41428
41765
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
41429
41766
|
}
|
|
@@ -41467,7 +41804,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
41467
41804
|
return visit3(value, 0);
|
|
41468
41805
|
}
|
|
41469
41806
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
41470
|
-
return
|
|
41807
|
+
return createHash27("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
41471
41808
|
}
|
|
41472
41809
|
|
|
41473
41810
|
// src/lib/remote-recurring-contract.ts
|
|
@@ -42291,17 +42628,17 @@ function runQuoteReceipt(value) {
|
|
|
42291
42628
|
return value;
|
|
42292
42629
|
}
|
|
42293
42630
|
function parseRemoteRunQuote(value) {
|
|
42294
|
-
const
|
|
42295
|
-
runQuoteReceipt(
|
|
42296
|
-
const pricing = object7(
|
|
42297
|
-
if (typeof
|
|
42631
|
+
const quote3 = object7(value);
|
|
42632
|
+
runQuoteReceipt(quote3.quoteReceipt);
|
|
42633
|
+
const pricing = object7(quote3.pricing);
|
|
42634
|
+
if (typeof quote3.skill !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(quote3.skill))
|
|
42298
42635
|
throw new Error("Invalid quoted skill");
|
|
42299
42636
|
const costCents = creditCount(pricing.costCredits ?? pricing.costCents);
|
|
42300
42637
|
if (pricing.costCredits !== undefined && pricing.costCents !== undefined && pricing.costCents !== costCents)
|
|
42301
42638
|
throw new Error("Inconsistent quoted credit count");
|
|
42302
|
-
if (
|
|
42639
|
+
if (quote3.availability && object7(quote3.availability).status !== "available")
|
|
42303
42640
|
throw new Error("This skill is unavailable for remote execution");
|
|
42304
|
-
return { ...
|
|
42641
|
+
return { ...quote3, skill: quote3.skill, pricing: { ...pricing, costCredits: costCents, costCents, formattedCost: `${costCents} credits` } };
|
|
42305
42642
|
}
|
|
42306
42643
|
function parseRemoteCreditPacks(value) {
|
|
42307
42644
|
if (!Array.isArray(value))
|
|
@@ -42376,14 +42713,14 @@ function creditCheckoutFailure(value, status, key) {
|
|
|
42376
42713
|
}
|
|
42377
42714
|
|
|
42378
42715
|
// src/lib/remote-profile.ts
|
|
42379
|
-
function customerNamePatch(input2,
|
|
42380
|
-
if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2,
|
|
42716
|
+
function customerNamePatch(input2, field3) {
|
|
42717
|
+
if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field3))
|
|
42381
42718
|
throw new Error("Provide only the requested name field.");
|
|
42382
|
-
const value = input2[
|
|
42719
|
+
const value = input2[field3];
|
|
42383
42720
|
if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
|
|
42384
42721
|
throw new Error("Use a name of 1\u2013100 characters without control characters or newlines.");
|
|
42385
42722
|
}
|
|
42386
|
-
return { [
|
|
42723
|
+
return { [field3]: value.trim() };
|
|
42387
42724
|
}
|
|
42388
42725
|
function isRecord5(value) {
|
|
42389
42726
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -42428,10 +42765,10 @@ async function readQuoteUnavailableCode(response) {
|
|
|
42428
42765
|
let timer;
|
|
42429
42766
|
let deadlineExceeded = false;
|
|
42430
42767
|
const expired = Symbol("quote body deadline");
|
|
42431
|
-
const deadline = new Promise((
|
|
42768
|
+
const deadline = new Promise((resolve23) => {
|
|
42432
42769
|
timer = setTimeout(() => {
|
|
42433
42770
|
deadlineExceeded = true;
|
|
42434
|
-
|
|
42771
|
+
resolve23(expired);
|
|
42435
42772
|
reader.cancel().catch(() => {});
|
|
42436
42773
|
}, 1500);
|
|
42437
42774
|
});
|
|
@@ -42778,18 +43115,18 @@ class RemoteSkillsClient {
|
|
|
42778
43115
|
const maximum = creditCount(approval.maxCredits ?? approval.maxCostCents ?? 0);
|
|
42779
43116
|
if (approval.maxCostCents !== undefined && approval.maxCostCents !== maximum)
|
|
42780
43117
|
throw new Error("Credit approval fields disagree");
|
|
42781
|
-
const
|
|
42782
|
-
if (
|
|
42783
|
-
throw new RemoteCreditApprovalError(
|
|
43118
|
+
const quote3 = approval.quoteReceipt === undefined ? await this.quoteRun(slug2, input2, args, approval.inputFiles?.length ? approval.inputFiles : undefined) : undefined;
|
|
43119
|
+
if (quote3 && quote3.pricing.costCents > maximum)
|
|
43120
|
+
throw new RemoteCreditApprovalError(quote3.pricing.costCents, maximum);
|
|
42784
43121
|
const capabilities = await this.getCapabilities();
|
|
42785
43122
|
if (!capabilities.capabilities.includes("runs.submit") || capabilities.billing?.boundedRunApproval !== true || capabilities.billing.unit !== "credits") {
|
|
42786
43123
|
throw new Error("The configured server does not support bounded credit approval; refusing remote submission");
|
|
42787
43124
|
}
|
|
42788
|
-
return this.submitRun(
|
|
43125
|
+
return this.submitRun(quote3?.skill ?? slug2, input2, args, {
|
|
42789
43126
|
...approval,
|
|
42790
43127
|
maxCredits: maximum,
|
|
42791
43128
|
maxCostCents: maximum,
|
|
42792
|
-
...(
|
|
43129
|
+
...(quote3?.quoteReceipt ?? approval.quoteReceipt) === undefined ? {} : { quoteReceipt: quote3?.quoteReceipt ?? approval.quoteReceipt }
|
|
42793
43130
|
});
|
|
42794
43131
|
}
|
|
42795
43132
|
async getIdentity() {
|
|
@@ -43390,13 +43727,13 @@ class RemoteSkillsClient {
|
|
|
43390
43727
|
return normalizeUpdatedSincePage(await response.json());
|
|
43391
43728
|
}
|
|
43392
43729
|
}
|
|
43393
|
-
function requireOptionalString(record8,
|
|
43394
|
-
if (record8[
|
|
43730
|
+
function requireOptionalString(record8, field3) {
|
|
43731
|
+
if (record8[field3] === undefined)
|
|
43395
43732
|
return;
|
|
43396
|
-
if (typeof record8[
|
|
43397
|
-
throw new Error(`Remote payload did not match the expected contract (${
|
|
43733
|
+
if (typeof record8[field3] !== "string") {
|
|
43734
|
+
throw new Error(`Remote payload did not match the expected contract (${field3} must be a string when present)`);
|
|
43398
43735
|
}
|
|
43399
|
-
return record8[
|
|
43736
|
+
return record8[field3];
|
|
43400
43737
|
}
|
|
43401
43738
|
var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
43402
43739
|
function isVersionRecord(value) {
|
|
@@ -43734,7 +44071,7 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
43734
44071
|
saveSchedules(data, targetDir);
|
|
43735
44072
|
}
|
|
43736
44073
|
// src/lib/revision.ts
|
|
43737
|
-
import { createHash as
|
|
44074
|
+
import { createHash as createHash28 } from "crypto";
|
|
43738
44075
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
43739
44076
|
function revisionIdOf(content) {
|
|
43740
44077
|
const canonicalContent = {
|
|
@@ -43755,15 +44092,15 @@ function revisionIdOf(content) {
|
|
|
43755
44092
|
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
43756
44093
|
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
43757
44094
|
}
|
|
43758
|
-
const
|
|
43759
|
-
return
|
|
44095
|
+
const canonical8 = JSON.stringify(canonicalContent);
|
|
44096
|
+
return createHash28("sha256").update(canonical8).digest("hex");
|
|
43760
44097
|
}
|
|
43761
44098
|
function revisionIdOfRecord(record8) {
|
|
43762
44099
|
return revisionIdOf(record8);
|
|
43763
44100
|
}
|
|
43764
44101
|
// src/lib/pull.ts
|
|
43765
44102
|
import { existsSync as existsSync18, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, readFileSync as readFileSync26, renameSync as renameSync7, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "fs";
|
|
43766
|
-
import { dirname as
|
|
44103
|
+
import { dirname as dirname23, join as join38 } from "path";
|
|
43767
44104
|
|
|
43768
44105
|
// src/lib/skill-bundles.ts
|
|
43769
44106
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -44105,7 +44442,7 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
44105
44442
|
try {
|
|
44106
44443
|
for (const entry of entries) {
|
|
44107
44444
|
const destination = join38(staging, entry.path);
|
|
44108
|
-
mkdirSync13(
|
|
44445
|
+
mkdirSync13(dirname23(destination), { recursive: true });
|
|
44109
44446
|
writeFileSync12(destination, entry.bytes, { mode: entry.mode });
|
|
44110
44447
|
}
|
|
44111
44448
|
writePullMarker(staging, {
|
|
@@ -44199,7 +44536,7 @@ function splitNameVersion(raw) {
|
|
|
44199
44536
|
return version2 ? { name, version: version2 } : { name };
|
|
44200
44537
|
}
|
|
44201
44538
|
// src/lib/recurring-surface.ts
|
|
44202
|
-
import { isAbsolute as
|
|
44539
|
+
import { isAbsolute as isAbsolute23 } from "path";
|
|
44203
44540
|
|
|
44204
44541
|
// src/lib/remote-invitation-recovery.ts
|
|
44205
44542
|
class InvitationEmailInputError extends Error {
|
|
@@ -44297,7 +44634,7 @@ async function requestInvitationEmail(origin, action, input2) {
|
|
|
44297
44634
|
}
|
|
44298
44635
|
|
|
44299
44636
|
// src/lib/remote-private-publications.ts
|
|
44300
|
-
import { createHash as
|
|
44637
|
+
import { createHash as createHash29 } from "crypto";
|
|
44301
44638
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
44302
44639
|
var failures2 = {
|
|
44303
44640
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -44344,7 +44681,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
|
44344
44681
|
var record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
44345
44682
|
var exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
|
|
44346
44683
|
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));
|
|
44347
|
-
var publicationSha256 = (bytes) =>
|
|
44684
|
+
var publicationSha256 = (bytes) => createHash29("sha256").update(bytes).digest("hex");
|
|
44348
44685
|
function checkedPublicationDeclaration(value) {
|
|
44349
44686
|
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)
|
|
44350
44687
|
return bad();
|
|
@@ -44589,12 +44926,12 @@ class RemotePrivatePublicationsClient {
|
|
|
44589
44926
|
previous = view;
|
|
44590
44927
|
if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
|
|
44591
44928
|
return view;
|
|
44592
|
-
await new Promise((
|
|
44929
|
+
await new Promise((resolve23) => {
|
|
44593
44930
|
const timer = setTimeout(done, Math.min(1000, until - Date.now()));
|
|
44594
44931
|
function done() {
|
|
44595
44932
|
clearTimeout(timer);
|
|
44596
44933
|
options.signal?.removeEventListener("abort", done);
|
|
44597
|
-
|
|
44934
|
+
resolve23();
|
|
44598
44935
|
}
|
|
44599
44936
|
options.signal?.addEventListener("abort", done, { once: true });
|
|
44600
44937
|
});
|
|
@@ -44858,7 +45195,7 @@ var stateSchema = exports_external.object({
|
|
|
44858
45195
|
var uuid13 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
44859
45196
|
var context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
|
|
44860
45197
|
var page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
|
|
44861
|
-
var directory = exports_external.string().max(4096).refine(
|
|
45198
|
+
var directory = exports_external.string().max(4096).refine(isAbsolute23, "Use an absolute recovery directory");
|
|
44862
45199
|
var email4 = exports_external.string().trim().toLowerCase().email().max(254);
|
|
44863
45200
|
var code = exports_external.string().regex(/^\d{6}$/);
|
|
44864
45201
|
var recurringSurfaceSchemas = {
|
|
@@ -45009,7 +45346,7 @@ function findSkillsParityForMcpTool(tool) {
|
|
|
45009
45346
|
}
|
|
45010
45347
|
// src/lib/registry-sync.ts
|
|
45011
45348
|
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
|
|
45012
|
-
import { dirname as
|
|
45349
|
+
import { dirname as dirname24, relative as relative9 } from "path";
|
|
45013
45350
|
function createRegistrySyncArtifact(options = {}) {
|
|
45014
45351
|
const profile = options.profile ?? "all";
|
|
45015
45352
|
const includeDocs = options.includeDocs ?? true;
|
|
@@ -45066,7 +45403,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
45066
45403
|
};
|
|
45067
45404
|
}
|
|
45068
45405
|
function writeRegistrySyncArtifact(path, artifact) {
|
|
45069
|
-
mkdirSync14(
|
|
45406
|
+
mkdirSync14(dirname24(path), { recursive: true });
|
|
45070
45407
|
writeFileSync13(path, `${JSON.stringify(artifact, null, 2)}
|
|
45071
45408
|
`);
|
|
45072
45409
|
}
|
|
@@ -46151,14 +46488,14 @@ function clone3(value) {
|
|
|
46151
46488
|
}
|
|
46152
46489
|
// src/lib/feedback.ts
|
|
46153
46490
|
import { appendFileSync, existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
|
|
46154
|
-
import { dirname as
|
|
46491
|
+
import { dirname as dirname25, join as join39 } from "path";
|
|
46155
46492
|
import { Database } from "bun:sqlite";
|
|
46156
46493
|
function getFeedbackDbPath() {
|
|
46157
46494
|
return join39(getDataDir(), "skills.db");
|
|
46158
46495
|
}
|
|
46159
46496
|
function getFeedbackDb() {
|
|
46160
46497
|
const dbPath = getFeedbackDbPath();
|
|
46161
|
-
const dir =
|
|
46498
|
+
const dir = dirname25(dbPath);
|
|
46162
46499
|
if (!existsSync19(dir))
|
|
46163
46500
|
mkdirSync15(dir, { recursive: true });
|
|
46164
46501
|
const db = new Database(dbPath);
|
|
@@ -46187,7 +46524,7 @@ function saveFeedback(input2) {
|
|
|
46187
46524
|
const category = input2.category ?? "general";
|
|
46188
46525
|
if (isApiMode()) {
|
|
46189
46526
|
const path = join39(getDataDir(), "feedback.jsonl");
|
|
46190
|
-
const dir =
|
|
46527
|
+
const dir = dirname25(path);
|
|
46191
46528
|
if (!existsSync19(dir))
|
|
46192
46529
|
mkdirSync15(dir, { recursive: true });
|
|
46193
46530
|
appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
@@ -46210,7 +46547,7 @@ function isApiMode(env = process.env) {
|
|
|
46210
46547
|
}
|
|
46211
46548
|
}
|
|
46212
46549
|
// src/lib/native-storage.ts
|
|
46213
|
-
import { createHash as
|
|
46550
|
+
import { createHash as createHash30, createHmac as createHmac2 } from "crypto";
|
|
46214
46551
|
import {
|
|
46215
46552
|
existsSync as existsSync20,
|
|
46216
46553
|
mkdirSync as mkdirSync16,
|
|
@@ -46219,7 +46556,7 @@ import {
|
|
|
46219
46556
|
statSync as statSync13,
|
|
46220
46557
|
writeFileSync as writeFileSync14
|
|
46221
46558
|
} from "fs";
|
|
46222
|
-
import { dirname as
|
|
46559
|
+
import { dirname as dirname26, join as join40, normalize as normalize5, relative as relative10, sep as sep11 } from "path";
|
|
46223
46560
|
var SKILLS_STORAGE_TABLES = [
|
|
46224
46561
|
"skills_sync_records",
|
|
46225
46562
|
"skills_sync_cursors"
|
|
@@ -46386,7 +46723,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
46386
46723
|
files.push({
|
|
46387
46724
|
path: relativePath,
|
|
46388
46725
|
sizeBytes: bytes.byteLength,
|
|
46389
|
-
sha256:
|
|
46726
|
+
sha256: createHash30("sha256").update(bytes).digest("hex"),
|
|
46390
46727
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
46391
46728
|
});
|
|
46392
46729
|
}
|
|
@@ -46411,11 +46748,11 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
|
|
|
46411
46748
|
continue;
|
|
46412
46749
|
}
|
|
46413
46750
|
const bytes = Buffer.from(file2.contentBase64, "base64");
|
|
46414
|
-
const hash4 =
|
|
46751
|
+
const hash4 = createHash30("sha256").update(bytes).digest("hex");
|
|
46415
46752
|
if (hash4 !== file2.sha256) {
|
|
46416
46753
|
throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
|
|
46417
46754
|
}
|
|
46418
|
-
mkdirSync16(
|
|
46755
|
+
mkdirSync16(dirname26(absolutePath4), { recursive: true });
|
|
46419
46756
|
writeFileSync14(absolutePath4, bytes);
|
|
46420
46757
|
written += 1;
|
|
46421
46758
|
}
|
|
@@ -46743,7 +47080,7 @@ function toIsoString(value) {
|
|
|
46743
47080
|
return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
|
|
46744
47081
|
}
|
|
46745
47082
|
function sha256Hex2(value) {
|
|
46746
|
-
return
|
|
47083
|
+
return createHash30("sha256").update(value).digest("hex");
|
|
46747
47084
|
}
|
|
46748
47085
|
function normalizeHeaders(headers) {
|
|
46749
47086
|
const result = {};
|
|
@@ -46795,7 +47132,7 @@ function toArrayBuffer(bytes) {
|
|
|
46795
47132
|
return buffer;
|
|
46796
47133
|
}
|
|
46797
47134
|
// src/lib/station-snapshot.ts
|
|
46798
|
-
import { createHash as
|
|
47135
|
+
import { createHash as createHash31 } from "crypto";
|
|
46799
47136
|
import {
|
|
46800
47137
|
copyFileSync,
|
|
46801
47138
|
mkdirSync as mkdirSync17,
|
|
@@ -46803,7 +47140,7 @@ import {
|
|
|
46803
47140
|
statSync as statSync15,
|
|
46804
47141
|
writeFileSync as writeFileSync15
|
|
46805
47142
|
} from "fs";
|
|
46806
|
-
import { dirname as
|
|
47143
|
+
import { dirname as dirname27, isAbsolute as isAbsolute24, relative as relative11, resolve as resolve23, sep as sep13 } from "path";
|
|
46807
47144
|
|
|
46808
47145
|
// src/lib/portable-snapshot-filter.ts
|
|
46809
47146
|
import { readdirSync as readdirSync15, statSync as statSync14 } from "fs";
|
|
@@ -46970,7 +47307,7 @@ function validateStationId(stationId) {
|
|
|
46970
47307
|
}
|
|
46971
47308
|
}
|
|
46972
47309
|
function sha256File(filePath) {
|
|
46973
|
-
return
|
|
47310
|
+
return createHash31("sha256").update(readFileSync28(filePath)).digest("hex");
|
|
46974
47311
|
}
|
|
46975
47312
|
function scanHome(definition, homesRoot) {
|
|
46976
47313
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -47041,7 +47378,7 @@ function humanHomes(scanned) {
|
|
|
47041
47378
|
}));
|
|
47042
47379
|
}
|
|
47043
47380
|
function writeStationSnapshot(options) {
|
|
47044
|
-
const repoRoot =
|
|
47381
|
+
const repoRoot = resolve23(options.repoRoot ?? process.cwd());
|
|
47045
47382
|
const { scanned, plans, totalBytes } = planStationSnapshot(options);
|
|
47046
47383
|
const manifestFiles = plans.map((plan) => ({
|
|
47047
47384
|
relativePath: plan.source.relativePath,
|
|
@@ -47067,9 +47404,9 @@ function writeStationSnapshot(options) {
|
|
|
47067
47404
|
const conflicts = [];
|
|
47068
47405
|
const untouched = [];
|
|
47069
47406
|
for (const plan of plans) {
|
|
47070
|
-
const destination =
|
|
47407
|
+
const destination = resolve23(repoRoot, plan.destination);
|
|
47071
47408
|
const destinationRelative = relative11(repoRoot, destination);
|
|
47072
|
-
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) ||
|
|
47409
|
+
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) || isAbsolute24(destinationRelative)) {
|
|
47073
47410
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
47074
47411
|
}
|
|
47075
47412
|
let existingDigest = null;
|
|
@@ -47090,8 +47427,8 @@ function writeStationSnapshot(options) {
|
|
|
47090
47427
|
}
|
|
47091
47428
|
let written = 0;
|
|
47092
47429
|
for (const plan of untouched) {
|
|
47093
|
-
const destination =
|
|
47094
|
-
mkdirSync17(
|
|
47430
|
+
const destination = resolve23(repoRoot, plan.destination);
|
|
47431
|
+
mkdirSync17(dirname27(destination), { recursive: true });
|
|
47095
47432
|
copyFileSync(plan.source.fullPath, destination);
|
|
47096
47433
|
written += 1;
|
|
47097
47434
|
}
|
|
@@ -47109,8 +47446,8 @@ function writeStationSnapshot(options) {
|
|
|
47109
47446
|
},
|
|
47110
47447
|
files: manifestFiles
|
|
47111
47448
|
};
|
|
47112
|
-
const manifestPath =
|
|
47113
|
-
mkdirSync17(
|
|
47449
|
+
const manifestPath = resolve23(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
|
|
47450
|
+
mkdirSync17(dirname27(manifestPath), { recursive: true });
|
|
47114
47451
|
writeFileSync15(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
47115
47452
|
`);
|
|
47116
47453
|
return {
|
|
@@ -47121,7 +47458,7 @@ function writeStationSnapshot(options) {
|
|
|
47121
47458
|
};
|
|
47122
47459
|
}
|
|
47123
47460
|
// src/lib/station-hydrate.ts
|
|
47124
|
-
import { createHash as
|
|
47461
|
+
import { createHash as createHash32 } from "crypto";
|
|
47125
47462
|
import {
|
|
47126
47463
|
copyFileSync as copyFileSync2,
|
|
47127
47464
|
mkdirSync as mkdirSync18,
|
|
@@ -47130,7 +47467,7 @@ import {
|
|
|
47130
47467
|
statSync as statSync16,
|
|
47131
47468
|
writeFileSync as writeFileSync16
|
|
47132
47469
|
} from "fs";
|
|
47133
|
-
import { dirname as
|
|
47470
|
+
import { dirname as dirname28, join as join42, resolve as resolve24, sep as sep14 } from "path";
|
|
47134
47471
|
var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
|
|
47135
47472
|
var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
|
|
47136
47473
|
var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
|
|
@@ -47327,12 +47664,12 @@ function skillSha256(skill) {
|
|
|
47327
47664
|
return sha256File(skill.files[0].winner.fullPath);
|
|
47328
47665
|
}
|
|
47329
47666
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
47330
|
-
return
|
|
47667
|
+
return createHash32("sha256").update(joined.sort().join(`
|
|
47331
47668
|
`)).digest("hex");
|
|
47332
47669
|
}
|
|
47333
47670
|
function writeStationHydration(options) {
|
|
47334
|
-
const repoRoot =
|
|
47335
|
-
const cacheRoot =
|
|
47671
|
+
const repoRoot = resolve24(options.repoRoot ?? process.cwd());
|
|
47672
|
+
const cacheRoot = resolve24(options.cacheRoot ?? resolveCorpusRoot());
|
|
47336
47673
|
const plan = planStationHydration(options.stationId, repoRoot);
|
|
47337
47674
|
const resultSkills = plan.winners.map((skill) => ({
|
|
47338
47675
|
ident: skill.ident,
|
|
@@ -47386,7 +47723,7 @@ function writeStationHydration(options) {
|
|
|
47386
47723
|
}
|
|
47387
47724
|
let written = 0;
|
|
47388
47725
|
for (const entry of toWrite) {
|
|
47389
|
-
mkdirSync18(
|
|
47726
|
+
mkdirSync18(dirname28(entry.destination), { recursive: true });
|
|
47390
47727
|
copyFileSync2(entry.fullPath, entry.destination);
|
|
47391
47728
|
written += 1;
|
|
47392
47729
|
}
|
|
@@ -47407,8 +47744,8 @@ function writeStationHydration(options) {
|
|
|
47407
47744
|
},
|
|
47408
47745
|
skills: resultSkills
|
|
47409
47746
|
};
|
|
47410
|
-
const hydrationManifestPath = join42(
|
|
47411
|
-
mkdirSync18(
|
|
47747
|
+
const hydrationManifestPath = join42(dirname28(cacheRoot), `hydration-${options.stationId}.json`);
|
|
47748
|
+
mkdirSync18(dirname28(hydrationManifestPath), { recursive: true });
|
|
47412
47749
|
writeFileSync16(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
47413
47750
|
`);
|
|
47414
47751
|
return {
|
|
@@ -47420,19 +47757,19 @@ function writeStationHydration(options) {
|
|
|
47420
47757
|
}
|
|
47421
47758
|
// src/lib/private-publication-recovery.ts
|
|
47422
47759
|
import { constants as constants15, closeSync as closeSync15, fsyncSync as fsyncSync7, fstatSync as fstatSync15, lstatSync as lstatSync27, mkdirSync as mkdirSync19, openSync as openSync15, readSync as readSync12, realpathSync as realpathSync14, renameSync as renameSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "fs";
|
|
47423
|
-
import { dirname as
|
|
47760
|
+
import { dirname as dirname29, isAbsolute as isAbsolute25, join as join43, resolve as resolve25 } from "path";
|
|
47424
47761
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
47425
47762
|
var fail3 = () => {
|
|
47426
47763
|
throw new PrivatePublicationError("PUBLICATION_RECOVERY_INVALID", "The recovery directory is invalid or changed. Preserve it and inspect the existing intent; do not start a replacement automatically.");
|
|
47427
47764
|
};
|
|
47428
47765
|
function safeDirectory(directory2) {
|
|
47429
|
-
if (!
|
|
47766
|
+
if (!isAbsolute25(directory2) || directory2 !== resolve25(directory2) || realpathSync14(directory2) !== directory2)
|
|
47430
47767
|
return fail3();
|
|
47431
|
-
for (let path = directory2;; path =
|
|
47768
|
+
for (let path = directory2;; path = dirname29(path)) {
|
|
47432
47769
|
const stat2 = lstatSync27(path);
|
|
47433
47770
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
47434
47771
|
return fail3();
|
|
47435
|
-
if (path ===
|
|
47772
|
+
if (path === dirname29(path))
|
|
47436
47773
|
break;
|
|
47437
47774
|
}
|
|
47438
47775
|
const own2 = lstatSync27(directory2);
|
|
@@ -47564,11 +47901,11 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
|
|
|
47564
47901
|
phase: "prepared",
|
|
47565
47902
|
intent: null
|
|
47566
47903
|
};
|
|
47567
|
-
if (!
|
|
47904
|
+
if (!isAbsolute25(recoveryDirectory) || resolve25(recoveryDirectory) !== recoveryDirectory || realpathSync14(dirname29(recoveryDirectory)) !== dirname29(recoveryDirectory))
|
|
47568
47905
|
return fail3();
|
|
47569
47906
|
mkdirSync19(recoveryDirectory, { mode: 448 });
|
|
47570
47907
|
safeDirectory(recoveryDirectory);
|
|
47571
|
-
const parent = openSync15(
|
|
47908
|
+
const parent = openSync15(dirname29(recoveryDirectory), constants15.O_RDONLY | constants15.O_NOFOLLOW);
|
|
47572
47909
|
try {
|
|
47573
47910
|
fsyncSync7(parent);
|
|
47574
47911
|
} finally {
|
|
@@ -47655,8 +47992,8 @@ async function inspectLocked(client2, directory2, cancel) {
|
|
|
47655
47992
|
return privatePublicationResult(directory2, receipt);
|
|
47656
47993
|
}
|
|
47657
47994
|
// src/lib/skill-context.ts
|
|
47658
|
-
import { createHash as
|
|
47659
|
-
import { resolve as
|
|
47995
|
+
import { createHash as createHash33 } from "crypto";
|
|
47996
|
+
import { resolve as resolve26 } from "path";
|
|
47660
47997
|
function words(value) {
|
|
47661
47998
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
47662
47999
|
}
|
|
@@ -47695,7 +48032,7 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
47695
48032
|
const promptWords = words(prompt);
|
|
47696
48033
|
const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
|
|
47697
48034
|
const loaded = new Set(resolved.session?.loaded ?? []);
|
|
47698
|
-
const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd,
|
|
48035
|
+
const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve26(input2.cwd)] : []];
|
|
47699
48036
|
const candidates = profile.selections.map((selection) => {
|
|
47700
48037
|
const key = selectionKey(selection);
|
|
47701
48038
|
if (input2.restore && loaded.has(key))
|
|
@@ -47722,7 +48059,7 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
47722
48059
|
const omitted = [];
|
|
47723
48060
|
let chars = 0;
|
|
47724
48061
|
for (const candidate2 of candidates) {
|
|
47725
|
-
const { selection, reason } = candidate2;
|
|
48062
|
+
const { selection, reason: reason2 } = candidate2;
|
|
47726
48063
|
if (!input2.restore && !explicit.has(selectionKey(selection)) && loaded.has(selectionKey(selection)))
|
|
47727
48064
|
continue;
|
|
47728
48065
|
const loadCommand = `skills load ${selection.slug}@${selection.version} --selection-profile ${profile.profileId}`;
|
|
@@ -47732,7 +48069,7 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
47732
48069
|
}
|
|
47733
48070
|
const entries = await readSelectedEntries(selection, resolved, resolverOptions);
|
|
47734
48071
|
const { content } = readSelectedDocument(entries);
|
|
47735
|
-
const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${
|
|
48072
|
+
const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${reason2})
|
|
47736
48073
|
${content}`;
|
|
47737
48074
|
if (maxChars !== undefined && chars + section.length + (sections.length ? 2 : 0) > maxChars) {
|
|
47738
48075
|
omitted.push({ slug: selection.slug, version: selection.version, reason: "context-budget", loadCommand });
|
|
@@ -47740,7 +48077,7 @@ ${content}`;
|
|
|
47740
48077
|
}
|
|
47741
48078
|
chars += section.length + (sections.length ? 2 : 0);
|
|
47742
48079
|
sections.push(section);
|
|
47743
|
-
selections.push({ ...selection, reason });
|
|
48080
|
+
selections.push({ ...selection, reason: reason2 });
|
|
47744
48081
|
loaded.add(selectionKey(selection));
|
|
47745
48082
|
}
|
|
47746
48083
|
const context2 = sections.join(`
|
|
@@ -47748,7 +48085,7 @@ ${content}`;
|
|
|
47748
48085
|
`);
|
|
47749
48086
|
const receipt = {
|
|
47750
48087
|
schemaVersion: 1,
|
|
47751
|
-
id:
|
|
48088
|
+
id: createHash33("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
47752
48089
|
profileId: profile.profileId,
|
|
47753
48090
|
profileRevision: profile.profileRevision,
|
|
47754
48091
|
authority: profile.authority,
|
|
@@ -47764,9 +48101,9 @@ ${content}`;
|
|
|
47764
48101
|
return { context: context2, selections, omitted, receipt };
|
|
47765
48102
|
}
|
|
47766
48103
|
// src/lib/session-reconciliation.ts
|
|
47767
|
-
import { createHash as
|
|
48104
|
+
import { createHash as createHash34 } from "crypto";
|
|
47768
48105
|
var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
|
|
47769
|
-
var digest3 = (value) =>
|
|
48106
|
+
var digest3 = (value) => createHash34("sha256").update(JSON.stringify(value)).digest("hex");
|
|
47770
48107
|
function exactTimestamp(value) {
|
|
47771
48108
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
47772
48109
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -47799,6 +48136,8 @@ function inspectSkillSession(sessionId, options = {}) {
|
|
|
47799
48136
|
loadedCount: receipt.loaded.length
|
|
47800
48137
|
};
|
|
47801
48138
|
}
|
|
48139
|
+
var SESSION_RENEWAL_REFUSAL_TTL_MS = 5 * 60 * 1000;
|
|
48140
|
+
var SESSION_RENEWAL_REFUSAL_MAX_BYTES = 4 * 1024;
|
|
47802
48141
|
async function reconcileSkillSession(input2, options = {}) {
|
|
47803
48142
|
if (![input2.fromProfile, input2.fromRevision, input2.selectionProfile, input2.profileRevision].every((value) => typeof value === "string" && value.trim()) || !/^[a-f0-9]{64}$/.test(input2.receiptSha256)) {
|
|
47804
48143
|
throw new SkillSelectionError("INVALID_SESSION_RECONCILIATION", "Name the exact old receipt SHA256, old profile/revision and intended target profile/revision.");
|
|
@@ -47986,6 +48325,7 @@ export {
|
|
|
47986
48325
|
isPortableWithinSkill,
|
|
47987
48326
|
isGatewayBackedSkill,
|
|
47988
48327
|
isExcludedSkillFileName,
|
|
48328
|
+
isDiscoveryRootUnresolved,
|
|
47989
48329
|
isCodexNativeSkillDisabled,
|
|
47990
48330
|
isBasicSkillName,
|
|
47991
48331
|
inventoryNativeSkills,
|
|
@@ -47998,6 +48338,7 @@ export {
|
|
|
47998
48338
|
inspectPrivatePublication,
|
|
47999
48339
|
importSkillsLocalSnapshot,
|
|
48000
48340
|
homePathFor,
|
|
48341
|
+
hashClaudeMarketplaceEntry,
|
|
48001
48342
|
getToolPrimitive,
|
|
48002
48343
|
getStorageStatus,
|
|
48003
48344
|
getStorageDatabaseUrl,
|
|
@@ -48082,6 +48423,7 @@ export {
|
|
|
48082
48423
|
captureClaudeSettings,
|
|
48083
48424
|
captureClaudeMarketplaceRegistryV2,
|
|
48084
48425
|
captureClaudeMarketplaceRegistry,
|
|
48426
|
+
captureClaudeMarketplaceEntry,
|
|
48085
48427
|
canonicalizeManifest,
|
|
48086
48428
|
buildSkillsS3ObjectUrl,
|
|
48087
48429
|
buildSkillsApiUrl,
|
|
@@ -48163,9 +48505,11 @@ export {
|
|
|
48163
48505
|
InvitationEmailInputError,
|
|
48164
48506
|
HttpProfileClient,
|
|
48165
48507
|
HostedApiError,
|
|
48508
|
+
DISCOVERY_ROOT_UNRESOLVED,
|
|
48166
48509
|
DEFAULT_EXPORT_DIR,
|
|
48167
48510
|
ContentHashInputError,
|
|
48168
48511
|
CONTENT_HASH_LIMITS,
|
|
48512
|
+
CLAUDE_MARKETPLACE_ENTRY_HASH_MODE,
|
|
48169
48513
|
CLAUDE_COORDINATED_HOOK_EVENTS,
|
|
48170
48514
|
CATEGORIES,
|
|
48171
48515
|
BASIC_SKILL_NAMES,
|