@hasna/skills 0.10.50 → 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 +86 -5
- package/bin/index.js +1827 -1374
- package/bin/maintenance.js +199 -2
- package/bin/mcp.js +730 -482
- 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 +1052 -704
- package/dist/lib/agent-discovery.d.ts +38 -2
- 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/codex-native-compatibility.d.ts +4 -1
- package/dist/lib/selection-cache.d.ts +2 -2
- package/dist/lib/session-reconciliation.d.ts +9 -0
- package/dist/sdk/index.js +1001 -658
- package/dist/server/maintenance-entry.d.ts +14 -0
- package/dist/server/schema-census.d.ts +77 -0
- package/docs/plugin-admission.md +190 -3
- 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,222 @@ 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";
|
|
9387
|
+
|
|
9388
|
+
// src/lib/codex-native-compatibility.ts
|
|
9389
|
+
var MEASURED_CODEX_CAPABILITIES = {
|
|
9390
|
+
"codex-cli 0.153.0": ["hooks"],
|
|
9391
|
+
"codex-cli 0.154.0": ["hooks"],
|
|
9392
|
+
"codex-cli 0.155.0": ["hooks"],
|
|
9393
|
+
"codex-cli 0.155.1": ["hooks"],
|
|
9394
|
+
"codex-cli 0.156.1": ["hooks"],
|
|
9395
|
+
"codex-cli 0.157.0": ["hooks"],
|
|
9396
|
+
"codex-cli 0.157.1": ["hooks"],
|
|
9397
|
+
"codex-cli 0.158.0": ["hooks"],
|
|
9398
|
+
"codex-cli 0.159.0": ["hooks"],
|
|
9399
|
+
"codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
|
|
9400
|
+
"codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
|
|
9401
|
+
"codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
|
|
9402
|
+
};
|
|
9403
|
+
function supportsCodexNativeCapability(version, capability) {
|
|
9404
|
+
return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
|
|
9405
|
+
}
|
|
9406
|
+
var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
|
|
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
|
+
|
|
9601
|
+
// src/lib/agent-policy-limits.ts
|
|
9383
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 });
|
|
9384
9603
|
function object(value) {
|
|
9385
9604
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -9433,7 +9652,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9433
9652
|
text(control.pluginId, 1024);
|
|
9434
9653
|
text(control.namespace, 64);
|
|
9435
9654
|
text(control.pluginParent);
|
|
9436
|
-
requireBound(
|
|
9655
|
+
requireBound(isAbsolute4(control.pluginParent) && resolve4(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
|
|
9437
9656
|
if (control.remotePluginId !== undefined)
|
|
9438
9657
|
requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
|
|
9439
9658
|
if (control.skillsOnly !== undefined)
|
|
@@ -9445,16 +9664,24 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9445
9664
|
}
|
|
9446
9665
|
if (bridge.codexInactivePlugins !== undefined) {
|
|
9447
9666
|
const controls = array(bridge.codexInactivePlugins, 4096), ids = new Set;
|
|
9448
|
-
requireBound(controls.length === 0 || object(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version
|
|
9667
|
+
requireBound(controls.length === 0 || object(bridge.codexPluginSkillReview) && supportsCodexNativeCapability(bridge.codexPluginSkillReview.version, "installed-plugin-review") && typeof bridge.codexPluginSkillReview.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(bridge.codexPluginSkillReview.catalogSha256));
|
|
9449
9668
|
for (const control of controls) {
|
|
9450
9669
|
requireBound(object(control) && Object.keys(control).every((key) => ["pluginId", "namespace", "pluginParent", "sourceType", "sourceSha256"].includes(key)));
|
|
9451
9670
|
text(control.pluginId, 1024);
|
|
9452
9671
|
text(control.namespace, 64);
|
|
9453
9672
|
text(control.pluginParent);
|
|
9454
|
-
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));
|
|
9455
9674
|
ids.add(control.pluginId);
|
|
9456
9675
|
}
|
|
9457
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
|
+
}
|
|
9458
9685
|
if (bridge.disabledBuiltins !== undefined)
|
|
9459
9686
|
for (const builtin of array(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
|
|
9460
9687
|
requireBound(object(builtin));
|
|
@@ -9480,11 +9707,11 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9480
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);
|
|
9481
9708
|
for (const root of roots) {
|
|
9482
9709
|
text(root);
|
|
9483
|
-
requireBound(
|
|
9710
|
+
requireBound(isAbsolute4(root) && resolve4(root) === root);
|
|
9484
9711
|
}
|
|
9485
9712
|
for (const parent of parents2) {
|
|
9486
9713
|
text(parent);
|
|
9487
|
-
requireBound(
|
|
9714
|
+
requireBound(isAbsolute4(parent) && resolve4(parent) === parent);
|
|
9488
9715
|
}
|
|
9489
9716
|
for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
9490
9717
|
requireBound(object(directory));
|
|
@@ -9497,14 +9724,14 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9497
9724
|
}
|
|
9498
9725
|
if (value.codexInstallationInputs !== undefined) {
|
|
9499
9726
|
const proof = record(value.codexInstallationInputs, 4), inputs = array(proof.plugins, 4096), ids = new Set;
|
|
9500
|
-
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && proof.version
|
|
9727
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && supportsCodexNativeCapability(proof.version, "installed-plugin-review") && typeof proof.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(proof.catalogSha256) && inputs.length > 0 && object(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === proof.version && bridge.codexPluginSkillReview.catalogSha256 === proof.catalogSha256);
|
|
9501
9728
|
for (const input of inputs) {
|
|
9502
9729
|
requireBound(object(input) && Object.keys(input).every((key) => ["pluginId", "namespace", "pluginParent", "sourceRoot", "sourceSha256"].includes(key)));
|
|
9503
9730
|
text(input.pluginId, 1024);
|
|
9504
9731
|
text(input.namespace, 64);
|
|
9505
9732
|
text(input.pluginParent);
|
|
9506
9733
|
text(input.sourceRoot);
|
|
9507
|
-
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));
|
|
9508
9735
|
ids.add(input.pluginId);
|
|
9509
9736
|
}
|
|
9510
9737
|
if (proof.directories !== undefined)
|
|
@@ -9521,17 +9748,19 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9521
9748
|
text(source.path);
|
|
9522
9749
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
9523
9750
|
if (source.hashMode !== undefined)
|
|
9524
|
-
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));
|
|
9525
9752
|
if (source.hashMode === "claude-plugin-manifest-v1")
|
|
9526
|
-
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);
|
|
9527
9754
|
if (source.hashMode === "sumi-settings-v1")
|
|
9528
|
-
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");
|
|
9529
9756
|
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
9530
|
-
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");
|
|
9531
9758
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
|
|
9532
|
-
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");
|
|
9533
9760
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
|
|
9534
|
-
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));
|
|
9535
9764
|
if (source.hashMode === "claude-plugin-registry") {
|
|
9536
9765
|
requireBound(agent === "claude");
|
|
9537
9766
|
const managed = array(source.managedPlugins, 64);
|
|
@@ -9546,8 +9775,8 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9546
9775
|
if (source.format !== undefined)
|
|
9547
9776
|
requireBound(["json", "toml", "yaml"].includes(source.format));
|
|
9548
9777
|
if (source.fields !== undefined)
|
|
9549
|
-
for (const
|
|
9550
|
-
text(
|
|
9778
|
+
for (const field2 of array(source.fields, AGENT_POLICY_LIMITS.fields))
|
|
9779
|
+
text(field2, 256);
|
|
9551
9780
|
}
|
|
9552
9781
|
if (value.directories !== undefined)
|
|
9553
9782
|
for (const directory of array(value.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
@@ -9562,9 +9791,9 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9562
9791
|
}
|
|
9563
9792
|
|
|
9564
9793
|
// src/lib/agent-discovery.ts
|
|
9565
|
-
import { createHash as
|
|
9794
|
+
import { createHash as createHash25 } from "crypto";
|
|
9566
9795
|
import { lstatSync as lstatSync25, readFileSync as readFileSync20, readdirSync as readdirSync10, realpathSync as realpathSync11 } from "fs";
|
|
9567
|
-
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";
|
|
9568
9797
|
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
9569
9798
|
|
|
9570
9799
|
// node_modules/yaml/dist/index.js
|
|
@@ -9614,7 +9843,7 @@ var $visit = visit.visit;
|
|
|
9614
9843
|
var $visitAsync = visit.visitAsync;
|
|
9615
9844
|
|
|
9616
9845
|
// src/lib/agent-hermes.ts
|
|
9617
|
-
import { dirname as
|
|
9846
|
+
import { dirname as dirname5, isAbsolute as isAbsolute5, join as join8, resolve as resolve5 } from "path";
|
|
9618
9847
|
import { lstatSync as lstatSync4, readFileSync as readFileSync6, opendirSync } from "fs";
|
|
9619
9848
|
var HERMES_OPT_OUT = ".no-bundled-skills";
|
|
9620
9849
|
var EVENTS = ["pre_llm_call", "pre_tool_call"];
|
|
@@ -9652,7 +9881,7 @@ function quoted(value) {
|
|
|
9652
9881
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
9653
9882
|
}
|
|
9654
9883
|
function hermesHookDefinitions(supervisor) {
|
|
9655
|
-
if (!
|
|
9884
|
+
if (!isAbsolute5(supervisor.runtime) || !isAbsolute5(supervisor.path))
|
|
9656
9885
|
throw new Error("Expected absolute Hermes supervisor paths");
|
|
9657
9886
|
const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
|
|
9658
9887
|
return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
|
|
@@ -9688,16 +9917,16 @@ function assertHermesEnvironment(home) {
|
|
|
9688
9917
|
throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
|
|
9689
9918
|
}
|
|
9690
9919
|
const selected = process.env.HERMES_HOME?.trim();
|
|
9691
|
-
if (selected && (!
|
|
9920
|
+
if (selected && (!isAbsolute5(selected) || resolve5(selected) !== join8(resolve5(home), ".hermes")))
|
|
9692
9921
|
throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
|
|
9693
9922
|
if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
|
|
9694
9923
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
|
|
9695
9924
|
}
|
|
9696
9925
|
function read(path) {
|
|
9697
|
-
for (let current = path;; current =
|
|
9926
|
+
for (let current = path;; current = dirname5(current)) {
|
|
9698
9927
|
if (lstatSync4(current, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
9699
9928
|
throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes protection input");
|
|
9700
|
-
if (
|
|
9929
|
+
if (dirname5(current) === current)
|
|
9701
9930
|
break;
|
|
9702
9931
|
}
|
|
9703
9932
|
const stat = lstatSync4(path, { throwIfNoEntry: false });
|
|
@@ -9711,7 +9940,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
|
|
|
9711
9940
|
assertHermesEnvironment(home);
|
|
9712
9941
|
if (read(join8(home, ".hermes", HERMES_OPT_OUT)) === null)
|
|
9713
9942
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
|
|
9714
|
-
if (!supervisor || !
|
|
9943
|
+
if (!supervisor || !isAbsolute5(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
|
|
9715
9944
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
|
|
9716
9945
|
const raw = read(join8(home, ".hermes/shell-hooks-allowlist.json"));
|
|
9717
9946
|
let trust;
|
|
@@ -9737,10 +9966,10 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
|
|
|
9737
9966
|
}
|
|
9738
9967
|
function assertNoHermesLegacyShadow(root) {
|
|
9739
9968
|
let entries = 0;
|
|
9740
|
-
for (let cursor =
|
|
9969
|
+
for (let cursor = resolve5(root);; cursor = dirname5(cursor)) {
|
|
9741
9970
|
if (lstatSync4(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
9742
9971
|
throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes legacy discovery root");
|
|
9743
|
-
if (
|
|
9972
|
+
if (dirname5(cursor) === cursor)
|
|
9744
9973
|
break;
|
|
9745
9974
|
}
|
|
9746
9975
|
function scan(path, depth) {
|
|
@@ -9761,10 +9990,10 @@ function assertNoHermesLegacyShadow(root) {
|
|
|
9761
9990
|
const hasDescriptor = Boolean(lstatSync4(join8(path, "SKILL.md"), { throwIfNoEntry: false }));
|
|
9762
9991
|
const directory = opendirSync(path);
|
|
9763
9992
|
try {
|
|
9764
|
-
for (let
|
|
9765
|
-
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))
|
|
9766
9995
|
continue;
|
|
9767
|
-
scan(join8(path,
|
|
9996
|
+
scan(join8(path, child2.name), depth + 1);
|
|
9768
9997
|
}
|
|
9769
9998
|
} finally {
|
|
9770
9999
|
directory.closeSync();
|
|
@@ -9821,18 +10050,18 @@ if (failed) process.exitCode = 2;
|
|
|
9821
10050
|
}
|
|
9822
10051
|
|
|
9823
10052
|
// src/lib/agent-discovery-directories.ts
|
|
9824
|
-
import { createHash as
|
|
10053
|
+
import { createHash as createHash5 } from "crypto";
|
|
9825
10054
|
import { lstatSync as lstatSync5, opendirSync as opendirSync2 } from "fs";
|
|
9826
|
-
import { dirname as
|
|
9827
|
-
function
|
|
10055
|
+
import { dirname as dirname6, isAbsolute as isAbsolute6, join as join9, resolve as resolve6 } from "path";
|
|
10056
|
+
function need3(value, message) {
|
|
9828
10057
|
if (!value)
|
|
9829
10058
|
throw new Error(message);
|
|
9830
10059
|
}
|
|
9831
10060
|
function safe(path) {
|
|
9832
|
-
|
|
9833
|
-
for (let cursor = path;; cursor =
|
|
9834
|
-
|
|
9835
|
-
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)
|
|
9836
10065
|
break;
|
|
9837
10066
|
}
|
|
9838
10067
|
}
|
|
@@ -9846,45 +10075,45 @@ function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
|
|
|
9846
10075
|
const root = lstatSync5(path, { throwIfNoEntry: false });
|
|
9847
10076
|
if (!root)
|
|
9848
10077
|
return { path, sha256: null };
|
|
9849
|
-
|
|
9850
|
-
const hash =
|
|
10078
|
+
need3(root.isDirectory(), "Unsupported native discovery directory input");
|
|
10079
|
+
const hash = createHash5("sha256");
|
|
9851
10080
|
function walk(current, relative2, before, depth) {
|
|
9852
|
-
|
|
10081
|
+
need3(depth <= 64, "Native discovery directory depth limit exceeded");
|
|
9853
10082
|
const names = [], directory = opendirSync2(current);
|
|
9854
10083
|
try {
|
|
9855
10084
|
for (let entry = directory.readSync();entry; entry = directory.readSync()) {
|
|
9856
|
-
|
|
10085
|
+
need3(++budget2.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
|
|
9857
10086
|
names.push(entry.name);
|
|
9858
10087
|
}
|
|
9859
10088
|
} finally {
|
|
9860
10089
|
directory.closeSync();
|
|
9861
10090
|
}
|
|
9862
|
-
|
|
10091
|
+
need3(new Set(names).size === names.length, "Ambiguous native discovery directory names");
|
|
9863
10092
|
for (const name of names.sort()) {
|
|
9864
|
-
const
|
|
9865
|
-
if (omittedRoots.includes(
|
|
10093
|
+
const child2 = join9(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
|
|
10094
|
+
if (omittedRoots.includes(child2))
|
|
9866
10095
|
continue;
|
|
9867
|
-
|
|
9868
|
-
const stat = lstatSync5(
|
|
9869
|
-
|
|
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");
|
|
9870
10099
|
const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
|
|
9871
10100
|
`;
|
|
9872
10101
|
budget2.bytes += Buffer.byteLength(row);
|
|
9873
|
-
|
|
10102
|
+
need3(budget2.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
|
|
9874
10103
|
hash.update(row);
|
|
9875
10104
|
rows?.push(row);
|
|
9876
|
-
if (stat.isDirectory() && !excludedRoots.includes(
|
|
9877
|
-
walk(
|
|
10105
|
+
if (stat.isDirectory() && !excludedRoots.includes(child2))
|
|
10106
|
+
walk(child2, childRelative, stat, depth + 1);
|
|
9878
10107
|
}
|
|
9879
|
-
|
|
10108
|
+
need3(same(before, lstatSync5(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
|
|
9880
10109
|
}
|
|
9881
10110
|
walk(path, "", root, 0);
|
|
9882
10111
|
safe(path);
|
|
9883
10112
|
return { path, sha256: hash.digest("hex") };
|
|
9884
10113
|
}
|
|
9885
10114
|
function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
|
|
9886
|
-
|
|
9887
|
-
|
|
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");
|
|
9888
10117
|
for (const root of omittedRoots)
|
|
9889
10118
|
safe(root);
|
|
9890
10119
|
const budget2 = { entries: 0, bytes: 0 };
|
|
@@ -9894,62 +10123,62 @@ function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
|
|
|
9894
10123
|
});
|
|
9895
10124
|
}
|
|
9896
10125
|
function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
|
|
9897
|
-
|
|
10126
|
+
need3(directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid projected discovery directory collection");
|
|
9898
10127
|
let count = 0, bytes = 0;
|
|
9899
10128
|
const expected = directories.map((directory) => {
|
|
9900
10129
|
safe(directory.path);
|
|
9901
|
-
|
|
9902
|
-
const hash =
|
|
10130
|
+
need3(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
|
|
10131
|
+
const hash = createHash5("sha256");
|
|
9903
10132
|
const kept = [];
|
|
9904
10133
|
for (const row of directory.entries) {
|
|
9905
|
-
|
|
10134
|
+
need3(typeof row === "string" && ++count <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Invalid reviewed directory membership rows");
|
|
9906
10135
|
bytes += Buffer.byteLength(row);
|
|
9907
|
-
|
|
10136
|
+
need3(bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Reviewed directory membership byte limit exceeded");
|
|
9908
10137
|
const value = JSON.parse(row);
|
|
9909
|
-
|
|
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) + `
|
|
9910
10139
|
`, "Invalid reviewed directory member");
|
|
9911
10140
|
hash.update(row);
|
|
9912
10141
|
const path = join9(directory.path, value[0]);
|
|
9913
10142
|
if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
|
|
9914
10143
|
kept.push(row);
|
|
9915
10144
|
}
|
|
9916
|
-
|
|
10145
|
+
need3(directory.sha256 === null ? directory.entries.length === 0 : hash.digest("hex") === directory.sha256, "Reviewed directory inventory does not match its fingerprint");
|
|
9917
10146
|
const absent = omittedRoots.some((root) => directory.path === root || directory.path.startsWith(root + "/"));
|
|
9918
|
-
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") };
|
|
9919
10148
|
});
|
|
9920
10149
|
const current = captureDiscoveryDirectoryProjection(directories.map((item) => item.path), omittedRoots);
|
|
9921
|
-
|
|
10150
|
+
need3(current.every((item, index) => item.sha256 === expected[index].sha256), "Native discovery directory membership changed outside retired Codex roots");
|
|
9922
10151
|
}
|
|
9923
10152
|
function captureDiscoveryDirectories(paths, excludedRoots = []) {
|
|
9924
|
-
|
|
10153
|
+
need3(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
|
|
9925
10154
|
const budget2 = { entries: 0, bytes: 0 };
|
|
9926
10155
|
for (const root of excludedRoots)
|
|
9927
10156
|
safe(root);
|
|
9928
10157
|
return paths.map((path) => capture(path, budget2, excludedRoots));
|
|
9929
10158
|
}
|
|
9930
10159
|
function verifyDiscoveryDirectories(directories, excludedRoots = []) {
|
|
9931
|
-
|
|
10160
|
+
need3(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
|
|
9932
10161
|
for (const directory of directories)
|
|
9933
|
-
|
|
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");
|
|
9934
10163
|
const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
|
|
9935
10164
|
for (let i = 0;i < directories.length; i++)
|
|
9936
|
-
|
|
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}`);
|
|
9937
10166
|
}
|
|
9938
10167
|
|
|
9939
10168
|
// src/lib/agent-discovery-bytes.ts
|
|
9940
|
-
import { createHash as
|
|
10169
|
+
import { createHash as createHash6 } from "crypto";
|
|
9941
10170
|
import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync6, openSync as openSync2, readSync as readSync2 } from "fs";
|
|
9942
|
-
import { dirname as
|
|
10171
|
+
import { dirname as dirname7, isAbsolute as isAbsolute7, resolve as resolve7 } from "path";
|
|
9943
10172
|
function discoveryByteBudget() {
|
|
9944
10173
|
return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
|
|
9945
10174
|
}
|
|
9946
10175
|
function safe2(path) {
|
|
9947
|
-
if (typeof path !== "string" || !
|
|
10176
|
+
if (typeof path !== "string" || !isAbsolute7(path) || path !== resolve7(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
|
|
9948
10177
|
throw new Error("Expected a canonical absolute raw discovery path");
|
|
9949
|
-
for (let cursor = path;; cursor =
|
|
10178
|
+
for (let cursor = path;; cursor = dirname7(cursor)) {
|
|
9950
10179
|
if (lstatSync6(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
9951
10180
|
throw new Error("Refusing symlink raw discovery input");
|
|
9952
|
-
if (
|
|
10181
|
+
if (dirname7(cursor) === cursor)
|
|
9953
10182
|
break;
|
|
9954
10183
|
}
|
|
9955
10184
|
}
|
|
@@ -9968,7 +10197,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
|
|
|
9968
10197
|
if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
9969
10198
|
throw new Error("Raw discovery file byte limit exceeded");
|
|
9970
10199
|
consume(budget2, bytes);
|
|
9971
|
-
return
|
|
10200
|
+
return createHash6("sha256").update(text2).digest("hex");
|
|
9972
10201
|
}
|
|
9973
10202
|
const initial = lstatSync6(path, { throwIfNoEntry: false });
|
|
9974
10203
|
if (!initial)
|
|
@@ -9982,7 +10211,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
|
|
|
9982
10211
|
const opened = fstatSync2(fd);
|
|
9983
10212
|
if (!opened.isFile() || !same2(initial, opened))
|
|
9984
10213
|
throw new Error("Raw discovery input changed during open");
|
|
9985
|
-
const hash =
|
|
10214
|
+
const hash = createHash6("sha256"), buffer = Buffer.alloc(64 * 1024);
|
|
9986
10215
|
let bytes = 0;
|
|
9987
10216
|
while (true) {
|
|
9988
10217
|
const count = readSync2(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
|
|
@@ -10005,9 +10234,9 @@ function hashRawDiscoveryFile(path, budget2, changes) {
|
|
|
10005
10234
|
}
|
|
10006
10235
|
|
|
10007
10236
|
// src/lib/agent-discovery-path-bytes.ts
|
|
10008
|
-
import { createHash as
|
|
10237
|
+
import { createHash as createHash7 } from "crypto";
|
|
10009
10238
|
import { lstatSync as lstatSync7, readlinkSync } from "fs";
|
|
10010
|
-
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";
|
|
10011
10240
|
function identity2(stat, mutable = false) {
|
|
10012
10241
|
return {
|
|
10013
10242
|
dev: String(stat.dev),
|
|
@@ -10018,12 +10247,12 @@ function identity2(stat, mutable = false) {
|
|
|
10018
10247
|
...mutable ? { size: String(stat.size), mtimeNs: String(stat.mtimeNs), ctimeNs: String(stat.ctimeNs) } : {}
|
|
10019
10248
|
};
|
|
10020
10249
|
}
|
|
10021
|
-
function
|
|
10022
|
-
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)
|
|
10023
10252
|
throw new Error("Expected a canonical absolute discovery path");
|
|
10024
10253
|
}
|
|
10025
10254
|
function trace(path) {
|
|
10026
|
-
|
|
10255
|
+
canonical3(path);
|
|
10027
10256
|
const rows = [], paths = [];
|
|
10028
10257
|
let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(sep3);
|
|
10029
10258
|
function record2(path2, row) {
|
|
@@ -10048,7 +10277,7 @@ function trace(path) {
|
|
|
10048
10277
|
if (!component || component === ".")
|
|
10049
10278
|
continue;
|
|
10050
10279
|
if (component === "..") {
|
|
10051
|
-
cursor =
|
|
10280
|
+
cursor = dirname8(cursor);
|
|
10052
10281
|
continue;
|
|
10053
10282
|
}
|
|
10054
10283
|
const candidate = join10(cursor, component);
|
|
@@ -10068,11 +10297,11 @@ function trace(path) {
|
|
|
10068
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)))
|
|
10069
10298
|
throw new Error("Discovery link changed during read");
|
|
10070
10299
|
record2(candidate, { type: "link", target, ...identity2(stat, true) });
|
|
10071
|
-
if (
|
|
10300
|
+
if (isAbsolute8(target)) {
|
|
10072
10301
|
cursor = parse2(target).root;
|
|
10073
10302
|
root();
|
|
10074
10303
|
}
|
|
10075
|
-
const remainder =
|
|
10304
|
+
const remainder = isAbsolute8(target) ? target.slice(parse2(target).root.length) : target;
|
|
10076
10305
|
pending = [...remainder.split(sep3), ...pending];
|
|
10077
10306
|
if (pending.length + steps > AGENT_POLICY_LIMITS.discoveryPathSteps)
|
|
10078
10307
|
throw new Error("Discovery path step limit exceeded");
|
|
@@ -10115,15 +10344,15 @@ function hashDiscoveryPathFile(path, budget2, changes) {
|
|
|
10115
10344
|
const after = trace(path);
|
|
10116
10345
|
if (JSON.stringify(before.rows) !== JSON.stringify(after.rows) || before.file !== after.file)
|
|
10117
10346
|
throw new Error("Discovery path input changed during read");
|
|
10118
|
-
return
|
|
10347
|
+
return createHash7("sha256").update(JSON.stringify({ version: 1, path, rows: before.rows, content })).digest("hex");
|
|
10119
10348
|
}
|
|
10120
10349
|
|
|
10121
10350
|
// src/lib/claude-plugin-manifest-witness.ts
|
|
10122
|
-
import { createHash as
|
|
10351
|
+
import { createHash as createHash8 } from "crypto";
|
|
10123
10352
|
var CLAUDE_PLUGIN_MANIFEST_LIMITS = Object.freeze({ bytes: 1024 * 1024, depth: 32, nodes: 65536, stringCharacters: 16384 });
|
|
10124
|
-
function requireValue(value,
|
|
10353
|
+
function requireValue(value, reason2) {
|
|
10125
10354
|
if (!value)
|
|
10126
|
-
throw new Error("Claude plugin manifest " +
|
|
10355
|
+
throw new Error("Claude plugin manifest " + reason2);
|
|
10127
10356
|
}
|
|
10128
10357
|
function parse3(text2) {
|
|
10129
10358
|
requireValue(Buffer.byteLength(text2, "utf8") <= CLAUDE_PLUGIN_MANIFEST_LIMITS.bytes, "exceeds its byte limit");
|
|
@@ -10199,7 +10428,7 @@ function parse3(text2) {
|
|
|
10199
10428
|
requireValue(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
|
|
10200
10429
|
return root;
|
|
10201
10430
|
}
|
|
10202
|
-
function
|
|
10431
|
+
function canonical4(value) {
|
|
10203
10432
|
if (value.kind === "string")
|
|
10204
10433
|
return JSON.stringify(value.value);
|
|
10205
10434
|
if (value.kind === "number")
|
|
@@ -10207,19 +10436,19 @@ function canonical3(value) {
|
|
|
10207
10436
|
if (value.kind === "literal")
|
|
10208
10437
|
return value.value;
|
|
10209
10438
|
if (value.kind === "array")
|
|
10210
|
-
return "[" + value.items.map(
|
|
10211
|
-
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(",") + "}";
|
|
10212
10441
|
}
|
|
10213
10442
|
var DESCRIPTIVE_FIELDS = new Set(["description", "version", "author", "homepage", "repository", "license", "keywords"]);
|
|
10214
|
-
function validateText(value,
|
|
10215
|
-
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");
|
|
10216
10445
|
}
|
|
10217
10446
|
function validateDescriptiveFields(manifest) {
|
|
10218
10447
|
const values = new Map(manifest.entries);
|
|
10219
|
-
for (const
|
|
10220
|
-
const value = values.get(
|
|
10448
|
+
for (const field2 of ["description", "version", "homepage", "license"]) {
|
|
10449
|
+
const value = values.get(field2);
|
|
10221
10450
|
if (value !== undefined)
|
|
10222
|
-
validateText(value,
|
|
10451
|
+
validateText(value, field2);
|
|
10223
10452
|
}
|
|
10224
10453
|
const author = values.get("author");
|
|
10225
10454
|
if (author !== undefined) {
|
|
@@ -10244,10 +10473,10 @@ function hashClaudePluginManifest(text2) {
|
|
|
10244
10473
|
const manifest = parse3(text2);
|
|
10245
10474
|
validateDescriptiveFields(manifest);
|
|
10246
10475
|
const discovery = { kind: "object", entries: manifest.entries.filter(([key]) => !DESCRIPTIVE_FIELDS.has(key)) };
|
|
10247
|
-
return
|
|
10476
|
+
return createHash8("sha256").update(canonical4(discovery)).digest("hex");
|
|
10248
10477
|
}
|
|
10249
10478
|
function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
|
|
10250
|
-
if (!/^[a-f0-9]{64}$/.test(reviewedSha256) ||
|
|
10479
|
+
if (!/^[a-f0-9]{64}$/.test(reviewedSha256) || createHash8("sha256").update(text2).digest("hex") !== reviewedSha256) {
|
|
10251
10480
|
throw new Error("Reviewed Claude plugin manifest changed before semantic projection");
|
|
10252
10481
|
}
|
|
10253
10482
|
return hashClaudePluginManifest(text2);
|
|
@@ -10255,22 +10484,22 @@ function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
|
|
|
10255
10484
|
|
|
10256
10485
|
// src/lib/plugin-discovery.ts
|
|
10257
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";
|
|
10258
|
-
import { basename as
|
|
10487
|
+
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute19, join as join31, resolve as resolve18 } from "path";
|
|
10259
10488
|
|
|
10260
10489
|
// src/lib/plugin-admission.ts
|
|
10261
10490
|
import { homedir as homedir5 } from "os";
|
|
10262
|
-
import { isAbsolute as
|
|
10491
|
+
import { isAbsolute as isAbsolute18, join as join30, resolve as resolve17 } from "path";
|
|
10263
10492
|
|
|
10264
10493
|
// src/lib/selection-cache.ts
|
|
10265
|
-
import { createHash as
|
|
10494
|
+
import { createHash as createHash10, randomUUID } from "crypto";
|
|
10266
10495
|
import { spawnSync } from "child_process";
|
|
10267
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";
|
|
10268
10497
|
import { hostname } from "os";
|
|
10269
|
-
import { dirname as
|
|
10498
|
+
import { dirname as dirname9, join as join12, parse as parse4, resolve as resolve9 } from "path";
|
|
10270
10499
|
import { isDeepStrictEqual } from "util";
|
|
10271
10500
|
|
|
10272
10501
|
// src/lib/skill-bundle.ts
|
|
10273
|
-
import { createHash as
|
|
10502
|
+
import { createHash as createHash9 } from "crypto";
|
|
10274
10503
|
import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
|
|
10275
10504
|
import { join as join11, relative as relative2 } from "path";
|
|
10276
10505
|
import { createGunzip } from "zlib";
|
|
@@ -10425,7 +10654,7 @@ function ownBytes(view) {
|
|
|
10425
10654
|
return out;
|
|
10426
10655
|
}
|
|
10427
10656
|
function sha256Hex(bytes) {
|
|
10428
|
-
return
|
|
10657
|
+
return createHash9("sha256").update(bytes).digest("hex");
|
|
10429
10658
|
}
|
|
10430
10659
|
function collectSkillBundleEntries(dir) {
|
|
10431
10660
|
const entries = [];
|
|
@@ -10621,12 +10850,12 @@ function inspectionLimits(options) {
|
|
|
10621
10850
|
for (const key of Object.keys(options.limits ?? {})) {
|
|
10622
10851
|
if (!Object.hasOwn(limits, key))
|
|
10623
10852
|
throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Unknown bundle limit");
|
|
10624
|
-
const
|
|
10625
|
-
const value = options.limits[
|
|
10626
|
-
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]) {
|
|
10627
10856
|
throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Bundle limits must be positive integers within the hard ceilings");
|
|
10628
10857
|
}
|
|
10629
|
-
limits[
|
|
10858
|
+
limits[field2] = value;
|
|
10630
10859
|
}
|
|
10631
10860
|
return limits;
|
|
10632
10861
|
}
|
|
@@ -10656,7 +10885,14 @@ async function inspectSkillBundle(bundle, options = {}) {
|
|
|
10656
10885
|
decoder.destroy(terminalError);
|
|
10657
10886
|
};
|
|
10658
10887
|
const onAbort = () => stop("BUNDLE_ABORTED");
|
|
10659
|
-
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())));
|
|
10660
10896
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
10661
10897
|
let decompressedByteSize = 0;
|
|
10662
10898
|
let bytesSinceYield = 0;
|
|
@@ -10671,7 +10907,7 @@ async function inspectSkillBundle(bundle, options = {}) {
|
|
|
10671
10907
|
parser2.push(chunk);
|
|
10672
10908
|
bytesSinceYield += chunk.byteLength;
|
|
10673
10909
|
if (bytesSinceYield >= 256 * 1024) {
|
|
10674
|
-
await new Promise((
|
|
10910
|
+
await new Promise((resolve9) => setTimeout(resolve9, 0));
|
|
10675
10911
|
bytesSinceYield = 0;
|
|
10676
10912
|
check();
|
|
10677
10913
|
}
|
|
@@ -10808,8 +11044,8 @@ class BoundedTarReader {
|
|
|
10808
11044
|
}
|
|
10809
11045
|
}
|
|
10810
11046
|
}
|
|
10811
|
-
function tarOctal(
|
|
10812
|
-
const text2 = new TextDecoder().decode(
|
|
11047
|
+
function tarOctal(field2) {
|
|
11048
|
+
const text2 = new TextDecoder().decode(field2);
|
|
10813
11049
|
if (!/^[0-7]+[\0 ]*$/.test(text2))
|
|
10814
11050
|
invalidBundle("Invalid tar octal field");
|
|
10815
11051
|
const value = Number.parseInt(text2, 8);
|
|
@@ -10900,8 +11136,8 @@ function profileSelectionSnapshot(selections) {
|
|
|
10900
11136
|
// src/lib/selection-cache.ts
|
|
10901
11137
|
class SkillSelectionError extends Error {
|
|
10902
11138
|
code;
|
|
10903
|
-
constructor(code, message) {
|
|
10904
|
-
super(message);
|
|
11139
|
+
constructor(code, message, options) {
|
|
11140
|
+
super(message, options);
|
|
10905
11141
|
this.code = code;
|
|
10906
11142
|
this.name = "SkillSelectionError";
|
|
10907
11143
|
}
|
|
@@ -10909,14 +11145,14 @@ class SkillSelectionError extends Error {
|
|
|
10909
11145
|
var MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
10910
11146
|
var SELECTION_LOCK_FILE = "selection.lock.json";
|
|
10911
11147
|
function selectionCacheRoot(options = {}) {
|
|
10912
|
-
return
|
|
11148
|
+
return resolve9(options.cacheDir ?? join12(getDataDirReadOnly(), "selection-cache"));
|
|
10913
11149
|
}
|
|
10914
11150
|
function selectionKey(selection) {
|
|
10915
11151
|
validateSelection(selection);
|
|
10916
11152
|
return hash([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
|
|
10917
11153
|
}
|
|
10918
11154
|
function hash(value) {
|
|
10919
|
-
return
|
|
11155
|
+
return createHash10("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10920
11156
|
}
|
|
10921
11157
|
function validateSelection(selection) {
|
|
10922
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()) {
|
|
@@ -10956,7 +11192,7 @@ function selectionBundlePath(selection, options = {}) {
|
|
|
10956
11192
|
return join12(selectionCacheRoot(options), "objects", hash(selection.authority), hash(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
|
|
10957
11193
|
}
|
|
10958
11194
|
function assertRegularPath(path, createParents = false) {
|
|
10959
|
-
const absolute =
|
|
11195
|
+
const absolute = resolve9(path);
|
|
10960
11196
|
const root = parse4(absolute).root;
|
|
10961
11197
|
let cursor = root;
|
|
10962
11198
|
const segments = absolute.slice(root.length).split("/");
|
|
@@ -11013,7 +11249,7 @@ function writeSelectionJson(path, value) {
|
|
|
11013
11249
|
}
|
|
11014
11250
|
function atomicWrite(path, bytes, mode, durable = false) {
|
|
11015
11251
|
assertRegularPath(path, true);
|
|
11016
|
-
const temporary = join12(
|
|
11252
|
+
const temporary = join12(dirname9(path), `.selection-${randomUUID()}.tmp`);
|
|
11017
11253
|
try {
|
|
11018
11254
|
const fd = openSync3(temporary, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, mode);
|
|
11019
11255
|
try {
|
|
@@ -11025,7 +11261,7 @@ function atomicWrite(path, bytes, mode, durable = false) {
|
|
|
11025
11261
|
}
|
|
11026
11262
|
renameSync(temporary, path);
|
|
11027
11263
|
if (durable)
|
|
11028
|
-
syncDirectory(
|
|
11264
|
+
syncDirectory(dirname9(path));
|
|
11029
11265
|
} finally {
|
|
11030
11266
|
try {
|
|
11031
11267
|
unlinkSync(temporary);
|
|
@@ -11035,8 +11271,8 @@ function atomicWrite(path, bytes, mode, durable = false) {
|
|
|
11035
11271
|
}
|
|
11036
11272
|
}
|
|
11037
11273
|
}
|
|
11038
|
-
function readSelectionJson(path) {
|
|
11039
|
-
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));
|
|
11040
11276
|
if (!bytes)
|
|
11041
11277
|
return null;
|
|
11042
11278
|
try {
|
|
@@ -11048,10 +11284,10 @@ function readSelectionJson(path) {
|
|
|
11048
11284
|
async function verifiedEntries(selection, bytes, signal, deadline) {
|
|
11049
11285
|
if (`sha256:${sha256Hex(bytes)}` !== selection.bundleDigest)
|
|
11050
11286
|
throw new SkillSelectionError("BUNDLE_DIGEST_MISMATCH", "The skill bundle does not match its selected digest.");
|
|
11051
|
-
const remaining = deadline === undefined ? undefined : Math.
|
|
11287
|
+
const remaining = deadline === undefined ? undefined : Math.ceil(deadline - performance.now());
|
|
11052
11288
|
signal?.throwIfAborted();
|
|
11053
11289
|
if (remaining !== undefined && remaining <= 0)
|
|
11054
|
-
throw new
|
|
11290
|
+
throw new SkillBundleInspectionError("BUNDLE_TIMEOUT", "Bundle inspection deadline exceeded");
|
|
11055
11291
|
const bundle = await inspectSkillBundle(bytes, { signal, ...remaining === undefined ? {} : { limits: { timeoutMs: remaining } } });
|
|
11056
11292
|
return bundle.entries;
|
|
11057
11293
|
}
|
|
@@ -11157,7 +11393,7 @@ function assertFreshCachedProfile(receipt, options = {}) {
|
|
|
11157
11393
|
}
|
|
11158
11394
|
}
|
|
11159
11395
|
function projectSelectionLockPath(projectDir) {
|
|
11160
|
-
return join12(
|
|
11396
|
+
return join12(resolve9(projectDir), ".skills", SELECTION_LOCK_FILE);
|
|
11161
11397
|
}
|
|
11162
11398
|
function readProjectSelection(projectDir) {
|
|
11163
11399
|
const receipt = readSelectionJson(projectSelectionLockPath(projectDir));
|
|
@@ -11316,13 +11552,13 @@ function publishSessionLock(path, marker) {
|
|
|
11316
11552
|
fsyncSync(fd);
|
|
11317
11553
|
linkSync(stagingPath, path);
|
|
11318
11554
|
published = true;
|
|
11319
|
-
syncDirectory(
|
|
11555
|
+
syncDirectory(dirname9(path));
|
|
11320
11556
|
const staged = lstatSync8(stagingPath);
|
|
11321
11557
|
if (!staged.isFile() || staged.dev !== stat.dev || staged.ino !== stat.ino) {
|
|
11322
11558
|
throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "The initialized lock staging inode changed before publication completed.");
|
|
11323
11559
|
}
|
|
11324
11560
|
unlinkSync(stagingPath);
|
|
11325
|
-
syncDirectory(
|
|
11561
|
+
syncDirectory(dirname9(path));
|
|
11326
11562
|
handedOff = true;
|
|
11327
11563
|
return { path, stagingPath, fd, dev: stat.dev, ino: stat.ino };
|
|
11328
11564
|
} finally {
|
|
@@ -11367,14 +11603,14 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
11367
11603
|
writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
|
|
11368
11604
|
`);
|
|
11369
11605
|
fsyncSync(guardFd);
|
|
11370
|
-
const cacheOptions = { cacheDir:
|
|
11606
|
+
const cacheOptions = { cacheDir: dirname9(dirname9(lock.path)) };
|
|
11371
11607
|
const current = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
11372
11608
|
if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
|
|
11373
11609
|
return null;
|
|
11374
11610
|
const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
|
|
11375
11611
|
if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
|
|
11376
11612
|
return null;
|
|
11377
|
-
const directory = join12(
|
|
11613
|
+
const directory = join12(dirname9(dirname9(lock.path)), "session-lock-recoveries", randomUUID());
|
|
11378
11614
|
const archivePath = join12(directory, "original.write-lock");
|
|
11379
11615
|
const recordPath = join12(directory, "recovery.json");
|
|
11380
11616
|
assertRegularPath(archivePath, true);
|
|
@@ -11391,7 +11627,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
11391
11627
|
})}
|
|
11392
11628
|
`));
|
|
11393
11629
|
syncDirectory(directory);
|
|
11394
|
-
syncDirectory(
|
|
11630
|
+
syncDirectory(dirname9(directory));
|
|
11395
11631
|
const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
11396
11632
|
if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
|
|
11397
11633
|
return null;
|
|
@@ -11406,7 +11642,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
11406
11642
|
archivedPaths.push(publicationPath);
|
|
11407
11643
|
}
|
|
11408
11644
|
syncDirectory(directory);
|
|
11409
|
-
syncDirectory(
|
|
11645
|
+
syncDirectory(dirname9(lock.path));
|
|
11410
11646
|
for (const path of archivedPaths) {
|
|
11411
11647
|
const before = lstatSync8(path, { throwIfNoEntry: false });
|
|
11412
11648
|
const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
|
|
@@ -11576,7 +11812,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11576
11812
|
archiveBytes(receiptPath, Buffer.from(`${JSON.stringify(receipt)}
|
|
11577
11813
|
`));
|
|
11578
11814
|
syncDirectory(directory);
|
|
11579
|
-
syncDirectory(
|
|
11815
|
+
syncDirectory(dirname9(directory));
|
|
11580
11816
|
syncDirectory(selectionCacheRoot(options));
|
|
11581
11817
|
if (sha256Hex(readRegularFile(archivePath, MAX_PROFILE_DOCUMENT_BYTES)) !== before.sha256 || !sameSnapshot(readSkillSessionSnapshot(replacement.sessionId, options), expected)) {
|
|
11582
11818
|
throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The session or its preservation archive changed before replacement; retained evidence must be reviewed.");
|
|
@@ -11596,7 +11832,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11596
11832
|
// package.json
|
|
11597
11833
|
var package_default = {
|
|
11598
11834
|
name: "@hasna/skills",
|
|
11599
|
-
version: "0.10.
|
|
11835
|
+
version: "0.10.52",
|
|
11600
11836
|
description: "Skills library for AI coding agents",
|
|
11601
11837
|
type: "module",
|
|
11602
11838
|
bin: {
|
|
@@ -11733,12 +11969,12 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
11733
11969
|
import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
|
|
11734
11970
|
import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
|
|
11735
11971
|
import { hostname as osHostname } from "os";
|
|
11736
|
-
import { isAbsolute as
|
|
11972
|
+
import { isAbsolute as isAbsolute9, join as join22 } from "path";
|
|
11737
11973
|
import { createRequire } from "module";
|
|
11738
11974
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11739
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";
|
|
11740
11976
|
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
11741
|
-
import { basename as
|
|
11977
|
+
import { basename as basename5, dirname as dirname10, join as join13, relative as relative3, sep as sep4 } from "path";
|
|
11742
11978
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
11743
11979
|
function envToken(name) {
|
|
11744
11980
|
return name.toUpperCase().replace(/-/g, "_");
|
|
@@ -11844,9 +12080,9 @@ function entryPoint(directory) {
|
|
|
11844
12080
|
return pathToFileURL(file).href;
|
|
11845
12081
|
}
|
|
11846
12082
|
function resolveInstalledSecrets(parentUrl) {
|
|
11847
|
-
let directory =
|
|
12083
|
+
let directory = dirname10(fileURLToPath(parentUrl));
|
|
11848
12084
|
for (;; ) {
|
|
11849
|
-
if (
|
|
12085
|
+
if (basename5(directory) !== "node_modules") {
|
|
11850
12086
|
const candidate = join13(directory, "node_modules", PACKAGE);
|
|
11851
12087
|
let absent = false;
|
|
11852
12088
|
try {
|
|
@@ -11859,7 +12095,7 @@ function resolveInstalledSecrets(parentUrl) {
|
|
|
11859
12095
|
if (!absent)
|
|
11860
12096
|
return entryPoint(candidate);
|
|
11861
12097
|
}
|
|
11862
|
-
const parent =
|
|
12098
|
+
const parent = dirname10(directory);
|
|
11863
12099
|
if (parent === directory)
|
|
11864
12100
|
throw new Error("Secrets SDK is not installed for this consumer");
|
|
11865
12101
|
directory = parent;
|
|
@@ -11879,8 +12115,8 @@ class CredentialResolutionError extends Error {
|
|
|
11879
12115
|
|
|
11880
12116
|
class CredentialFileUnsafeError extends Error {
|
|
11881
12117
|
path;
|
|
11882
|
-
constructor(path,
|
|
11883
|
-
super(`Refusing unsafe credential/config file ${path}: ${
|
|
12118
|
+
constructor(path, reason2) {
|
|
12119
|
+
super(`Refusing unsafe credential/config file ${path}: ${reason2}.`);
|
|
11884
12120
|
this.name = "CredentialFileUnsafeError";
|
|
11885
12121
|
this.path = path;
|
|
11886
12122
|
}
|
|
@@ -11906,7 +12142,7 @@ function homeDir(env) {
|
|
|
11906
12142
|
}
|
|
11907
12143
|
function absoluteOverride(env, key) {
|
|
11908
12144
|
const value = env[key]?.trim();
|
|
11909
|
-
return value &&
|
|
12145
|
+
return value && isAbsolute9(value) ? value : null;
|
|
11910
12146
|
}
|
|
11911
12147
|
function hasnaHomeDir(env) {
|
|
11912
12148
|
const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
|
|
@@ -11952,9 +12188,9 @@ function parseEnvFile(text2) {
|
|
|
11952
12188
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
11953
12189
|
continue;
|
|
11954
12190
|
let value = withoutExport.slice(equals + 1).trim();
|
|
11955
|
-
const
|
|
11956
|
-
if (
|
|
11957
|
-
if (value.length < 2 || !value.endsWith(
|
|
12191
|
+
const quote2 = value[0];
|
|
12192
|
+
if (quote2 === '"' || quote2 === "'") {
|
|
12193
|
+
if (value.length < 2 || !value.endsWith(quote2)) {
|
|
11958
12194
|
unusable.add(key);
|
|
11959
12195
|
continue;
|
|
11960
12196
|
}
|
|
@@ -11978,8 +12214,8 @@ function configFileReadsCoherent(before, after) {
|
|
|
11978
12214
|
return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs;
|
|
11979
12215
|
}
|
|
11980
12216
|
function readAppConfigFile(path) {
|
|
11981
|
-
const unsafe = (
|
|
11982
|
-
throw new CredentialFileUnsafeError(path,
|
|
12217
|
+
const unsafe = (reason2) => {
|
|
12218
|
+
throw new CredentialFileUnsafeError(path, reason2);
|
|
11983
12219
|
};
|
|
11984
12220
|
let fd = -1;
|
|
11985
12221
|
try {
|
|
@@ -12175,8 +12411,8 @@ function readKeychainItem(name, env, kind, options) {
|
|
|
12175
12411
|
try {
|
|
12176
12412
|
result = run(["find-generic-password", "-a", account, "-s", service, "-w"]);
|
|
12177
12413
|
} catch (error) {
|
|
12178
|
-
const
|
|
12179
|
-
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]);
|
|
12180
12416
|
}
|
|
12181
12417
|
if (result.status === KEYCHAIN_ITEM_NOT_FOUND_STATUS)
|
|
12182
12418
|
return null;
|
|
@@ -12994,7 +13230,7 @@ class MissingSkillsFleetError extends Error {
|
|
|
12994
13230
|
}
|
|
12995
13231
|
|
|
12996
13232
|
// src/lib/remote-files.ts
|
|
12997
|
-
import { createHash as
|
|
13233
|
+
import { createHash as createHash11 } from "crypto";
|
|
12998
13234
|
var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
12999
13235
|
function describeRemoteFiles(files) {
|
|
13000
13236
|
if (files.length > 10)
|
|
@@ -13012,7 +13248,7 @@ function describeRemoteFiles(files) {
|
|
|
13012
13248
|
});
|
|
13013
13249
|
}
|
|
13014
13250
|
function sha256(bytes) {
|
|
13015
|
-
return
|
|
13251
|
+
return createHash11("sha256").update(bytes).digest("hex");
|
|
13016
13252
|
}
|
|
13017
13253
|
async function readBoundedResponse(response, maximum, signal) {
|
|
13018
13254
|
if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
|
|
@@ -13070,6 +13306,10 @@ function identifier(value) {
|
|
|
13070
13306
|
function invalid() {
|
|
13071
13307
|
throw new Error("The Skills API returned an invalid profile response");
|
|
13072
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
|
+
}
|
|
13073
13313
|
function validateSelection2(value) {
|
|
13074
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))
|
|
13075
13315
|
invalid();
|
|
@@ -13117,8 +13357,8 @@ class HttpProfileClient {
|
|
|
13117
13357
|
...init.headers
|
|
13118
13358
|
}
|
|
13119
13359
|
});
|
|
13120
|
-
} catch {
|
|
13121
|
-
throw
|
|
13360
|
+
} catch (error) {
|
|
13361
|
+
throw unavailable(error, "Unable to reach the configured Skills API");
|
|
13122
13362
|
}
|
|
13123
13363
|
if (!response.ok) {
|
|
13124
13364
|
response.body?.cancel().catch(() => {});
|
|
@@ -13135,7 +13375,9 @@ class HttpProfileClient {
|
|
|
13135
13375
|
async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
|
|
13136
13376
|
try {
|
|
13137
13377
|
return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit, this.signal)));
|
|
13138
|
-
} catch {
|
|
13378
|
+
} catch (error) {
|
|
13379
|
+
if (this.signal?.aborted)
|
|
13380
|
+
throw unavailable(this.signal.reason ?? error, "The Skills API response was cut short");
|
|
13139
13381
|
invalid();
|
|
13140
13382
|
}
|
|
13141
13383
|
}
|
|
@@ -13222,17 +13464,17 @@ import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSyn
|
|
|
13222
13464
|
import { join as join14 } from "path";
|
|
13223
13465
|
|
|
13224
13466
|
// src/lib/preparation-process.ts
|
|
13225
|
-
function capturePreparationProcess(
|
|
13467
|
+
function capturePreparationProcess(child2, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
|
|
13226
13468
|
let spawnedPid;
|
|
13227
13469
|
try {
|
|
13228
|
-
spawnedPid =
|
|
13470
|
+
spawnedPid = child2.pid;
|
|
13229
13471
|
} catch {}
|
|
13230
13472
|
const groupId = typeof spawnedPid === "number" && Number.isSafeInteger(spawnedPid) && spawnedPid > 1 ? spawnedPid : undefined;
|
|
13231
13473
|
const canSignalGroup = platform !== "win32" && groupId !== undefined;
|
|
13232
13474
|
const killLiveChild = () => {
|
|
13233
|
-
if (
|
|
13475
|
+
if (child2.exitCode === null && child2.signalCode === null) {
|
|
13234
13476
|
try {
|
|
13235
|
-
|
|
13477
|
+
child2.kill("SIGKILL");
|
|
13236
13478
|
} catch {}
|
|
13237
13479
|
}
|
|
13238
13480
|
};
|
|
@@ -13431,20 +13673,20 @@ import { join as join28 } from "path";
|
|
|
13431
13673
|
// src/lib/codex-corpus-write.ts
|
|
13432
13674
|
import { KernelLock } from "@hasna/contracts/kernel-lock";
|
|
13433
13675
|
import { execFileSync } from "child_process";
|
|
13434
|
-
import { createHash as
|
|
13676
|
+
import { createHash as createHash12 } from "crypto";
|
|
13435
13677
|
import { accessSync, constants as constants6, lstatSync as lstatSync12, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
|
|
13436
|
-
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";
|
|
13437
13679
|
var KEY = ".native-corpus-admission";
|
|
13438
13680
|
var fail = () => {
|
|
13439
13681
|
throw new Error("CODEX_CORPUS_ADMISSION_REQUIRED: verified native admission and its existing shared lock are required");
|
|
13440
13682
|
};
|
|
13441
13683
|
function physicalTarget(path) {
|
|
13442
13684
|
const suffix = [];
|
|
13443
|
-
for (let cursor = path;; cursor =
|
|
13685
|
+
for (let cursor = path;; cursor = dirname11(cursor)) {
|
|
13444
13686
|
try {
|
|
13445
13687
|
return join15(realpathSync3(cursor), ...suffix);
|
|
13446
13688
|
} catch (error) {
|
|
13447
|
-
if (error.code !== "ENOENT" || cursor ===
|
|
13689
|
+
if (error.code !== "ENOENT" || cursor === dirname11(cursor))
|
|
13448
13690
|
throw error;
|
|
13449
13691
|
try {
|
|
13450
13692
|
if (lstatSync12(cursor).isSymbolicLink())
|
|
@@ -13453,14 +13695,14 @@ function physicalTarget(path) {
|
|
|
13453
13695
|
if (statError.code !== "ENOENT")
|
|
13454
13696
|
throw statError;
|
|
13455
13697
|
}
|
|
13456
|
-
suffix.unshift(
|
|
13698
|
+
suffix.unshift(basename6(cursor));
|
|
13457
13699
|
}
|
|
13458
13700
|
}
|
|
13459
13701
|
}
|
|
13460
13702
|
function codexCorpusRootForPath(path) {
|
|
13461
|
-
const target =
|
|
13703
|
+
const target = resolve10(path), physical = physicalTarget(target);
|
|
13462
13704
|
const selected = process.env.CODEX_HOME;
|
|
13463
|
-
const selectedRoots = selected &&
|
|
13705
|
+
const selectedRoots = selected && isAbsolute10(selected) ? [resolve10(selected), physicalTarget(resolve10(selected))] : [];
|
|
13464
13706
|
const classify = (candidate) => {
|
|
13465
13707
|
const checked = (root2) => {
|
|
13466
13708
|
if (candidate === root2 || candidate === join15(root2, `${KEY}.flock-v1`))
|
|
@@ -13469,10 +13711,10 @@ function codexCorpusRootForPath(path) {
|
|
|
13469
13711
|
};
|
|
13470
13712
|
for (const root2 of selectedRoots) {
|
|
13471
13713
|
const distance = relative4(root2, candidate);
|
|
13472
|
-
if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !
|
|
13714
|
+
if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !isAbsolute10(distance))
|
|
13473
13715
|
return checked(root2);
|
|
13474
13716
|
}
|
|
13475
|
-
for (let cursor = candidate;cursor !==
|
|
13717
|
+
for (let cursor = candidate;cursor !== dirname11(cursor); cursor = dirname11(cursor)) {
|
|
13476
13718
|
if (cursor.endsWith(`${sep5}.codex`))
|
|
13477
13719
|
return checked(cursor);
|
|
13478
13720
|
}
|
|
@@ -13498,10 +13740,10 @@ function executable(options) {
|
|
|
13498
13740
|
if (typeof command !== "string" || !command || command.includes("\x00") || options.codexSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.codexSha256))
|
|
13499
13741
|
return fail();
|
|
13500
13742
|
let found;
|
|
13501
|
-
if (
|
|
13743
|
+
if (isAbsolute10(command))
|
|
13502
13744
|
found = command;
|
|
13503
13745
|
else if (!command.includes(sep5)) {
|
|
13504
|
-
found = (process.env.PATH ?? "").split(delimiter).filter(
|
|
13746
|
+
found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute10).map((dir) => join15(dir, command)).find((candidate) => {
|
|
13505
13747
|
try {
|
|
13506
13748
|
accessSync(candidate, constants6.X_OK);
|
|
13507
13749
|
return true;
|
|
@@ -13515,7 +13757,7 @@ function executable(options) {
|
|
|
13515
13757
|
const path = realpathSync3(found), initial = statSync7(path, { bigint: true });
|
|
13516
13758
|
if (!initial.isFile() || (initial.mode & 0o022n) !== 0n || (initial.mode & 0o111n) === 0n || initial.uid !== BigInt(process.getuid()) && initial.uid !== 0n)
|
|
13517
13759
|
return fail();
|
|
13518
|
-
const hash2 = () =>
|
|
13760
|
+
const hash2 = () => createHash12("sha256").update(readFileSync11(path)).digest("hex");
|
|
13519
13761
|
const digest = hash2();
|
|
13520
13762
|
if (options.codexSha256 && digest !== options.codexSha256)
|
|
13521
13763
|
return fail();
|
|
@@ -13539,7 +13781,7 @@ function acquireCodexCorpusWrite(roots, options) {
|
|
|
13539
13781
|
}
|
|
13540
13782
|
};
|
|
13541
13783
|
try {
|
|
13542
|
-
if (roots.some((root) => typeof root !== "string" || !
|
|
13784
|
+
if (roots.some((root) => typeof root !== "string" || !isAbsolute10(root) || root.includes("\x00")))
|
|
13543
13785
|
fail();
|
|
13544
13786
|
const inputs = [...new Set(roots)].map((input) => ({ input, root: realpathSync3(input) })).sort((a, b) => a.root.localeCompare(b.root));
|
|
13545
13787
|
for (const { input, root } of inputs) {
|
|
@@ -13609,10 +13851,10 @@ import {
|
|
|
13609
13851
|
writeFileSync as writeFileSync7
|
|
13610
13852
|
} from "fs";
|
|
13611
13853
|
import { homedir as homedir3 } from "os";
|
|
13612
|
-
import { basename as
|
|
13854
|
+
import { basename as basename8, dirname as dirname16, join as join25 } from "path";
|
|
13613
13855
|
|
|
13614
13856
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13615
|
-
import { createHash as
|
|
13857
|
+
import { createHash as createHash13 } from "crypto";
|
|
13616
13858
|
|
|
13617
13859
|
// src/lib/codex-local-mcp-controls.ts
|
|
13618
13860
|
function isCodexLocalStdioMcpServer(name, server) {
|
|
@@ -13681,43 +13923,24 @@ function isCodexLocalStdioMcpServer(name, server) {
|
|
|
13681
13923
|
}
|
|
13682
13924
|
|
|
13683
13925
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13684
|
-
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";
|
|
13685
13927
|
import { lstatSync as lstatSync13, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync6 } from "fs";
|
|
13686
13928
|
|
|
13687
|
-
// src/lib/codex-native-compatibility.ts
|
|
13688
|
-
var MEASURED_CODEX_CAPABILITIES = {
|
|
13689
|
-
"codex-cli 0.153.0": ["hooks"],
|
|
13690
|
-
"codex-cli 0.154.0": ["hooks"],
|
|
13691
|
-
"codex-cli 0.155.0": ["hooks"],
|
|
13692
|
-
"codex-cli 0.155.1": ["hooks"],
|
|
13693
|
-
"codex-cli 0.156.1": ["hooks"],
|
|
13694
|
-
"codex-cli 0.157.0": ["hooks"],
|
|
13695
|
-
"codex-cli 0.157.1": ["hooks"],
|
|
13696
|
-
"codex-cli 0.158.0": ["hooks"],
|
|
13697
|
-
"codex-cli 0.159.0": ["hooks"],
|
|
13698
|
-
"codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
|
|
13699
|
-
"codex-cli 0.160.0": ["hooks", "qualified-skill-catalog"]
|
|
13700
|
-
};
|
|
13701
|
-
function supportsCodexNativeCapability(version, capability) {
|
|
13702
|
-
return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
|
|
13703
|
-
}
|
|
13704
|
-
var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
|
|
13705
|
-
|
|
13706
13929
|
// src/lib/codex-native-skill-catalog.ts
|
|
13707
13930
|
import { realpathSync as realpathSync5 } from "fs";
|
|
13708
|
-
import { isAbsolute as
|
|
13931
|
+
import { isAbsolute as isAbsolute12, normalize as normalize2 } from "path";
|
|
13709
13932
|
|
|
13710
13933
|
// src/lib/codex-hook-rpc.ts
|
|
13711
13934
|
import { join as join16 } from "path";
|
|
13712
13935
|
import { execFile, spawn } from "child_process";
|
|
13713
13936
|
import { realpathSync as realpathSync4, statSync as statSync8 } from "fs";
|
|
13714
|
-
import { isAbsolute as
|
|
13937
|
+
import { isAbsolute as isAbsolute11 } from "path";
|
|
13715
13938
|
import { promisify } from "util";
|
|
13716
13939
|
async function connectCodexHookRpc(options) {
|
|
13717
13940
|
const timeoutMs = options.timeoutMs ?? 20000;
|
|
13718
13941
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 20000)
|
|
13719
13942
|
throw new Error("CODEX_HOOK_TRUST_INVALID_TIMEOUT");
|
|
13720
|
-
const found =
|
|
13943
|
+
const found = isAbsolute11(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
|
|
13721
13944
|
if (!found)
|
|
13722
13945
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE: install Codex or pass --codex-command");
|
|
13723
13946
|
const binary = realpathSync4(found), stat = statSync8(binary);
|
|
@@ -13741,16 +13964,16 @@ async function connectCodexHookRpc(options) {
|
|
|
13741
13964
|
else
|
|
13742
13965
|
env.CODEX_HOME = options.codexHome;
|
|
13743
13966
|
const corpusLease = acquireCodexCorpusWrite([options.codexHome ?? join16(options.home, ".codex")], { codexCommand: binary });
|
|
13744
|
-
const
|
|
13967
|
+
const child2 = spawn(binary, ["app-server", "--strict-config", "--stdio"], {
|
|
13745
13968
|
cwd: options.home,
|
|
13746
13969
|
env,
|
|
13747
13970
|
stdio: ["pipe", "pipe", "pipe"]
|
|
13748
13971
|
});
|
|
13749
13972
|
let childClosed = false;
|
|
13750
|
-
const actualClose = new Promise((
|
|
13973
|
+
const actualClose = new Promise((resolve11) => child2.once("close", () => {
|
|
13751
13974
|
childClosed = true;
|
|
13752
13975
|
corpusLease.close();
|
|
13753
|
-
|
|
13976
|
+
resolve11();
|
|
13754
13977
|
}));
|
|
13755
13978
|
const pending = new Map;
|
|
13756
13979
|
let nextId = 1, buffer = Buffer.alloc(0), stopped = false, diagnosticBytes = 0;
|
|
@@ -13762,20 +13985,20 @@ async function connectCodexHookRpc(options) {
|
|
|
13762
13985
|
}
|
|
13763
13986
|
pending.clear();
|
|
13764
13987
|
};
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
|
|
13988
|
+
child2.on("error", fail2);
|
|
13989
|
+
child2.on("exit", fail2);
|
|
13990
|
+
child2.stdin.on("error", fail2);
|
|
13991
|
+
child2.stderr.on("data", (bytes) => {
|
|
13769
13992
|
diagnosticBytes += bytes.length;
|
|
13770
13993
|
if (diagnosticBytes > 2 * 1024 * 1024) {
|
|
13771
13994
|
fail2();
|
|
13772
|
-
|
|
13995
|
+
child2.kill();
|
|
13773
13996
|
}
|
|
13774
13997
|
});
|
|
13775
|
-
|
|
13998
|
+
child2.stdout.on("data", (bytes) => {
|
|
13776
13999
|
if (buffer.length + bytes.length > 8 * 1024 * 1024) {
|
|
13777
14000
|
fail2();
|
|
13778
|
-
|
|
14001
|
+
child2.kill();
|
|
13779
14002
|
return;
|
|
13780
14003
|
}
|
|
13781
14004
|
buffer = Buffer.concat([buffer, bytes]);
|
|
@@ -13790,12 +14013,12 @@ async function connectCodexHookRpc(options) {
|
|
|
13790
14013
|
value = JSON.parse(line.toString("utf8"));
|
|
13791
14014
|
} catch {
|
|
13792
14015
|
fail2();
|
|
13793
|
-
|
|
14016
|
+
child2.kill();
|
|
13794
14017
|
return;
|
|
13795
14018
|
}
|
|
13796
14019
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
13797
14020
|
fail2();
|
|
13798
|
-
|
|
14021
|
+
child2.kill();
|
|
13799
14022
|
return;
|
|
13800
14023
|
}
|
|
13801
14024
|
const waiting = pending.get(value.id);
|
|
@@ -13811,29 +14034,29 @@ async function connectCodexHookRpc(options) {
|
|
|
13811
14034
|
});
|
|
13812
14035
|
const rpc = {
|
|
13813
14036
|
version,
|
|
13814
|
-
processId:
|
|
14037
|
+
processId: child2.pid,
|
|
13815
14038
|
request(method, params) {
|
|
13816
14039
|
if (stopped)
|
|
13817
14040
|
return Promise.reject(new Error("CODEX_HOOK_TRUST_NATIVE_RPC_CLOSED"));
|
|
13818
|
-
return new Promise((
|
|
14041
|
+
return new Promise((resolve11, reject) => {
|
|
13819
14042
|
const id = nextId++;
|
|
13820
14043
|
const timer = setTimeout(() => {
|
|
13821
14044
|
fail2();
|
|
13822
|
-
|
|
14045
|
+
child2.kill();
|
|
13823
14046
|
}, timeoutMs);
|
|
13824
|
-
pending.set(id, { resolve:
|
|
13825
|
-
|
|
14047
|
+
pending.set(id, { resolve: resolve11, reject, timer });
|
|
14048
|
+
child2.stdin.write(JSON.stringify({ id, method, params }) + `
|
|
13826
14049
|
`);
|
|
13827
14050
|
});
|
|
13828
14051
|
},
|
|
13829
14052
|
async close() {
|
|
13830
14053
|
fail2();
|
|
13831
|
-
|
|
14054
|
+
child2.stdin.end();
|
|
13832
14055
|
if (childClosed)
|
|
13833
14056
|
return;
|
|
13834
|
-
await new Promise((
|
|
14057
|
+
await new Promise((resolve11, reject) => {
|
|
13835
14058
|
const terminate = setTimeout(() => {
|
|
13836
|
-
|
|
14059
|
+
child2.kill();
|
|
13837
14060
|
}, 1000);
|
|
13838
14061
|
const deadline = setTimeout(() => {
|
|
13839
14062
|
clearTimeout(terminate);
|
|
@@ -13842,7 +14065,7 @@ async function connectCodexHookRpc(options) {
|
|
|
13842
14065
|
actualClose.then(() => {
|
|
13843
14066
|
clearTimeout(terminate);
|
|
13844
14067
|
clearTimeout(deadline);
|
|
13845
|
-
|
|
14068
|
+
resolve11();
|
|
13846
14069
|
});
|
|
13847
14070
|
});
|
|
13848
14071
|
}
|
|
@@ -13852,7 +14075,7 @@ async function connectCodexHookRpc(options) {
|
|
|
13852
14075
|
const nativeVersion = version.slice("codex-cli ".length);
|
|
13853
14076
|
if (typeof initialized?.userAgent !== "string" || !(initialized.userAgent === `skills-native-hook-enrollment/${nativeVersion}` || initialized.userAgent.startsWith(`skills-native-hook-enrollment/${nativeVersion} `)))
|
|
13854
14077
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_VERSION_MISMATCH");
|
|
13855
|
-
|
|
14078
|
+
child2.stdin.write(`{"method":"initialized"}
|
|
13856
14079
|
`);
|
|
13857
14080
|
return rpc;
|
|
13858
14081
|
} catch {
|
|
@@ -13876,7 +14099,7 @@ function refuse() {
|
|
|
13876
14099
|
var object4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13877
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);
|
|
13878
14101
|
var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
|
|
13879
|
-
var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) &&
|
|
14102
|
+
var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute12(value);
|
|
13880
14103
|
function projectPluginSource(source) {
|
|
13881
14104
|
if (!object4(source))
|
|
13882
14105
|
return {};
|
|
@@ -14075,7 +14298,7 @@ function mcpControls(path, read2) {
|
|
|
14075
14298
|
function isBundledCleanupHooks(root, namespace, value) {
|
|
14076
14299
|
const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
14077
14300
|
const exactKeys = (v, keys) => Object.keys(v).length === keys.length && keys.every((key) => Object.hasOwn(v, key));
|
|
14078
|
-
if (
|
|
14301
|
+
if (basename7(dirname12(root)) !== namespace || basename7(dirname12(dirname12(root))) !== "openai-bundled")
|
|
14079
14302
|
return false;
|
|
14080
14303
|
const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
|
|
14081
14304
|
if (!server || !object5(value) || !exactKeys(value, ["hooks"]) || !object5(value.hooks))
|
|
@@ -14109,7 +14332,7 @@ function rootControls(root, read2) {
|
|
|
14109
14332
|
}
|
|
14110
14333
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
14111
14334
|
refuse2();
|
|
14112
|
-
const cacheVersion =
|
|
14335
|
+
const cacheVersion = basename7(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
14113
14336
|
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
14114
14337
|
refuse2();
|
|
14115
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 }))
|
|
@@ -14154,7 +14377,7 @@ function documentIdentity(document2, cache, read2) {
|
|
|
14154
14377
|
}
|
|
14155
14378
|
if (!front || typeof front !== "object" || !identifier2(front.name) || typeof front.description !== "string" || !front.description.trim())
|
|
14156
14379
|
refuse2();
|
|
14157
|
-
for (let root =
|
|
14380
|
+
for (let root = dirname12(document2), depth = 0;root.startsWith(cache + sep6) && depth < 12; root = dirname12(root), depth++) {
|
|
14158
14381
|
const path = join17(root, ".codex-plugin", "plugin.json");
|
|
14159
14382
|
if (!lstatSync13(path, { throwIfNoEntry: false }))
|
|
14160
14383
|
continue;
|
|
@@ -14172,9 +14395,9 @@ function documentIdentity(document2, cache, read2) {
|
|
|
14172
14395
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
14173
14396
|
refuse2();
|
|
14174
14397
|
const declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
14175
|
-
if (declaredVersion === null || (declaredVersion ? declaredVersion !==
|
|
14398
|
+
if (declaredVersion === null || (declaredVersion ? declaredVersion !== basename7(root) : basename7(root) !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
14176
14399
|
refuse2();
|
|
14177
|
-
return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent:
|
|
14400
|
+
return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent: dirname12(root), root };
|
|
14178
14401
|
}
|
|
14179
14402
|
refuse2();
|
|
14180
14403
|
}
|
|
@@ -14203,13 +14426,13 @@ function classifyCodexPluginCacheDocument(document2, cache, read2) {
|
|
|
14203
14426
|
function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
14204
14427
|
try {
|
|
14205
14428
|
const parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
|
|
14206
|
-
if (
|
|
14429
|
+
if (resolve11(parent) !== parent || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !enrolled.length || !Array.isArray(rules))
|
|
14207
14430
|
return false;
|
|
14208
|
-
for (let cursor = parent;; cursor =
|
|
14431
|
+
for (let cursor = parent;; cursor = dirname12(cursor)) {
|
|
14209
14432
|
const stat = lstatSync13(cursor, { throwIfNoEntry: false });
|
|
14210
14433
|
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
14211
14434
|
return false;
|
|
14212
|
-
if (
|
|
14435
|
+
if (dirname12(cursor) === cursor)
|
|
14213
14436
|
break;
|
|
14214
14437
|
}
|
|
14215
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));
|
|
@@ -14222,14 +14445,14 @@ function absentDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
|
14222
14445
|
}
|
|
14223
14446
|
function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbsent = false) {
|
|
14224
14447
|
try {
|
|
14225
|
-
const parent =
|
|
14226
|
-
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))
|
|
14227
14450
|
return false;
|
|
14228
|
-
for (let cursor = root;; cursor =
|
|
14451
|
+
for (let cursor = root;; cursor = dirname12(cursor)) {
|
|
14229
14452
|
const stat = lstatSync13(cursor, { throwIfNoEntry: false });
|
|
14230
14453
|
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
14231
14454
|
return false;
|
|
14232
|
-
if (
|
|
14455
|
+
if (dirname12(cursor) === cursor)
|
|
14233
14456
|
break;
|
|
14234
14457
|
}
|
|
14235
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)))
|
|
@@ -14276,8 +14499,8 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
|
|
|
14276
14499
|
for (const name of names) {
|
|
14277
14500
|
let root = join17(parent, name), stat = lstatSync13(root);
|
|
14278
14501
|
if (stat.isSymbolicLink()) {
|
|
14279
|
-
const target =
|
|
14280
|
-
if (name !== "latest" ||
|
|
14502
|
+
const target = resolve11(parent, readlinkSync3(root));
|
|
14503
|
+
if (name !== "latest" || dirname12(target) !== parent || !lstatSync13(target).isDirectory() || realpathSync6(target) !== target)
|
|
14281
14504
|
return false;
|
|
14282
14505
|
root = target;
|
|
14283
14506
|
stat = lstatSync13(root);
|
|
@@ -14297,6 +14520,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
|
|
|
14297
14520
|
function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules, pluginSettings, plannedRules, denials = []) {
|
|
14298
14521
|
if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
|
|
14299
14522
|
refuse2();
|
|
14523
|
+
const installedPluginReview = supportsCodexNativeCapability(catalog.version, "installed-plugin-review");
|
|
14300
14524
|
if (!Array.isArray(denials) || denials.length > 4096 || new Set(denials.map((item) => item?.path)).size !== denials.length || denials.some((item) => !item || Object.keys(item).sort().join(",") !== "name,path,sha256" || typeof item.name !== "string" || typeof item.path !== "string" || !/^[a-f0-9]{64}$/.test(item.sha256)))
|
|
14301
14525
|
refuse2();
|
|
14302
14526
|
const usedDenials = new Set;
|
|
@@ -14328,7 +14552,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14328
14552
|
if (listed.length > 1)
|
|
14329
14553
|
refuse2();
|
|
14330
14554
|
const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
|
|
14331
|
-
if (
|
|
14555
|
+
if (installedPluginReview && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename7(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
|
|
14332
14556
|
const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
|
|
14333
14557
|
if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache + sep6) && (lstatSync13(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep6)))) {
|
|
14334
14558
|
if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep6)) || !sourceInputUnchanged(input, cache, read2))
|
|
@@ -14337,7 +14561,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14337
14561
|
}
|
|
14338
14562
|
}
|
|
14339
14563
|
const settings = pluginSettings && typeof pluginSettings === "object" && !Array.isArray(pluginSettings) ? pluginSettings : undefined;
|
|
14340
|
-
if (
|
|
14564
|
+
if (installedPluginReview && !listed.length && !skills.some((skill) => skill.pluginId === expectedPluginId) && installed.length === 1 && installed[0].installed && !installed[0].enabled && codexPluginSourceIsConfigControlled(installed[0]) && settings && Object.hasOwn(settings, expectedPluginId) && settings[expectedPluginId]?.enabled === false) {
|
|
14341
14565
|
if (!Array.isArray(rules))
|
|
14342
14566
|
refuse2();
|
|
14343
14567
|
const exactPathDeny = rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
|
|
@@ -14360,7 +14584,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14360
14584
|
refuse2();
|
|
14361
14585
|
const plugin = installed[0];
|
|
14362
14586
|
if (plugin.localVersion !== null) {
|
|
14363
|
-
if (plugin.localVersion !==
|
|
14587
|
+
if (plugin.localVersion !== basename7(dirname12(dirname12(dirname12(document2)))))
|
|
14364
14588
|
continue;
|
|
14365
14589
|
} else {
|
|
14366
14590
|
if (plugin.sourceType !== "remote" || !plugin.remotePluginId || !remoteInstallationMatches(parsed.pluginParent, plugin.remotePluginId, read2) || !lstatSync13(document2).isFile() || realpathSync6(document2) !== document2)
|
|
@@ -14370,12 +14594,12 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14370
14594
|
const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
|
|
14371
14595
|
const nativeSkill = { name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true };
|
|
14372
14596
|
const denial = denials.find((item) => item.path === document2);
|
|
14373
|
-
const explicitDeny = denial !== undefined && remotePluginId !== undefined &&
|
|
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);
|
|
14374
14598
|
if (denial && !explicitDeny)
|
|
14375
14599
|
refuse2();
|
|
14376
14600
|
if (explicitDeny)
|
|
14377
14601
|
usedDenials.add(document2);
|
|
14378
|
-
const plannedPathDeny =
|
|
14602
|
+
const plannedPathDeny = installedPluginReview && remotePluginId !== undefined && Array.isArray(rules) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === parsed.name && rule.enabled === false) && !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) && isCodexNativeSkillDisabled(nativeSkill, plannedRules);
|
|
14379
14603
|
if (!explicitDeny && (!exactPathDeny && !plannedPathDeny || !isCodexNativeSkillDisabled(nativeSkill, rules)))
|
|
14380
14604
|
refuse2();
|
|
14381
14605
|
}
|
|
@@ -14391,7 +14615,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14391
14615
|
function sourceInputUnchanged(input, cache, read2) {
|
|
14392
14616
|
try {
|
|
14393
14617
|
const parts = relative5(cache, input.pluginParent).split(sep6);
|
|
14394
|
-
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))
|
|
14395
14619
|
return false;
|
|
14396
14620
|
const path = join17(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync13(path);
|
|
14397
14621
|
if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync6(path) !== path)
|
|
@@ -14457,9 +14681,9 @@ enabled = false
|
|
|
14457
14681
|
}
|
|
14458
14682
|
|
|
14459
14683
|
// src/lib/codex-native-skill-policy.ts
|
|
14460
|
-
import { createHash as
|
|
14684
|
+
import { createHash as createHash14, randomUUID as randomUUID2 } from "crypto";
|
|
14461
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";
|
|
14462
|
-
import { dirname as
|
|
14686
|
+
import { dirname as dirname13, isAbsolute as isAbsolute13, join as join18, resolve as resolve12 } from "path";
|
|
14463
14687
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
14464
14688
|
var CODEX_NATIVE_POLICY_CAPABILITY = "host-path-allowlist-v1";
|
|
14465
14689
|
var CODEX_NATIVE_POLICY_RECEIPT_SCHEMA = "skills.codex-native-policy-acceptance/v1";
|
|
@@ -14482,9 +14706,9 @@ function refuse3(code, detail) {
|
|
|
14482
14706
|
throw new Error(`${code}: ${detail}`);
|
|
14483
14707
|
}
|
|
14484
14708
|
var object5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
14485
|
-
function boundedId(value,
|
|
14709
|
+
function boundedId(value, field2) {
|
|
14486
14710
|
if (typeof value !== "string" || !value || value.length > MAX_ENVELOPE_ID_LENGTH || value.trim() !== value || /[\u0000-\u001f\u007f]/.test(value))
|
|
14487
|
-
refuse3("NATIVE_SKILL_POLICY_INVALID", `${
|
|
14711
|
+
refuse3("NATIVE_SKILL_POLICY_INVALID", `${field2} must be a bounded non-empty string`);
|
|
14488
14712
|
return value;
|
|
14489
14713
|
}
|
|
14490
14714
|
function parseCodexNativePolicyTrust(value) {
|
|
@@ -14507,9 +14731,9 @@ function parseCodexNativeHookEnvelope(envelope, bridgeDocument) {
|
|
|
14507
14731
|
if (!object5(value))
|
|
14508
14732
|
refuse3("NATIVE_SKILL_POLICY_INVALID", "native_skill_policy must be an object");
|
|
14509
14733
|
const keys = Object.keys(value);
|
|
14510
|
-
for (const
|
|
14511
|
-
if (!Object.hasOwn(value,
|
|
14512
|
-
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`);
|
|
14513
14737
|
if (keys.length !== POLICY_FIELDS.length)
|
|
14514
14738
|
refuse3("NATIVE_SKILL_POLICY_INVALID", "native_skill_policy carries unknown fields");
|
|
14515
14739
|
if (value.capability !== CODEX_NATIVE_POLICY_CAPABILITY)
|
|
@@ -14573,19 +14797,19 @@ function readBounded(path, maximum, what) {
|
|
|
14573
14797
|
}
|
|
14574
14798
|
}
|
|
14575
14799
|
function verifyCodexNativeBridgeDocument(path, expectedContent, expectedSha256) {
|
|
14576
|
-
if (!
|
|
14800
|
+
if (!isAbsolute13(path) || resolve12(path) !== path || !path.endsWith("/SKILL.md"))
|
|
14577
14801
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document path is not an exact absolute SKILL.md path");
|
|
14578
|
-
for (let cursor = path;; cursor =
|
|
14802
|
+
for (let cursor = path;; cursor = dirname13(cursor)) {
|
|
14579
14803
|
const stat = lstatSync14(cursor, { throwIfNoEntry: false });
|
|
14580
14804
|
if (!stat || stat.isSymbolicLink() || (cursor === path ? !stat.isFile() : !stat.isDirectory()))
|
|
14581
14805
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", `the bridge path has a missing, linked or unexpected component: ${cursor}`);
|
|
14582
|
-
if (
|
|
14806
|
+
if (dirname13(cursor) === cursor)
|
|
14583
14807
|
break;
|
|
14584
14808
|
}
|
|
14585
14809
|
if (realpathSync7.native(path) !== path)
|
|
14586
14810
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document real path differs from its lexical path");
|
|
14587
14811
|
const bytes = readBounded(path, MAX_BRIDGE_DOCUMENT_BYTES, "the bridge document");
|
|
14588
|
-
const sha2562 =
|
|
14812
|
+
const sha2562 = createHash14("sha256").update(bytes).digest("hex");
|
|
14589
14813
|
if (!bytes.equals(Buffer.from(expectedContent, "utf8")) || sha2562 !== expectedSha256)
|
|
14590
14814
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document bytes differ from the managed bridge");
|
|
14591
14815
|
return { path, sha256: sha2562 };
|
|
@@ -14651,13 +14875,13 @@ function assertExecutableWitnessStat(stat, currentUid) {
|
|
|
14651
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) };
|
|
14652
14876
|
}
|
|
14653
14877
|
function executableWitness(path) {
|
|
14654
|
-
if (!
|
|
14878
|
+
if (!isAbsolute13(path) || resolve12(path) !== path)
|
|
14655
14879
|
refuse3("NATIVE_SKILL_POLICY_PROCESS_UNBOUND", "the consumer executable path is not an exact absolute path");
|
|
14656
|
-
for (let cursor =
|
|
14880
|
+
for (let cursor = dirname13(path);; cursor = dirname13(cursor)) {
|
|
14657
14881
|
const stat2 = lstatSync14(cursor, { throwIfNoEntry: false });
|
|
14658
14882
|
if (!stat2 || stat2.isSymbolicLink() || !stat2.isDirectory())
|
|
14659
14883
|
refuse3("NATIVE_SKILL_POLICY_PROCESS_UNBOUND", `the consumer executable path has a missing, linked or unexpected component: ${cursor}`);
|
|
14660
|
-
if (
|
|
14884
|
+
if (dirname13(cursor) === cursor)
|
|
14661
14885
|
break;
|
|
14662
14886
|
}
|
|
14663
14887
|
const stat = lstatSync14(path, { throwIfNoEntry: false, bigint: true });
|
|
@@ -14668,7 +14892,7 @@ function executableWitness(path) {
|
|
|
14668
14892
|
var witnessKey = (path, witness) => JSON.stringify([path, witness.dev, witness.ino, witness.size, witness.mtimeNs, witness.ctimeNs, witness.uid]);
|
|
14669
14893
|
var executableDigestMemory = new Map;
|
|
14670
14894
|
function executableCachePath(dataDir2) {
|
|
14671
|
-
return join18(
|
|
14895
|
+
return join18(resolve12(dataDir2), "agent-hooks", "codex-native-policy-executable-cache.json");
|
|
14672
14896
|
}
|
|
14673
14897
|
var currentUid = () => process.getuid?.() ?? -1;
|
|
14674
14898
|
function readTrustedCacheBytes(path, ownerUid) {
|
|
@@ -14713,16 +14937,16 @@ function readExecutableCache(dataDir2, ownerUid = currentUid()) {
|
|
|
14713
14937
|
if (!object5(value) || value.schema !== CODEX_NATIVE_POLICY_EXECUTABLE_CACHE_SCHEMA || !Array.isArray(value.entries) || value.entries.length > 16)
|
|
14714
14938
|
return [];
|
|
14715
14939
|
const fields = ["path", "sha256", "dev", "ino", "size", "mtimeNs", "ctimeNs", "uid"];
|
|
14716
|
-
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])));
|
|
14717
14941
|
} catch {
|
|
14718
14942
|
return [];
|
|
14719
14943
|
}
|
|
14720
14944
|
}
|
|
14721
14945
|
function writeExecutableCache(dataDir2, entries) {
|
|
14722
|
-
const path = executableCachePath(dataDir2), directory =
|
|
14946
|
+
const path = executableCachePath(dataDir2), directory = dirname13(path), temporary = `${path}.skills-${randomUUID2()}`;
|
|
14723
14947
|
let descriptor;
|
|
14724
14948
|
try {
|
|
14725
|
-
const parent = lstatSync14(
|
|
14949
|
+
const parent = lstatSync14(dirname13(directory), { throwIfNoEntry: false });
|
|
14726
14950
|
if (!parent || parent.isSymbolicLink() || !parent.isDirectory())
|
|
14727
14951
|
return;
|
|
14728
14952
|
const existing = lstatSync14(directory, { throwIfNoEntry: false });
|
|
@@ -14790,16 +15014,16 @@ function verifyCodexNativeExecutable(inspector, processId, trust, cache = {}, ex
|
|
|
14790
15014
|
}
|
|
14791
15015
|
function assertOperatorTrustRoot(dataDir2) {
|
|
14792
15016
|
const check = (path, what, required) => {
|
|
14793
|
-
if (!
|
|
15017
|
+
if (!isAbsolute13(path) || resolve12(path) !== path)
|
|
14794
15018
|
refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} path is not an exact absolute path`);
|
|
14795
|
-
for (let cursor =
|
|
15019
|
+
for (let cursor = dirname13(path);; cursor = dirname13(cursor)) {
|
|
14796
15020
|
const stat2 = lstatSync14(cursor, { throwIfNoEntry: false });
|
|
14797
15021
|
if (!stat2) {
|
|
14798
15022
|
if (required)
|
|
14799
15023
|
refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} has a missing path component: ${cursor}`);
|
|
14800
15024
|
} else if (stat2.isSymbolicLink() || !stat2.isDirectory())
|
|
14801
15025
|
refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} has a linked or unexpected path component: ${cursor}`);
|
|
14802
|
-
if (
|
|
15026
|
+
if (dirname13(cursor) === cursor)
|
|
14803
15027
|
break;
|
|
14804
15028
|
}
|
|
14805
15029
|
const stat = lstatSync14(path, { throwIfNoEntry: false, bigint: true });
|
|
@@ -14829,9 +15053,9 @@ function assertOperatorTrustRoot(dataDir2) {
|
|
|
14829
15053
|
if ((stat.mode & 0o022n) !== 0n)
|
|
14830
15054
|
refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} is group- or world-writable`);
|
|
14831
15055
|
};
|
|
14832
|
-
checkDirectory(
|
|
14833
|
-
checkDirectory(
|
|
14834
|
-
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);
|
|
14835
15059
|
check(executableCachePath(dataDir2), "the executable identity cache", false);
|
|
14836
15060
|
}
|
|
14837
15061
|
function runCodexNativePolicyHelper(request) {
|
|
@@ -15002,12 +15226,12 @@ function defaultProcessInspector() {
|
|
|
15002
15226
|
return { platform: process.platform, arch: process.arch, pid: process.pid, parentOf: () => null, startTime: () => null, executablePath: () => null };
|
|
15003
15227
|
}
|
|
15004
15228
|
function codexNativePolicyReceiptPath(dataDir2) {
|
|
15005
|
-
return join18(
|
|
15229
|
+
return join18(resolve12(dataDir2), "agent-hooks", "codex-native-policy-acceptance.json");
|
|
15006
15230
|
}
|
|
15007
15231
|
function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
|
|
15008
|
-
const path = codexNativePolicyReceiptPath(dataDir2), directory =
|
|
15232
|
+
const path = codexNativePolicyReceiptPath(dataDir2), directory = dirname13(path);
|
|
15009
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);
|
|
15010
|
-
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)
|
|
15011
15235
|
refuse3("NATIVE_SKILL_POLICY_RECEIPT_FAILED", "accepted cache entries must be unique absolute paths with tree digests");
|
|
15012
15236
|
const value = {
|
|
15013
15237
|
schema: CODEX_NATIVE_POLICY_RECEIPT_SCHEMA,
|
|
@@ -15053,11 +15277,11 @@ function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
|
|
|
15053
15277
|
}
|
|
15054
15278
|
|
|
15055
15279
|
// src/lib/codex-settings-witness-v1.ts
|
|
15056
|
-
import { createHash as
|
|
15057
|
-
var sha = (text2) =>
|
|
15280
|
+
import { createHash as createHash15 } from "crypto";
|
|
15281
|
+
var sha = (text2) => createHash15("sha256").update(text2).digest("hex");
|
|
15058
15282
|
var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
15059
15283
|
var VERBOSITY = new Set(["low", "medium", "high"]);
|
|
15060
|
-
function
|
|
15284
|
+
function need4(value) {
|
|
15061
15285
|
if (!value)
|
|
15062
15286
|
throw new Error("Invalid Codex settings witness");
|
|
15063
15287
|
}
|
|
@@ -15072,14 +15296,14 @@ function numericTokens(text2) {
|
|
|
15072
15296
|
continue;
|
|
15073
15297
|
}
|
|
15074
15298
|
if (char === '"' || char === "'") {
|
|
15075
|
-
const
|
|
15299
|
+
const quote2 = char, triple = text2.slice(at, at + 3) === quote2.repeat(3);
|
|
15076
15300
|
at += triple ? 3 : 1;
|
|
15077
15301
|
while (at < text2.length) {
|
|
15078
|
-
if (
|
|
15302
|
+
if (quote2 === '"' && text2[at] === "\\") {
|
|
15079
15303
|
at += 2;
|
|
15080
15304
|
continue;
|
|
15081
15305
|
}
|
|
15082
|
-
if (text2.slice(at, at + (triple ? 3 : 1)) ===
|
|
15306
|
+
if (text2.slice(at, at + (triple ? 3 : 1)) === quote2.repeat(triple ? 3 : 1)) {
|
|
15083
15307
|
at += triple ? 3 : 1;
|
|
15084
15308
|
break;
|
|
15085
15309
|
}
|
|
@@ -15100,8 +15324,8 @@ function numericTokens(text2) {
|
|
|
15100
15324
|
}
|
|
15101
15325
|
return result;
|
|
15102
15326
|
}
|
|
15103
|
-
function
|
|
15104
|
-
|
|
15327
|
+
function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
15328
|
+
need4(depth <= 32 && --budget2.remaining >= 0);
|
|
15105
15329
|
if (value === null)
|
|
15106
15330
|
return ["null"];
|
|
15107
15331
|
if (typeof value === "string")
|
|
@@ -15109,20 +15333,20 @@ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
|
15109
15333
|
if (typeof value === "boolean")
|
|
15110
15334
|
return ["boolean", value];
|
|
15111
15335
|
if (typeof value === "number") {
|
|
15112
|
-
|
|
15336
|
+
need4(Number.isFinite(value));
|
|
15113
15337
|
return ["number", String(value)];
|
|
15114
15338
|
}
|
|
15115
15339
|
if (value instanceof Date) {
|
|
15116
|
-
|
|
15340
|
+
need4(Number.isFinite(value.getTime()));
|
|
15117
15341
|
return ["date", value.toISOString()];
|
|
15118
15342
|
}
|
|
15119
15343
|
if (Array.isArray(value))
|
|
15120
|
-
return ["array", value.map((
|
|
15121
|
-
|
|
15122
|
-
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)])];
|
|
15123
15347
|
}
|
|
15124
15348
|
function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15125
|
-
|
|
15349
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15126
15350
|
budget2.remaining -= Buffer.byteLength(text2);
|
|
15127
15351
|
let config;
|
|
15128
15352
|
try {
|
|
@@ -15130,17 +15354,17 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
|
|
|
15130
15354
|
} catch {
|
|
15131
15355
|
throw new Error("Invalid Codex settings witness");
|
|
15132
15356
|
}
|
|
15133
|
-
|
|
15357
|
+
need4(config && typeof config === "object" && !Array.isArray(config));
|
|
15134
15358
|
if (Object.hasOwn(config, "model")) {
|
|
15135
|
-
|
|
15359
|
+
need4(typeof config.model === "string" && /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(config.model));
|
|
15136
15360
|
delete config.model;
|
|
15137
15361
|
}
|
|
15138
15362
|
for (const [key, values] of [["model_reasoning_effort", EFFORT], ["model_verbosity", VERBOSITY]])
|
|
15139
15363
|
if (Object.hasOwn(config, key)) {
|
|
15140
|
-
|
|
15364
|
+
need4(typeof config[key] === "string" && values.has(config[key]));
|
|
15141
15365
|
delete config[key];
|
|
15142
15366
|
}
|
|
15143
|
-
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)])}`);
|
|
15144
15368
|
}
|
|
15145
15369
|
function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15146
15370
|
const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
|
|
@@ -15211,15 +15435,15 @@ function projectRegularCodexMcp(config, source) {
|
|
|
15211
15435
|
}
|
|
15212
15436
|
|
|
15213
15437
|
// src/lib/codex-settings-witness.ts
|
|
15214
|
-
import { createHash as
|
|
15438
|
+
import { createHash as createHash16 } from "crypto";
|
|
15215
15439
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
15216
|
-
var sha2 = (text2) =>
|
|
15440
|
+
var sha2 = (text2) => createHash16("sha256").update(text2).digest("hex");
|
|
15217
15441
|
var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
15218
15442
|
var VERBOSITY2 = new Set(["low", "medium", "high"]);
|
|
15219
15443
|
var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
15220
15444
|
var CODEX_DISCOVERY_PROJECTION_FIELDS = ["plugins", "marketplaces", "skills"];
|
|
15221
15445
|
var INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
|
|
15222
|
-
function
|
|
15446
|
+
function need5(value) {
|
|
15223
15447
|
if (!value)
|
|
15224
15448
|
throw new Error("Invalid Codex settings witness");
|
|
15225
15449
|
}
|
|
@@ -15240,14 +15464,14 @@ function numericTokens2(text2) {
|
|
|
15240
15464
|
continue;
|
|
15241
15465
|
}
|
|
15242
15466
|
if (char === '"' || char === "'") {
|
|
15243
|
-
const
|
|
15467
|
+
const quote2 = char, triple = text2.slice(at, at + 3) === quote2.repeat(3);
|
|
15244
15468
|
at += triple ? 3 : 1;
|
|
15245
15469
|
while (at < text2.length) {
|
|
15246
|
-
if (
|
|
15470
|
+
if (quote2 === '"' && text2[at] === "\\") {
|
|
15247
15471
|
at += 2;
|
|
15248
15472
|
continue;
|
|
15249
15473
|
}
|
|
15250
|
-
if (text2.slice(at, at + (triple ? 3 : 1)) ===
|
|
15474
|
+
if (text2.slice(at, at + (triple ? 3 : 1)) === quote2.repeat(triple ? 3 : 1)) {
|
|
15251
15475
|
at += triple ? 3 : 1;
|
|
15252
15476
|
break;
|
|
15253
15477
|
}
|
|
@@ -15268,8 +15492,8 @@ function numericTokens2(text2) {
|
|
|
15268
15492
|
}
|
|
15269
15493
|
return result.sort();
|
|
15270
15494
|
}
|
|
15271
|
-
function
|
|
15272
|
-
|
|
15495
|
+
function canonical6(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
15496
|
+
need5(depth <= 32 && --budget2.remaining >= 0);
|
|
15273
15497
|
if (value === null)
|
|
15274
15498
|
return ["null"];
|
|
15275
15499
|
if (typeof value === "string")
|
|
@@ -15277,17 +15501,17 @@ function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
|
15277
15501
|
if (typeof value === "boolean")
|
|
15278
15502
|
return ["boolean", value];
|
|
15279
15503
|
if (typeof value === "number") {
|
|
15280
|
-
|
|
15504
|
+
need5(Number.isFinite(value));
|
|
15281
15505
|
return ["number", String(value)];
|
|
15282
15506
|
}
|
|
15283
15507
|
if (isDate(value)) {
|
|
15284
|
-
|
|
15508
|
+
need5(Number.isFinite(value.getTime()));
|
|
15285
15509
|
return ["date", value.toISOString()];
|
|
15286
15510
|
}
|
|
15287
15511
|
if (Array.isArray(value))
|
|
15288
|
-
return ["array", value.map((
|
|
15289
|
-
|
|
15290
|
-
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)])];
|
|
15291
15515
|
}
|
|
15292
15516
|
function normalize3(config, version = 2) {
|
|
15293
15517
|
if (isRecord3(config.hooks)) {
|
|
@@ -15329,15 +15553,15 @@ function inference(target, version) {
|
|
|
15329
15553
|
if (Object.hasOwn(target, key)) {
|
|
15330
15554
|
const value = target[key];
|
|
15331
15555
|
if (key === "model")
|
|
15332
|
-
|
|
15556
|
+
need5(typeof value === "string" && MODEL.test(value));
|
|
15333
15557
|
else if (key === "personality")
|
|
15334
|
-
|
|
15558
|
+
need5(typeof value === "string" && ["none", "friendly", "pragmatic"].includes(value));
|
|
15335
15559
|
else if (version >= 3 && key !== "model_verbosity") {
|
|
15336
|
-
|
|
15560
|
+
need5(typeof value === "string" && value.length <= 128 && !/[\x00-\x1f\x7f]/.test(value));
|
|
15337
15561
|
if (key !== "service_tier")
|
|
15338
|
-
|
|
15562
|
+
need5(value.length > 0);
|
|
15339
15563
|
} else
|
|
15340
|
-
|
|
15564
|
+
need5(typeof value === "string" && (key === "model_reasoning_effort" ? EFFORT2.has(value) : VERBOSITY2.has(value)));
|
|
15341
15565
|
delete target[key];
|
|
15342
15566
|
}
|
|
15343
15567
|
}
|
|
@@ -15370,14 +15594,14 @@ function projectModelAvailabilityNux(config, text2) {
|
|
|
15370
15594
|
continue;
|
|
15371
15595
|
}
|
|
15372
15596
|
if (char === '"' || char === "'") {
|
|
15373
|
-
const
|
|
15597
|
+
const quote2 = char;
|
|
15374
15598
|
at++;
|
|
15375
15599
|
while (at < span.length) {
|
|
15376
|
-
if (
|
|
15600
|
+
if (quote2 === '"' && span[at] === "\\") {
|
|
15377
15601
|
at += 2;
|
|
15378
15602
|
continue;
|
|
15379
15603
|
}
|
|
15380
|
-
if (span[at++] ===
|
|
15604
|
+
if (span[at++] === quote2)
|
|
15381
15605
|
break;
|
|
15382
15606
|
}
|
|
15383
15607
|
continue;
|
|
@@ -15457,14 +15681,14 @@ function settingsDigest2(text2, version = 2) {
|
|
|
15457
15681
|
} catch {
|
|
15458
15682
|
throw new Error("Invalid Codex settings witness");
|
|
15459
15683
|
}
|
|
15460
|
-
|
|
15684
|
+
need5(isRecord3(config));
|
|
15461
15685
|
const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
|
|
15462
15686
|
const projected = version === 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
|
|
15463
15687
|
normalize3(projected.config, version);
|
|
15464
|
-
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)])}`);
|
|
15465
15689
|
}
|
|
15466
15690
|
function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15467
|
-
|
|
15691
|
+
need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15468
15692
|
budget2.remaining -= Buffer.byteLength(text2);
|
|
15469
15693
|
return settingsDigest2(text2);
|
|
15470
15694
|
}
|
|
@@ -15476,17 +15700,17 @@ function readCodexSettingsPreimage(path, budget2 = { remaining: 2 * 1024 * 1024
|
|
|
15476
15700
|
return readNativeSettingsWitnessFile(path, budget2, "config.toml");
|
|
15477
15701
|
}
|
|
15478
15702
|
function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
|
|
15479
|
-
|
|
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));
|
|
15480
15704
|
const budget2 = { remaining: 2 * 1024 * 1024 };
|
|
15481
15705
|
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
|
|
15482
|
-
|
|
15706
|
+
need5((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
|
|
15483
15707
|
const sha2562 = settingsDigest2(current);
|
|
15484
15708
|
if (settingsDigest2(before) !== sha2562)
|
|
15485
15709
|
throw new Error("Codex non-preference settings changed; explicit discovery review required");
|
|
15486
15710
|
return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
|
|
15487
15711
|
}
|
|
15488
15712
|
function hashCodexSettingsReplacementV3(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15489
|
-
|
|
15713
|
+
need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15490
15714
|
budget2.remaining -= Buffer.byteLength(text2);
|
|
15491
15715
|
return settingsDigest2(text2, 3);
|
|
15492
15716
|
}
|
|
@@ -15494,18 +15718,18 @@ function captureCodexSettingsV3(path, budget2 = { remaining: 256 * 1024 * 1024 }
|
|
|
15494
15718
|
return { path, hashMode: "codex-settings-v3", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 3) };
|
|
15495
15719
|
}
|
|
15496
15720
|
function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
|
|
15497
|
-
|
|
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));
|
|
15498
15722
|
const budget2 = { remaining: 2 * 1024 * 1024 };
|
|
15499
15723
|
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
|
|
15500
15724
|
const original = previous.hashMode === "codex-settings-v2" ? settingsDigest2(before) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
|
|
15501
|
-
|
|
15725
|
+
need5(original === previous.sha256);
|
|
15502
15726
|
const sha2562 = settingsDigest2(current, 3);
|
|
15503
15727
|
if (settingsDigest2(before, 3) !== sha2562)
|
|
15504
15728
|
throw new Error("Codex settings outside the reviewed V3 contract changed; explicit discovery review required");
|
|
15505
15729
|
return { path: previous.path, hashMode: "codex-settings-v3", sha256: sha2562 };
|
|
15506
15730
|
}
|
|
15507
15731
|
function hashCodexSettingsReplacementV4(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15508
|
-
|
|
15732
|
+
need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15509
15733
|
budget2.remaining -= Buffer.byteLength(text2);
|
|
15510
15734
|
return settingsDigest2(text2, 4);
|
|
15511
15735
|
}
|
|
@@ -15513,11 +15737,11 @@ function captureCodexSettingsV4(path, budget2 = { remaining: 256 * 1024 * 1024 }
|
|
|
15513
15737
|
return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 4) };
|
|
15514
15738
|
}
|
|
15515
15739
|
function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
15516
|
-
|
|
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));
|
|
15517
15741
|
const budget2 = { remaining: 2 * 1024 * 1024 };
|
|
15518
15742
|
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
|
|
15519
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);
|
|
15520
|
-
|
|
15744
|
+
need5(original === previous.sha256);
|
|
15521
15745
|
const sha2562 = settingsDigest2(current, 4);
|
|
15522
15746
|
if (settingsDigest2(before, 4) !== sha2562)
|
|
15523
15747
|
throw new Error("Codex settings outside the reviewed V4 contract changed; explicit discovery review required");
|
|
@@ -15525,17 +15749,17 @@ function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
|
15525
15749
|
}
|
|
15526
15750
|
|
|
15527
15751
|
// src/lib/agent-integration.ts
|
|
15528
|
-
import { createHash as
|
|
15752
|
+
import { createHash as createHash20, randomUUID as randomUUID3 } from "crypto";
|
|
15529
15753
|
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
15530
|
-
import { existsSync as existsSync9, lstatSync as lstatSync20, statSync as statSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync15, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as
|
|
15531
|
-
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";
|
|
15532
15756
|
import { homedir as homedir2 } from "os";
|
|
15533
15757
|
|
|
15534
15758
|
// src/lib/managed-policy.ts
|
|
15535
15759
|
import { closeSync as closeSync8, constants as constants8, fstatSync as fstatSync8, lstatSync as lstatSync15, openSync as openSync8, readSync as readSync6 } from "fs";
|
|
15536
15760
|
import { join as join19 } from "path";
|
|
15537
|
-
function refusal(
|
|
15538
|
-
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.`);
|
|
15539
15763
|
}
|
|
15540
15764
|
function parseManagedSkillPolicy(text2) {
|
|
15541
15765
|
if (Buffer.byteLength(text2) > AGENT_POLICY_LIMITS.bytes)
|
|
@@ -15612,7 +15836,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
|
|
|
15612
15836
|
}
|
|
15613
15837
|
|
|
15614
15838
|
// src/lib/agent-bridge.ts
|
|
15615
|
-
import { createHash as
|
|
15839
|
+
import { createHash as createHash17 } from "crypto";
|
|
15616
15840
|
import { lstatSync as lstatSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
|
|
15617
15841
|
import { join as join20 } from "path";
|
|
15618
15842
|
var CLI_BRIDGE_NAME = "skills-cli";
|
|
@@ -15631,7 +15855,7 @@ Use the Skills CLI as the skill authority for this station.
|
|
|
15631
15855
|
|
|
15632
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.
|
|
15633
15857
|
`;
|
|
15634
|
-
var CLI_BRIDGE_DIGEST =
|
|
15858
|
+
var CLI_BRIDGE_DIGEST = createHash17("sha256").update(markdown).digest("hex");
|
|
15635
15859
|
var CLI_BRIDGE_FILES = Object.freeze({
|
|
15636
15860
|
"SKILL.md": markdown,
|
|
15637
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)}
|
|
@@ -15660,29 +15884,29 @@ var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^
|
|
|
15660
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");
|
|
15661
15885
|
function splitTopLevel(value, delimiter2) {
|
|
15662
15886
|
const result = [];
|
|
15663
|
-
let start = 0, square = 0, curly = 0,
|
|
15887
|
+
let start = 0, square = 0, curly = 0, quote2 = null, escaped = false;
|
|
15664
15888
|
for (let index = 0;index < value.length; index++) {
|
|
15665
15889
|
const char = value[index];
|
|
15666
|
-
if (
|
|
15890
|
+
if (quote2 === "double") {
|
|
15667
15891
|
if (escaped)
|
|
15668
15892
|
escaped = false;
|
|
15669
15893
|
else if (char === "\\")
|
|
15670
15894
|
escaped = true;
|
|
15671
15895
|
else if (char === '"')
|
|
15672
|
-
|
|
15896
|
+
quote2 = null;
|
|
15673
15897
|
continue;
|
|
15674
15898
|
}
|
|
15675
|
-
if (
|
|
15899
|
+
if (quote2 === "literal") {
|
|
15676
15900
|
if (char === "'")
|
|
15677
|
-
|
|
15901
|
+
quote2 = null;
|
|
15678
15902
|
continue;
|
|
15679
15903
|
}
|
|
15680
15904
|
if (char === '"') {
|
|
15681
|
-
|
|
15905
|
+
quote2 = "double";
|
|
15682
15906
|
continue;
|
|
15683
15907
|
}
|
|
15684
15908
|
if (char === "'") {
|
|
15685
|
-
|
|
15909
|
+
quote2 = "literal";
|
|
15686
15910
|
continue;
|
|
15687
15911
|
}
|
|
15688
15912
|
if (char === "[")
|
|
@@ -15700,7 +15924,7 @@ function splitTopLevel(value, delimiter2) {
|
|
|
15700
15924
|
start = index + 1;
|
|
15701
15925
|
}
|
|
15702
15926
|
}
|
|
15703
|
-
if (
|
|
15927
|
+
if (quote2 !== null || square !== 0 || curly !== 0)
|
|
15704
15928
|
return null;
|
|
15705
15929
|
result.push(value.slice(start));
|
|
15706
15930
|
return result;
|
|
@@ -15708,29 +15932,29 @@ function splitTopLevel(value, delimiter2) {
|
|
|
15708
15932
|
function inlineArrayEnd(value) {
|
|
15709
15933
|
if (!value.startsWith("["))
|
|
15710
15934
|
return null;
|
|
15711
|
-
let square = 0, curly = 0,
|
|
15935
|
+
let square = 0, curly = 0, quote2 = null, escaped = false;
|
|
15712
15936
|
for (let index = 0;index < value.length; index++) {
|
|
15713
15937
|
const char = value[index];
|
|
15714
|
-
if (
|
|
15938
|
+
if (quote2 === "double") {
|
|
15715
15939
|
if (escaped)
|
|
15716
15940
|
escaped = false;
|
|
15717
15941
|
else if (char === "\\")
|
|
15718
15942
|
escaped = true;
|
|
15719
15943
|
else if (char === '"')
|
|
15720
|
-
|
|
15944
|
+
quote2 = null;
|
|
15721
15945
|
continue;
|
|
15722
15946
|
}
|
|
15723
|
-
if (
|
|
15947
|
+
if (quote2 === "literal") {
|
|
15724
15948
|
if (char === "'")
|
|
15725
|
-
|
|
15949
|
+
quote2 = null;
|
|
15726
15950
|
continue;
|
|
15727
15951
|
}
|
|
15728
15952
|
if (char === '"') {
|
|
15729
|
-
|
|
15953
|
+
quote2 = "double";
|
|
15730
15954
|
continue;
|
|
15731
15955
|
}
|
|
15732
15956
|
if (char === "'") {
|
|
15733
|
-
|
|
15957
|
+
quote2 = "literal";
|
|
15734
15958
|
continue;
|
|
15735
15959
|
}
|
|
15736
15960
|
if (char === "[")
|
|
@@ -15767,7 +15991,7 @@ function inlineRowsAsTables(value, newline) {
|
|
|
15767
15991
|
const fields = splitTopLevel(row.slice(1, -1), ",");
|
|
15768
15992
|
if (!fields)
|
|
15769
15993
|
return null;
|
|
15770
|
-
const lines = fields.map((
|
|
15994
|
+
const lines = fields.map((field2) => field2.trim()).filter(Boolean);
|
|
15771
15995
|
if (lines.some((line) => !/^(?:path|name|enabled)\s*=/.test(line)))
|
|
15772
15996
|
return null;
|
|
15773
15997
|
const keys = lines.map((line) => line.slice(0, line.indexOf("=")).trim());
|
|
@@ -15792,27 +16016,27 @@ function normalizeCodexInlinePathConfig(text2) {
|
|
|
15792
16016
|
const matches = [];
|
|
15793
16017
|
for (const assignment of text2.matchAll(/^([ \t]*config[ \t]*=[ \t]*)(.*?)(\r?)$/gm)) {
|
|
15794
16018
|
const rawValue = assignment[2].trimEnd();
|
|
15795
|
-
let
|
|
16019
|
+
let quote2 = null, escaped = false, comment = "";
|
|
15796
16020
|
for (let index = 0;index < rawValue.length; index++) {
|
|
15797
16021
|
const char = rawValue[index];
|
|
15798
|
-
if (
|
|
16022
|
+
if (quote2 === "double") {
|
|
15799
16023
|
if (escaped)
|
|
15800
16024
|
escaped = false;
|
|
15801
16025
|
else if (char === "\\")
|
|
15802
16026
|
escaped = true;
|
|
15803
16027
|
else if (char === '"')
|
|
15804
|
-
|
|
16028
|
+
quote2 = null;
|
|
15805
16029
|
continue;
|
|
15806
16030
|
}
|
|
15807
|
-
if (
|
|
16031
|
+
if (quote2 === "literal") {
|
|
15808
16032
|
if (char === "'")
|
|
15809
|
-
|
|
16033
|
+
quote2 = null;
|
|
15810
16034
|
continue;
|
|
15811
16035
|
}
|
|
15812
16036
|
if (char === '"')
|
|
15813
|
-
|
|
16037
|
+
quote2 = "double";
|
|
15814
16038
|
else if (char === "'")
|
|
15815
|
-
|
|
16039
|
+
quote2 = "literal";
|
|
15816
16040
|
else if (char === "#") {
|
|
15817
16041
|
comment = rawValue.slice(index);
|
|
15818
16042
|
break;
|
|
@@ -15924,28 +16148,28 @@ function disableCodexBundledSkills(text2) {
|
|
|
15924
16148
|
|
|
15925
16149
|
// src/lib/agent-sumi.ts
|
|
15926
16150
|
import { existsSync as existsSync8, lstatSync as lstatSync17, realpathSync as realpathSync8 } from "fs";
|
|
15927
|
-
import { join as join21, resolve as
|
|
16151
|
+
import { join as join21, resolve as resolve13 } from "path";
|
|
15928
16152
|
function sumiConfigDirectory(home, env = process.env) {
|
|
15929
16153
|
for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
|
|
15930
16154
|
if (env[key] !== undefined)
|
|
15931
16155
|
throw new Error(`Sumi ${key} requires a dedicated discovery adapter; unset it before Skills integration`);
|
|
15932
16156
|
const selected = (key) => env[key]?.trim() ? env[key] : undefined;
|
|
15933
|
-
const expand = (value) =>
|
|
16157
|
+
const expand = (value) => resolve13(value === "~" ? home : value.startsWith("~/") ? join21(home, value.slice(2)) : value);
|
|
15934
16158
|
const explicit = selected("SUMI_CONFIG_DIR");
|
|
15935
16159
|
const xdg = selected("XDG_CONFIG_HOME");
|
|
15936
16160
|
const custom = selected("SUMI_HOME");
|
|
15937
|
-
const
|
|
16161
|
+
const canonical7 = explicit ? expand(explicit) : xdg ? join21(expand(xdg), "sumi") : join21(custom ? expand(custom) : join21(home, ".hasna-internal/sumi"), "config");
|
|
15938
16162
|
if (explicit || xdg || custom)
|
|
15939
|
-
return
|
|
16163
|
+
return canonical7;
|
|
15940
16164
|
const legacy = join21(home, ".config/sumi");
|
|
15941
|
-
const a = lstatSync17(
|
|
15942
|
-
if (a && b && realpathSync8(
|
|
16165
|
+
const a = lstatSync17(canonical7, { throwIfNoEntry: false }), b = lstatSync17(legacy, { throwIfNoEntry: false });
|
|
16166
|
+
if (a && b && realpathSync8(canonical7) !== realpathSync8(legacy))
|
|
15943
16167
|
throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
|
|
15944
|
-
const path = a ?
|
|
16168
|
+
const path = a ? canonical7 : b ? legacy : canonical7;
|
|
15945
16169
|
if (!existsSync8(path))
|
|
15946
16170
|
return path;
|
|
15947
16171
|
const physical = realpathSync8(path);
|
|
15948
|
-
if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![
|
|
16172
|
+
if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical7, legacy].includes(physical))
|
|
15949
16173
|
throw new Error("Sumi config alias must resolve to its canonical or legacy root");
|
|
15950
16174
|
return physical;
|
|
15951
16175
|
}
|
|
@@ -16045,33 +16269,49 @@ var SUMI_SKILL_PERMISSIONS = [
|
|
|
16045
16269
|
];
|
|
16046
16270
|
|
|
16047
16271
|
// src/lib/codex-hook-trust-files.ts
|
|
16048
|
-
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";
|
|
16049
|
-
import { createHash as
|
|
16050
|
-
import { dirname as
|
|
16051
|
-
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) {
|
|
16052
16276
|
if (!value)
|
|
16053
16277
|
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
16054
16278
|
}
|
|
16055
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
|
+
}
|
|
16056
16283
|
function safeParents(file) {
|
|
16057
|
-
for (let p =
|
|
16284
|
+
for (let p = dirname14(file);; p = dirname14(p)) {
|
|
16058
16285
|
const s = lstatSync18(p);
|
|
16059
|
-
|
|
16060
|
-
|
|
16061
|
-
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)
|
|
16062
16289
|
break;
|
|
16063
16290
|
}
|
|
16064
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
|
+
}
|
|
16065
16305
|
function snapshot(file, privateFile = false, options = {}) {
|
|
16066
16306
|
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
16067
16307
|
safeParents(file);
|
|
16068
16308
|
const initial = lstatSync18(file, { bigint: true });
|
|
16069
|
-
|
|
16070
|
-
|
|
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");
|
|
16071
16311
|
const fd = openSync9(file, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
|
|
16072
16312
|
try {
|
|
16073
16313
|
const before = fstatSync9(fd, { bigint: true });
|
|
16074
|
-
|
|
16314
|
+
need6(sameStat(initial, before), "INPUT_CHANGED");
|
|
16075
16315
|
const bytes = Buffer.alloc(Number(before.size) + 1);
|
|
16076
16316
|
let length = 0;
|
|
16077
16317
|
while (length < bytes.length) {
|
|
@@ -16080,22 +16320,22 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
16080
16320
|
break;
|
|
16081
16321
|
length += n2;
|
|
16082
16322
|
}
|
|
16083
|
-
|
|
16323
|
+
need6(length === Number(before.size) && sameStat(before, fstatSync9(fd, { bigint: true })) && sameStat(before, lstatSync18(file, { bigint: true })), "INPUT_CHANGED");
|
|
16084
16324
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
16085
|
-
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") };
|
|
16086
16326
|
} finally {
|
|
16087
16327
|
closeSync9(fd);
|
|
16088
16328
|
}
|
|
16089
16329
|
}
|
|
16090
16330
|
function unchanged2(before) {
|
|
16091
16331
|
const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
16092
|
-
|
|
16332
|
+
need6(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
16093
16333
|
}
|
|
16094
16334
|
|
|
16095
16335
|
// src/cli/commands/runtime-launcher.ts
|
|
16096
|
-
import { createHash as
|
|
16097
|
-
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
|
|
16098
|
-
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";
|
|
16099
16339
|
var PINNED_LAUNCHER_SCHEMA = "hasna-skills-pinned-launcher-v1";
|
|
16100
16340
|
var LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
|
|
16101
16341
|
var PINNED_LAUNCHER_BUN_FLAGS = ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install"];
|
|
@@ -16136,10 +16376,10 @@ var PINNED_LAUNCHER_ENV_PREFIXES = ["HASNA", "SKILLS", "SKILL", "MCP", "XDG", "L
|
|
|
16136
16376
|
var ENV_PREFIX = new RegExp(`^(${PINNED_LAUNCHER_ENV_PREFIXES.join("|")})_`);
|
|
16137
16377
|
var ENV_EXACT = new Set(PINNED_LAUNCHER_ENV_NAMES);
|
|
16138
16378
|
function sha2562(bytes) {
|
|
16139
|
-
return
|
|
16379
|
+
return createHash19("sha256").update(bytes).digest("hex");
|
|
16140
16380
|
}
|
|
16141
16381
|
function assertLauncherPath(value, code) {
|
|
16142
|
-
if (typeof value !== "string" || !
|
|
16382
|
+
if (typeof value !== "string" || !isAbsolute15(value) || /['\x00-\x1f\x7f]/.test(value))
|
|
16143
16383
|
throw new Error(code);
|
|
16144
16384
|
}
|
|
16145
16385
|
function renderPinnedLauncher(binding) {
|
|
@@ -16229,7 +16469,7 @@ function assertPinnedLauncherRuntimeSafe(runtime) {
|
|
|
16229
16469
|
}
|
|
16230
16470
|
|
|
16231
16471
|
// src/lib/agent-integration.ts
|
|
16232
|
-
var sha3 = (data) =>
|
|
16472
|
+
var sha3 = (data) => createHash20("sha256").update(data).digest("hex");
|
|
16233
16473
|
var ROOTS = NATIVE_SKILL_ROOTS;
|
|
16234
16474
|
function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
|
|
16235
16475
|
try {
|
|
@@ -16279,9 +16519,10 @@ function isSystemRootAlias(path) {
|
|
|
16279
16519
|
return false;
|
|
16280
16520
|
const target = `/private${path}`, link = `private${path}`;
|
|
16281
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}`;
|
|
16282
16523
|
try {
|
|
16283
16524
|
const before = lstatSync20(path, { bigint: true });
|
|
16284
|
-
if (!before.isSymbolicLink() || before.uid !== 0n ||
|
|
16525
|
+
if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync7(path) !== link)
|
|
16285
16526
|
return false;
|
|
16286
16527
|
const directories = ["/", "/private", target].map((directory) => {
|
|
16287
16528
|
const stat = lstatSync20(directory, { bigint: true });
|
|
@@ -16294,20 +16535,20 @@ function isSystemRootAlias(path) {
|
|
|
16294
16535
|
throw new Error("Unprotected system temporary directory");
|
|
16295
16536
|
const fd = openSync11(directory, constants11.O_RDONLY | constants11.O_DIRECTORY | constants11.O_NOFOLLOW);
|
|
16296
16537
|
try {
|
|
16297
|
-
if (
|
|
16538
|
+
if (directoryIdentity(fstatSync11(fd, { bigint: true })) !== directoryIdentity(stat) || !hasTrustedDarwinAcl(fd) || directoryIdentity(fstatSync11(fd, { bigint: true })) !== directoryIdentity(stat))
|
|
16298
16539
|
throw new Error("Unverified system alias directory ACL");
|
|
16299
16540
|
} finally {
|
|
16300
16541
|
closeSync11(fd);
|
|
16301
16542
|
}
|
|
16302
|
-
return { directory, identity:
|
|
16543
|
+
return { directory, identity: directoryIdentity(stat) };
|
|
16303
16544
|
});
|
|
16304
|
-
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);
|
|
16305
16546
|
} catch {
|
|
16306
16547
|
return false;
|
|
16307
16548
|
}
|
|
16308
16549
|
}
|
|
16309
16550
|
function canonicalSystemPath(path) {
|
|
16310
|
-
const absolute =
|
|
16551
|
+
const absolute = resolve15(path), root = `/${absolute.split(sep7)[1]}`;
|
|
16311
16552
|
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync20(root, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
16312
16553
|
return absolute;
|
|
16313
16554
|
if (!isSystemRootAlias(root))
|
|
@@ -16315,11 +16556,11 @@ function canonicalSystemPath(path) {
|
|
|
16315
16556
|
return `/private${absolute}`;
|
|
16316
16557
|
}
|
|
16317
16558
|
function assertSafePath(path) {
|
|
16318
|
-
let cursor =
|
|
16559
|
+
let cursor = resolve15(path);
|
|
16319
16560
|
while (true) {
|
|
16320
16561
|
if (lstatSync20(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
|
|
16321
16562
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
16322
|
-
const parent =
|
|
16563
|
+
const parent = dirname15(cursor);
|
|
16323
16564
|
if (parent === cursor)
|
|
16324
16565
|
return;
|
|
16325
16566
|
cursor = parent;
|
|
@@ -16331,13 +16572,13 @@ function readRootAlias(home, agent) {
|
|
|
16331
16572
|
const stat = lstatSync20(alias, { throwIfNoEntry: false, bigint: true });
|
|
16332
16573
|
if (!stat?.isSymbolicLink())
|
|
16333
16574
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
16334
|
-
const link =
|
|
16575
|
+
const link = readlinkSync7(alias), targetPath = resolve15(dirname15(alias), link);
|
|
16335
16576
|
assertSafePath(targetPath);
|
|
16336
16577
|
const targetStat = lstatSync20(targetPath, { throwIfNoEntry: false });
|
|
16337
16578
|
if (!targetStat?.isDirectory())
|
|
16338
16579
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
16339
16580
|
const target = realpathSync10(targetPath), inside = relative6(realHome, target);
|
|
16340
|
-
if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) ||
|
|
16581
|
+
if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) || isAbsolute16(inside))
|
|
16341
16582
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
16342
16583
|
return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
16343
16584
|
}
|
|
@@ -16363,13 +16604,13 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
16363
16604
|
const rows = skills.config;
|
|
16364
16605
|
if (!Array.isArray(rows))
|
|
16365
16606
|
return [];
|
|
16366
|
-
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())))
|
|
16367
16608
|
return [];
|
|
16368
16609
|
if (rows.some((entry) => entry.name !== undefined && entry.enabled !== false))
|
|
16369
16610
|
return [];
|
|
16370
16611
|
const settings = new Map;
|
|
16371
16612
|
for (const entry of rows) {
|
|
16372
|
-
if (typeof entry.path !== "string" || !
|
|
16613
|
+
if (typeof entry.path !== "string" || !isAbsolute16(entry.path))
|
|
16373
16614
|
continue;
|
|
16374
16615
|
let document2;
|
|
16375
16616
|
try {
|
|
@@ -16381,7 +16622,7 @@ function disabledCodexSkillPaths(config, aliases) {
|
|
|
16381
16622
|
continue;
|
|
16382
16623
|
settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
|
|
16383
16624
|
}
|
|
16384
|
-
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));
|
|
16385
16626
|
}
|
|
16386
16627
|
function recheckRootAliases(aliases) {
|
|
16387
16628
|
for (const binding of aliases) {
|
|
@@ -16423,7 +16664,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
16423
16664
|
}
|
|
16424
16665
|
}
|
|
16425
16666
|
function treeHash(root) {
|
|
16426
|
-
const hash2 =
|
|
16667
|
+
const hash2 = createHash20("sha256");
|
|
16427
16668
|
let bytes = 0, files = 0;
|
|
16428
16669
|
function visit2(path) {
|
|
16429
16670
|
const stat = lstatSync20(path);
|
|
@@ -16455,20 +16696,20 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
16455
16696
|
const identities = new Set;
|
|
16456
16697
|
const selected = [];
|
|
16457
16698
|
for (const target of manifest.targets) {
|
|
16458
|
-
if (!
|
|
16699
|
+
if (!isAbsolute16(target.projectRoot) || resolve15(target.projectRoot) !== target.projectRoot)
|
|
16459
16700
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
16460
|
-
const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative6(projectRoot,
|
|
16701
|
+
const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative6(projectRoot, resolve15(projectRoot, target.path));
|
|
16461
16702
|
if (!existsSync9(projectRoot) || !lstatSync20(projectRoot).isDirectory())
|
|
16462
16703
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
16463
16704
|
assertSafePath(projectRoot);
|
|
16464
|
-
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))
|
|
16465
16706
|
throw new Error(`Invalid native migration target: ${target.path}`);
|
|
16466
16707
|
const identity5 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
|
|
16467
16708
|
if (identities.has(identity5))
|
|
16468
16709
|
throw new Error(`Duplicate native migration target: ${target.path}`);
|
|
16469
16710
|
identities.add(identity5);
|
|
16470
|
-
const expected = canonicalSystemPath(
|
|
16471
|
-
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);
|
|
16472
16713
|
if (matches.length !== 1)
|
|
16473
16714
|
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
16474
16715
|
const entry = matches[0];
|
|
@@ -16487,7 +16728,7 @@ function projectAncestorDirectories(projects) {
|
|
|
16487
16728
|
if (depth >= 100)
|
|
16488
16729
|
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
16489
16730
|
directories.add(path);
|
|
16490
|
-
const parent =
|
|
16731
|
+
const parent = dirname15(path);
|
|
16491
16732
|
if (parent === path)
|
|
16492
16733
|
break;
|
|
16493
16734
|
path = parent;
|
|
@@ -16496,7 +16737,7 @@ function projectAncestorDirectories(projects) {
|
|
|
16496
16737
|
return [...directories];
|
|
16497
16738
|
}
|
|
16498
16739
|
function isCodexSkillDiscoveryInput(path) {
|
|
16499
|
-
for (let cursor = canonicalSystemPath(path);cursor !==
|
|
16740
|
+
for (let cursor = canonicalSystemPath(path);cursor !== dirname15(cursor); cursor = dirname15(cursor)) {
|
|
16500
16741
|
if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(join23(sep7, root))))
|
|
16501
16742
|
return true;
|
|
16502
16743
|
}
|
|
@@ -16504,9 +16745,9 @@ function isCodexSkillDiscoveryInput(path) {
|
|
|
16504
16745
|
}
|
|
16505
16746
|
function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
16506
16747
|
home = canonicalSystemPath(home);
|
|
16507
|
-
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !
|
|
16748
|
+
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute16(options.reviewedCacheAlias) || resolve15(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
|
|
16508
16749
|
throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
|
|
16509
|
-
if (options.disabledVendorPaths?.some((path) => !
|
|
16750
|
+
if (options.disabledVendorPaths?.some((path) => !isAbsolute16(path) || resolve15(path) !== path))
|
|
16510
16751
|
throw new Error("Disabled vendor paths must be exact absolute paths");
|
|
16511
16752
|
const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
|
|
16512
16753
|
const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
|
|
@@ -16525,7 +16766,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16525
16766
|
roots.push(["sumi", canonicalAgentPath(join23(project, directory, name), aliases)]);
|
|
16526
16767
|
}
|
|
16527
16768
|
const installationInputs = options.codexInstallationInputRoots ?? [];
|
|
16528
|
-
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)))))
|
|
16529
16770
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
16530
16771
|
const entries = [], seen = new Set;
|
|
16531
16772
|
const cacheScans = new Map, maxAliasProofEntries = 1e4;
|
|
@@ -16539,11 +16780,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16539
16780
|
}
|
|
16540
16781
|
let reviewedCacheAliasFound = false;
|
|
16541
16782
|
function verifiedSiblingCacheAlias(agent, path, parent) {
|
|
16542
|
-
const link =
|
|
16543
|
-
if (
|
|
16783
|
+
const link = readlinkSync7(path), lexicalTarget = resolve15(parent, link);
|
|
16784
|
+
if (isAbsolute16(link) ? link !== lexicalTarget : dirname15(link) !== ".")
|
|
16544
16785
|
return false;
|
|
16545
16786
|
const target = canonicalSystemPath(lexicalTarget);
|
|
16546
|
-
if (
|
|
16787
|
+
if (dirname15(target) !== parent)
|
|
16547
16788
|
return false;
|
|
16548
16789
|
if (!lstatSync20(target, { throwIfNoEntry: false })?.isDirectory())
|
|
16549
16790
|
return false;
|
|
@@ -16592,17 +16833,17 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16592
16833
|
}
|
|
16593
16834
|
const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
|
|
16594
16835
|
const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep7));
|
|
16595
|
-
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 } : {} });
|
|
16596
16837
|
return scan;
|
|
16597
16838
|
}
|
|
16598
16839
|
if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
|
|
16599
16840
|
return { ...scan, complete: false };
|
|
16600
16841
|
const children = [], directory = opendirSync3(path);
|
|
16601
16842
|
try {
|
|
16602
|
-
let
|
|
16603
|
-
while ((
|
|
16604
|
-
admit(join23(path,
|
|
16605
|
-
children.push(
|
|
16843
|
+
let child2;
|
|
16844
|
+
while ((child2 = directory.readSync()) !== null) {
|
|
16845
|
+
admit(join23(path, child2.name));
|
|
16846
|
+
children.push(child2);
|
|
16606
16847
|
}
|
|
16607
16848
|
} finally {
|
|
16608
16849
|
directory.closeSync();
|
|
@@ -16618,15 +16859,15 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
16618
16859
|
const isVendor = vendor || name === ".system";
|
|
16619
16860
|
if (isVendor && !options.includeVendor)
|
|
16620
16861
|
continue;
|
|
16621
|
-
const
|
|
16622
|
-
if (isVendor && lstatSync20(
|
|
16623
|
-
const target = statSync9(
|
|
16624
|
-
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)) {
|
|
16625
16866
|
scan.entries++;
|
|
16626
16867
|
continue;
|
|
16627
16868
|
}
|
|
16628
16869
|
}
|
|
16629
|
-
const childScan = visit2(agent,
|
|
16870
|
+
const childScan = visit2(agent, child2, isVendor, depth + 1, pluginCache, true, configuredRoot);
|
|
16630
16871
|
scan.complete &&= childScan.complete;
|
|
16631
16872
|
scan.hasSkills ||= childScan.hasSkills;
|
|
16632
16873
|
scan.entries += childScan.entries;
|
|
@@ -16762,7 +17003,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
16762
17003
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
16763
17004
|
const bundled = disableCodexBundledSkills(text2);
|
|
16764
17005
|
let result = normalizeCodexInlinePathConfig(bundled);
|
|
16765
|
-
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));
|
|
16766
17007
|
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
16767
17008
|
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
16768
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");
|
|
@@ -16774,7 +17015,7 @@ function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
|
16774
17015
|
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
16775
17016
|
const parsed = Bun.TOML.parse(section);
|
|
16776
17017
|
const declared = parsed.skills?.config?.[0];
|
|
16777
|
-
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))))
|
|
16778
17019
|
return section;
|
|
16779
17020
|
found = true;
|
|
16780
17021
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -16795,7 +17036,7 @@ enabled = false
|
|
|
16795
17036
|
return result;
|
|
16796
17037
|
}
|
|
16797
17038
|
function assertCodexInstallationInputs(home, projectDirs, inputs) {
|
|
16798
|
-
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)));
|
|
16799
17040
|
if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep7) || input.startsWith(root + sep7))))
|
|
16800
17041
|
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
16801
17042
|
}
|
|
@@ -16811,9 +17052,9 @@ function planAgentIntegration(options) {
|
|
|
16811
17052
|
throw new Error("Invalid existing Skills bridge policy");
|
|
16812
17053
|
if (policy.bridge?.agents !== undefined && (!Array.isArray(policy.bridge.agents) || policy.bridge.agents.some((agent) => !INTEGRATION_AGENTS.includes(agent))))
|
|
16813
17054
|
throw new Error("Invalid existing bridge agent inventory");
|
|
16814
|
-
for (const
|
|
16815
|
-
if (policy.bridge?.[
|
|
16816
|
-
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`);
|
|
16817
17058
|
const priorAgents = policy.bridge?.agents ?? [];
|
|
16818
17059
|
const profileId = options.profileId ?? policy.profileId ?? "default";
|
|
16819
17060
|
const validateProfile = (value) => {
|
|
@@ -16828,13 +17069,18 @@ function planAgentIntegration(options) {
|
|
|
16828
17069
|
return [agent, { command, profileId: profileId2 }];
|
|
16829
17070
|
}));
|
|
16830
17071
|
const retainedAgents = [];
|
|
17072
|
+
const priorExecutables = policy.bridge?.discoveryExecutables ?? {};
|
|
17073
|
+
const executables = new Map;
|
|
16831
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);
|
|
16832
17078
|
const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? { ...policy.bridge.discovery[agent], ...agent === "codex" ? { codexDisabledPluginSkills: policy.bridge.codexPluginSkills ?? [] } : {} } : undefined;
|
|
16833
17079
|
if (retainedReview) {
|
|
16834
17080
|
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
|
|
16835
17081
|
retainedAgents.push(agent);
|
|
16836
17082
|
}
|
|
16837
|
-
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 });
|
|
16838
17084
|
});
|
|
16839
17085
|
const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases)) : null;
|
|
16840
17086
|
const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
|
|
@@ -16842,7 +17088,7 @@ function planAgentIntegration(options) {
|
|
|
16842
17088
|
const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join23(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
|
|
16843
17089
|
const plannedCodexRules = codexPathConfig === undefined ? undefined : Bun.TOML.parse(codexPathConfig).skills?.config ?? [];
|
|
16844
17090
|
const observedNativeSources = [];
|
|
16845
|
-
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) => {
|
|
16846
17092
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
16847
17093
|
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
16848
17094
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
@@ -16963,7 +17209,7 @@ function planAgentIntegration(options) {
|
|
|
16963
17209
|
const { codexInstallationInputs: previousInputs, ...current } = rebound;
|
|
16964
17210
|
const inputs = codexPluginSourceInputs.map((input) => input.sourceRoot);
|
|
16965
17211
|
const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root) => root.startsWith(directory.path + sep7))).map((directory) => directory.path), inputs) : previousInputs.directories;
|
|
16966
|
-
const installed = codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version:
|
|
17212
|
+
const installed = codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: codexPluginSkillReview.version, catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
|
|
16967
17213
|
return captureRetiredCodexDiscovery(installed, codexPluginSkills, Bun.TOML.parse(changes.find((change) => change.path === canonicalAgentPath(join23(home, ".codex/config.toml"), aliases))?.after ?? codexConfigBefore ?? "").skills?.config ?? []);
|
|
16968
17214
|
});
|
|
16969
17215
|
const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
|
|
@@ -16971,7 +17217,7 @@ function planAgentIntegration(options) {
|
|
|
16971
17217
|
version: CLI_BRIDGE_VERSION,
|
|
16972
17218
|
digest: CLI_BRIDGE_DIGEST,
|
|
16973
17219
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
16974
|
-
home:
|
|
17220
|
+
home: resolve15(home),
|
|
16975
17221
|
includeVendor: true,
|
|
16976
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)) } } } : {},
|
|
16977
17223
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
@@ -16981,19 +17227,30 @@ function planAgentIntegration(options) {
|
|
|
16981
17227
|
...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
|
|
16982
17228
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
16983
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;
|
|
16984
17241
|
const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
|
|
16985
17242
|
if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
|
|
16986
17243
|
changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
|
|
16987
17244
|
recheckRootAliases(aliases);
|
|
16988
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 } } : {} };
|
|
16989
17246
|
}
|
|
16990
|
-
function hookProjectionRefusal(
|
|
16991
|
-
throw new Error(`CLAUDE_MANAGED_HOOK_${
|
|
17247
|
+
function hookProjectionRefusal(reason2) {
|
|
17248
|
+
throw new Error(`CLAUDE_MANAGED_HOOK_${reason2}`);
|
|
16992
17249
|
}
|
|
16993
17250
|
function projectedHookAcl(path, observed) {
|
|
16994
17251
|
if (process.platform !== "darwin")
|
|
16995
17252
|
return;
|
|
16996
|
-
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}`;
|
|
16997
17254
|
const fd = openSync11(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK | (observed.isDirectory() ? constants11.O_DIRECTORY : 0));
|
|
16998
17255
|
try {
|
|
16999
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))
|
|
@@ -17004,18 +17261,18 @@ function projectedHookAcl(path, observed) {
|
|
|
17004
17261
|
}
|
|
17005
17262
|
function projectedHookExecutable(command, expectedResolved) {
|
|
17006
17263
|
try {
|
|
17007
|
-
if (!
|
|
17264
|
+
if (!isAbsolute16(command) || /[\0\r\n]/.test(command) || resolve15(command) !== command)
|
|
17008
17265
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17009
17266
|
command = canonicalSystemPath(command);
|
|
17010
17267
|
const parents2 = (file = command) => {
|
|
17011
17268
|
const result = [];
|
|
17012
|
-
for (let path =
|
|
17269
|
+
for (let path = dirname15(file);; path = dirname15(path)) {
|
|
17013
17270
|
const stat = lstatSync20(path, { bigint: true });
|
|
17014
|
-
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))
|
|
17015
17272
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17016
17273
|
projectedHookAcl(path, stat);
|
|
17017
17274
|
result.push(`${path}:${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}`);
|
|
17018
|
-
if (
|
|
17275
|
+
if (dirname15(path) === path)
|
|
17019
17276
|
break;
|
|
17020
17277
|
}
|
|
17021
17278
|
return result;
|
|
@@ -17025,22 +17282,33 @@ function projectedHookExecutable(command, expectedResolved) {
|
|
|
17025
17282
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17026
17283
|
const linkIdentity = (stat) => `${stat.dev}:${stat.ino}:${stat.mode}:${stat.uid}:${stat.ctimeNs}`;
|
|
17027
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");
|
|
17028
17288
|
if (expectedResolved !== undefined && resolved !== expectedResolved)
|
|
17029
17289
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17030
17290
|
let launcher, launcherParentIdentity;
|
|
17291
|
+
let runtime;
|
|
17031
17292
|
if (target.pinned) {
|
|
17032
17293
|
launcherParentIdentity = parents2(target.physical);
|
|
17033
17294
|
launcher = snapshot(target.physical, false);
|
|
17034
17295
|
projectedHookAcl(launcher.file, launcher.stat);
|
|
17035
17296
|
if (launcher.sha256 !== target.pinned.sha256 || (launcher.stat.mode & 0o100n) === 0n)
|
|
17036
17297
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17037
|
-
|
|
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);
|
|
17038
17306
|
}
|
|
17039
17307
|
const resolvedParentIdentity = parents2(resolved);
|
|
17040
|
-
const manifest = snapshot(join23(
|
|
17308
|
+
const manifest = snapshot(join23(dirname15(dirname15(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
17041
17309
|
projectedHookAcl(manifest.file, manifest.stat);
|
|
17042
17310
|
const pkg = JSON.parse(manifest.text);
|
|
17043
|
-
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"))
|
|
17044
17312
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17045
17313
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
17046
17314
|
projectedHookAcl(cli.file, cli.stat);
|
|
@@ -17049,7 +17317,7 @@ function projectedHookExecutable(command, expectedResolved) {
|
|
|
17049
17317
|
const recheck = () => {
|
|
17050
17318
|
try {
|
|
17051
17319
|
const again = resolveLauncherCommand(command);
|
|
17052
|
-
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))
|
|
17053
17321
|
hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
|
|
17054
17322
|
if (launcher) {
|
|
17055
17323
|
if (!isDeepStrictEqual6(parents2(target.physical), launcherParentIdentity))
|
|
@@ -17057,6 +17325,12 @@ function projectedHookExecutable(command, expectedResolved) {
|
|
|
17057
17325
|
projectedHookAcl(launcher.file, launcher.stat);
|
|
17058
17326
|
unchanged2(launcher);
|
|
17059
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
|
+
}
|
|
17060
17334
|
projectedHookAcl(cli.file, cli.stat);
|
|
17061
17335
|
projectedHookAcl(manifest.file, manifest.stat);
|
|
17062
17336
|
unchanged2(cli);
|
|
@@ -17083,7 +17357,7 @@ function planClaudeManagedHookProjection(options) {
|
|
|
17083
17357
|
} catch {
|
|
17084
17358
|
hookProjectionRefusal("TARGET_INVALID");
|
|
17085
17359
|
}
|
|
17086
|
-
const home =
|
|
17360
|
+
const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17087
17361
|
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
17088
17362
|
let policyBytes, settingsBytes, settingsPath, command, profile;
|
|
17089
17363
|
try {
|
|
@@ -17175,29 +17449,28 @@ function planClaudePreToolUseHookUpdate(options) {
|
|
|
17175
17449
|
return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
|
|
17176
17450
|
}
|
|
17177
17451
|
function claudeSettingsConsumers(home, path, discoveries, aliases) {
|
|
17178
|
-
const
|
|
17452
|
+
const canonical7 = (value) => canonicalAgentPath(value, aliases);
|
|
17179
17453
|
const containsSettings = (root) => {
|
|
17180
|
-
const distance = relative6(
|
|
17181
|
-
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !
|
|
17454
|
+
const distance = relative6(canonical7(root), path);
|
|
17455
|
+
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !isAbsolute16(distance);
|
|
17182
17456
|
};
|
|
17183
|
-
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);
|
|
17184
17458
|
}
|
|
17185
|
-
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
17459
|
+
function verifyCoordinatedDiscovery(binding, home, aliases, executable2) {
|
|
17186
17460
|
try {
|
|
17187
17461
|
if (binding.method !== "automatic" && binding.method !== "reviewed")
|
|
17188
17462
|
throw new Error("Invalid managed discovery method");
|
|
17189
17463
|
verifyAgentDiscovery(binding);
|
|
17190
|
-
if (binding.method === "automatic")
|
|
17191
|
-
|
|
17192
|
-
if (JSON.stringify(current) !== JSON.stringify(binding))
|
|
17193
|
-
throw new Error("Configured native discovery roots changed during Claude update");
|
|
17194
|
-
}
|
|
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 });
|
|
17195
17466
|
} catch (error) {
|
|
17467
|
+
if (isDiscoveryRootUnresolved(error))
|
|
17468
|
+
throw error;
|
|
17196
17469
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
17197
17470
|
}
|
|
17198
17471
|
}
|
|
17199
17472
|
function planClaudeHookEventsUpdate(options) {
|
|
17200
|
-
const home =
|
|
17473
|
+
const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17201
17474
|
const policyPath = join23(dataDir2, "agent-policy.json");
|
|
17202
17475
|
assertSafePath(policyPath);
|
|
17203
17476
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
@@ -17223,8 +17496,9 @@ function planClaudeHookEventsUpdate(options) {
|
|
|
17223
17496
|
});
|
|
17224
17497
|
if (discoveries.length !== agents.length || !agents.includes("claude"))
|
|
17225
17498
|
throw new Error("Missing managed Claude discovery coverage");
|
|
17499
|
+
const discoveryExecutables = bridge.discoveryExecutables;
|
|
17226
17500
|
for (const binding of discoveries)
|
|
17227
|
-
verifyCoordinatedDiscovery(binding, home, aliases);
|
|
17501
|
+
verifyCoordinatedDiscovery(binding, home, aliases, discoveryExecutables?.[binding.agent]);
|
|
17228
17502
|
const consumers = claudeSettingsConsumers(home, path, discoveries, aliases);
|
|
17229
17503
|
for (const agent of consumers)
|
|
17230
17504
|
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: home });
|
|
@@ -17259,13 +17533,14 @@ function planClaudeHookEventsUpdate(options) {
|
|
|
17259
17533
|
observedPolicy: { path: policyPath, before: snapshot2.text },
|
|
17260
17534
|
discoveryBefore: discoveries,
|
|
17261
17535
|
discoveryAfter,
|
|
17536
|
+
...discoveryExecutables ? { discoveryExecutables } : {},
|
|
17262
17537
|
rootAliases: aliases,
|
|
17263
17538
|
managedAgentChecks: { home, agents: consumers }
|
|
17264
17539
|
};
|
|
17265
17540
|
}
|
|
17266
17541
|
function atomicWrite2(path, content) {
|
|
17267
17542
|
assertSafePath(path);
|
|
17268
|
-
mkdirSync6(
|
|
17543
|
+
mkdirSync6(dirname15(path), { recursive: true, mode: 448 });
|
|
17269
17544
|
const temporary = `${path}.skills-${randomUUID3()}`;
|
|
17270
17545
|
try {
|
|
17271
17546
|
writeFileSync6(temporary, content, { mode: 384, flag: "wx" });
|
|
@@ -17280,7 +17555,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
17280
17555
|
throw new Error("Invalid Codex witness target version");
|
|
17281
17556
|
if (options.targetClaudeVersion !== undefined && (options.agent !== "claude" || ![3, 4].includes(options.targetClaudeVersion)))
|
|
17282
17557
|
throw new Error("Invalid Claude witness target version");
|
|
17283
|
-
const home =
|
|
17558
|
+
const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17284
17559
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
17285
17560
|
if (!snapshot2 || sha3(snapshot2.text) !== options.expectedPolicySha256)
|
|
17286
17561
|
throw new Error("Managed policy preimage changed");
|
|
@@ -17340,7 +17615,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
17340
17615
|
const replacement = { ...binding, sources: rebound };
|
|
17341
17616
|
verifyAgentDiscovery(replacement);
|
|
17342
17617
|
const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
17343
|
-
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 })) } };
|
|
17344
17619
|
}
|
|
17345
17620
|
function applyAgentIntegration(plan, options = {}) {
|
|
17346
17621
|
const roots = plan.changes.flatMap((change) => {
|
|
@@ -17358,10 +17633,10 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17358
17633
|
throw new Error("Native settings changed after witness planning");
|
|
17359
17634
|
const policyPath = join23(plan.dataDir, "agent-policy.json");
|
|
17360
17635
|
assertSafePath(policyPath);
|
|
17361
|
-
const currentText = (path) =>
|
|
17636
|
+
const currentText = (path) => resolve15(path) === resolve15(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
|
|
17362
17637
|
readManagedSkillPolicySnapshot(plan.dataDir);
|
|
17363
17638
|
for (const change of plan.changes)
|
|
17364
|
-
if (
|
|
17639
|
+
if (resolve15(change.path) === resolve15(policyPath))
|
|
17365
17640
|
parseManagedSkillPolicy(change.after);
|
|
17366
17641
|
const aliases = plan.rootAliases ?? [];
|
|
17367
17642
|
assertCurrent();
|
|
@@ -17382,7 +17657,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17382
17657
|
assertManagedAgentBridgeWithDiscovery(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home }, provenDiscovery?.agent === agent ? provenDiscovery : undefined);
|
|
17383
17658
|
for (const binding of plan.discoveryBefore ?? []) {
|
|
17384
17659
|
if (plan.managedAgentChecks)
|
|
17385
|
-
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
|
|
17660
|
+
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases, plan.discoveryExecutables?.[binding.agent]);
|
|
17386
17661
|
else
|
|
17387
17662
|
verifyAgentDiscovery(binding);
|
|
17388
17663
|
}
|
|
@@ -17414,7 +17689,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17414
17689
|
recheckRootAliases(aliases);
|
|
17415
17690
|
if (currentText(change.path) !== change.before)
|
|
17416
17691
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
17417
|
-
for (let directory =
|
|
17692
|
+
for (let directory = dirname15(change.path);!existsSync9(directory); directory = dirname15(directory))
|
|
17418
17693
|
createdDirectories.add(directory);
|
|
17419
17694
|
const after = change.after;
|
|
17420
17695
|
atomicWrite2(change.path, after);
|
|
@@ -17429,7 +17704,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
|
|
|
17429
17704
|
throw new Error("Native settings changed during witness application");
|
|
17430
17705
|
for (const binding of plan.discoveryAfter ?? []) {
|
|
17431
17706
|
if (plan.managedAgentChecks)
|
|
17432
|
-
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
|
|
17707
|
+
verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases, plan.discoveryExecutables?.[binding.agent]);
|
|
17433
17708
|
else
|
|
17434
17709
|
verifyAgentDiscovery(binding);
|
|
17435
17710
|
}
|
|
@@ -17486,7 +17761,7 @@ function writeArchiveJournal(path, value) {
|
|
|
17486
17761
|
closeSync11(descriptor);
|
|
17487
17762
|
descriptor = undefined;
|
|
17488
17763
|
renameSync3(temporary, path);
|
|
17489
|
-
syncArchiveDirectory(
|
|
17764
|
+
syncArchiveDirectory(dirname15(path));
|
|
17490
17765
|
} finally {
|
|
17491
17766
|
if (descriptor !== undefined)
|
|
17492
17767
|
closeSync11(descriptor);
|
|
@@ -17544,14 +17819,14 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17544
17819
|
};
|
|
17545
17820
|
assertSafePath(archiveRoot);
|
|
17546
17821
|
const created = [];
|
|
17547
|
-
for (let path = archiveRoot;!existsSync9(path); path =
|
|
17822
|
+
for (let path = archiveRoot;!existsSync9(path); path = dirname15(path))
|
|
17548
17823
|
created.push(path);
|
|
17549
17824
|
mkdirSync6(archiveRoot, { recursive: true, mode: 448 });
|
|
17550
17825
|
try {
|
|
17551
17826
|
for (const directory of created)
|
|
17552
17827
|
syncArchiveDirectory(directory);
|
|
17553
17828
|
if (created.length)
|
|
17554
|
-
syncArchiveDirectory(
|
|
17829
|
+
syncArchiveDirectory(dirname15(created.at(-1)));
|
|
17555
17830
|
writeArchiveJournal(receiptPath, journal);
|
|
17556
17831
|
for (const [index, entry] of selected.entries()) {
|
|
17557
17832
|
assertCurrent();
|
|
@@ -17564,7 +17839,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17564
17839
|
writeArchiveJournal(receiptPath, journal);
|
|
17565
17840
|
renameSync3(move.source, move.archive);
|
|
17566
17841
|
moved.push(move);
|
|
17567
|
-
syncArchiveDirectory(
|
|
17842
|
+
syncArchiveDirectory(dirname15(move.source));
|
|
17568
17843
|
syncArchiveDirectory(archiveRoot);
|
|
17569
17844
|
verifyArchive(move);
|
|
17570
17845
|
move.status = "archived";
|
|
@@ -17601,7 +17876,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17601
17876
|
}
|
|
17602
17877
|
if (entry.discoveryOnly) {
|
|
17603
17878
|
linkSync2(entry.archive, entry.source);
|
|
17604
|
-
syncArchiveDirectory(
|
|
17879
|
+
syncArchiveDirectory(dirname15(entry.source));
|
|
17605
17880
|
unlinkSync4(entry.archive);
|
|
17606
17881
|
} else {
|
|
17607
17882
|
if (lstatSync20(entry.source, { throwIfNoEntry: false })) {
|
|
@@ -17612,7 +17887,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
|
|
|
17612
17887
|
renameSync3(entry.archive, entry.source);
|
|
17613
17888
|
}
|
|
17614
17889
|
entry.status = "restored";
|
|
17615
|
-
syncArchiveDirectory(
|
|
17890
|
+
syncArchiveDirectory(dirname15(entry.source));
|
|
17616
17891
|
syncArchiveDirectory(archiveRoot);
|
|
17617
17892
|
} catch {
|
|
17618
17893
|
entry.status = "recovery-required";
|
|
@@ -17637,7 +17912,7 @@ function isInertCodexPluginCacheCopy(entry, cache, allowedHostPaths, read2) {
|
|
|
17637
17912
|
return false;
|
|
17638
17913
|
const document2 = join23(entry.path, "SKILL.md");
|
|
17639
17914
|
const inside = relative6(cache, entry.path);
|
|
17640
|
-
if (!inside ||
|
|
17915
|
+
if (!inside || isAbsolute16(inside) || inside.split(sep7).some((segment) => segment === ".."))
|
|
17641
17916
|
return false;
|
|
17642
17917
|
assertSafePath(entry.path);
|
|
17643
17918
|
if (!lstatSync20(cache).isDirectory() || !lstatSync20(entry.path).isDirectory() || !lstatSync20(document2).isFile())
|
|
@@ -17657,7 +17932,7 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
17657
17932
|
assertManagedAgentBridgeWithDiscovery(agent, options);
|
|
17658
17933
|
}
|
|
17659
17934
|
function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
|
|
17660
|
-
const home =
|
|
17935
|
+
const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
17661
17936
|
assertSafePath(join23(dataDir2, "agent-policy.json"));
|
|
17662
17937
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
|
|
17663
17938
|
if (!snapshot2)
|
|
@@ -17755,12 +18030,11 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
17755
18030
|
verifyAgentDiscovery(agent === "codex" ? { ...discovery, codexDisabledPluginSkills: binding.codexPluginSkills ?? [] } : discovery, options.codexDiscoveryRecovery);
|
|
17756
18031
|
if (agent === "sumi" && discovery.method === "reviewed")
|
|
17757
18032
|
resolveAgentDiscovery({ home, agent, retainedReview: discovery, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
17758
|
-
if (discovery.method === "automatic")
|
|
17759
|
-
|
|
17760
|
-
if (JSON.stringify(current) !== JSON.stringify(discovery))
|
|
17761
|
-
throw new Error("Configured native discovery roots changed");
|
|
17762
|
-
}
|
|
18033
|
+
if (discovery.method === "automatic")
|
|
18034
|
+
verifyAutomaticDiscoveryClosure(discovery, { home, canonical: (path) => canonicalAgentPath(path, aliases), change: "Configured native discovery roots changed", executable: binding.discoveryExecutables?.[agent] });
|
|
17763
18035
|
} catch (error) {
|
|
18036
|
+
if (isDiscoveryRootUnresolved(error))
|
|
18037
|
+
throw error;
|
|
17764
18038
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
17765
18039
|
}
|
|
17766
18040
|
let installationInputRoots = [];
|
|
@@ -18053,9 +18327,9 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
|
|
|
18053
18327
|
const action = dirExists ? "update" : "create";
|
|
18054
18328
|
if (options.dryRun)
|
|
18055
18329
|
return { action, path: skillMdPath };
|
|
18056
|
-
const parentDir =
|
|
18330
|
+
const parentDir = dirname16(dir);
|
|
18057
18331
|
mkdirSync7(parentDir, { recursive: true });
|
|
18058
|
-
const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${
|
|
18332
|
+
const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${basename8(dir)}-`));
|
|
18059
18333
|
const candidateDir = join25(transactionDir, "candidate");
|
|
18060
18334
|
const backupDir = join25(transactionDir, "backup");
|
|
18061
18335
|
const candidateSkillMdPath = join25(candidateDir, "SKILL.md");
|
|
@@ -19047,7 +19321,7 @@ function exactProfileSelection(spec, profile) {
|
|
|
19047
19321
|
}
|
|
19048
19322
|
|
|
19049
19323
|
// src/lib/plugin-projection.ts
|
|
19050
|
-
import { createHash as
|
|
19324
|
+
import { createHash as createHash21 } from "crypto";
|
|
19051
19325
|
import { posix } from "path";
|
|
19052
19326
|
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
|
|
19053
19327
|
var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -19058,7 +19332,7 @@ function pluginNeed(value, message) {
|
|
|
19058
19332
|
if (!value)
|
|
19059
19333
|
pluginRefusal(message);
|
|
19060
19334
|
}
|
|
19061
|
-
var pluginHash = (bytes) =>
|
|
19335
|
+
var pluginHash = (bytes) => createHash21("sha256").update(bytes).digest("hex");
|
|
19062
19336
|
function pluginObject(value) {
|
|
19063
19337
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
19064
19338
|
}
|
|
@@ -19246,9 +19520,9 @@ function buildPluginProjection(entries) {
|
|
|
19246
19520
|
}
|
|
19247
19521
|
|
|
19248
19522
|
// src/lib/plugin-projection-store.ts
|
|
19249
|
-
import { createHash as
|
|
19523
|
+
import { createHash as createHash22, randomUUID as randomUUID4 } from "crypto";
|
|
19250
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";
|
|
19251
|
-
import { dirname as
|
|
19525
|
+
import { dirname as dirname17, isAbsolute as isAbsolute17, join as join29, parse as parse6, resolve as resolve16 } from "path";
|
|
19252
19526
|
var READ_FLAGS = constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
|
|
19253
19527
|
var WRITE_FLAGS = constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
|
|
19254
19528
|
var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
|
|
@@ -19262,8 +19536,8 @@ function stat(path) {
|
|
|
19262
19536
|
return pluginRefusal("Cannot inspect plugin filesystem entry");
|
|
19263
19537
|
}
|
|
19264
19538
|
}
|
|
19265
|
-
function
|
|
19266
|
-
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");
|
|
19267
19541
|
}
|
|
19268
19542
|
function owner(stats, system = false) {
|
|
19269
19543
|
const current = uid();
|
|
@@ -19273,8 +19547,8 @@ function same3(left, right, identityOnly = false) {
|
|
|
19273
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");
|
|
19274
19548
|
}
|
|
19275
19549
|
function ancestors(path, create = false) {
|
|
19276
|
-
|
|
19277
|
-
const result = [], parent =
|
|
19550
|
+
canonical7(path);
|
|
19551
|
+
const result = [], parent = dirname17(path), filesystemRoot = parse6(parent).root;
|
|
19278
19552
|
let current = filesystemRoot;
|
|
19279
19553
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
19280
19554
|
if (segment)
|
|
@@ -19302,7 +19576,7 @@ function verifyAncestors(chain, identityOnly = true) {
|
|
|
19302
19576
|
}
|
|
19303
19577
|
function privateParent(path) {
|
|
19304
19578
|
const chain = ancestors(path, true), parent = chain.at(-1);
|
|
19305
|
-
pluginNeed(parent && parent.path ===
|
|
19579
|
+
pluginNeed(parent && parent.path === dirname17(path), "Plugin storage parent is unavailable");
|
|
19306
19580
|
owner(parent.stat);
|
|
19307
19581
|
pluginNeed((parent.stat.mode & 0o7777n) === 0o700n, "Plugin storage parent must have mode 0700");
|
|
19308
19582
|
return chain;
|
|
@@ -19588,7 +19862,7 @@ function pluginExecutableDigest(path) {
|
|
|
19588
19862
|
const opened = fstatSync12(fd, { bigint: true });
|
|
19589
19863
|
same3(before, opened);
|
|
19590
19864
|
fileShape(opened, maximum, "executable");
|
|
19591
|
-
const hash2 =
|
|
19865
|
+
const hash2 = createHash22("sha256"), buffer = new Uint8Array(64 * 1024);
|
|
19592
19866
|
let remaining = Number(opened.size);
|
|
19593
19867
|
while (remaining > 0) {
|
|
19594
19868
|
const count = readSync9(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
|
|
@@ -19646,7 +19920,7 @@ function validatePluginTarget(value) {
|
|
|
19646
19920
|
for (const registration of value.registrations) {
|
|
19647
19921
|
pluginKeys(registration, ["scope", "projectPath"]);
|
|
19648
19922
|
pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
|
|
19649
|
-
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");
|
|
19650
19924
|
const key = JSON.stringify([registration.scope, registration.projectPath]);
|
|
19651
19925
|
pluginNeed(!scopes.has(key), "Duplicate plugin registration scope");
|
|
19652
19926
|
scopes.add(key);
|
|
@@ -19654,7 +19928,7 @@ function validatePluginTarget(value) {
|
|
|
19654
19928
|
for (const key of ["native", "resolver"]) {
|
|
19655
19929
|
pluginKeys(value[key], key === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
|
|
19656
19930
|
pluginText(value[key].executable);
|
|
19657
|
-
pluginNeed(
|
|
19931
|
+
pluginNeed(isAbsolute18(value[key].executable) && resolve17(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
|
|
19658
19932
|
pluginDigest(value[key].digest);
|
|
19659
19933
|
}
|
|
19660
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)");
|
|
@@ -19799,14 +20073,14 @@ function validatePlan(value) {
|
|
|
19799
20073
|
pluginNeed(observedEvidenceDigest(plan.observation) === plan.evidenceDigest, "Plugin receipt observed evidence changed");
|
|
19800
20074
|
pluginNeed(identityDigest(plan) === plan.planDigest, "Plugin receipt immutable identity changed");
|
|
19801
20075
|
}
|
|
19802
|
-
var
|
|
20076
|
+
var quote2 = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
19803
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"';
|
|
19804
20078
|
function pluginResolverCommand(binding) {
|
|
19805
|
-
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))}`;
|
|
19806
20080
|
}
|
|
19807
20081
|
function root(options) {
|
|
19808
20082
|
const path = options.storeRoot ?? pluginAdmissionRoot();
|
|
19809
|
-
pluginNeed(
|
|
20083
|
+
pluginNeed(isAbsolute18(path) && resolve17(path) === path, "Invalid plugin admission store root");
|
|
19810
20084
|
return path;
|
|
19811
20085
|
}
|
|
19812
20086
|
function pluginBindingPath(storeRoot, id) {
|
|
@@ -20011,15 +20285,15 @@ function validateManagedPluginWitnesses(value) {
|
|
|
20011
20285
|
pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
|
|
20012
20286
|
seen.add(item.bindingId);
|
|
20013
20287
|
pluginText(item.storeRoot);
|
|
20014
|
-
pluginNeed(
|
|
20288
|
+
pluginNeed(isAbsolute19(item.storeRoot) && resolve18(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
|
|
20015
20289
|
}
|
|
20016
20290
|
}
|
|
20017
20291
|
function safe3(path) {
|
|
20018
20292
|
pluginText(path);
|
|
20019
|
-
pluginNeed(
|
|
20020
|
-
for (let at = path;; at =
|
|
20293
|
+
pluginNeed(isAbsolute19(path) && resolve18(path) === path, "Invalid plugin registry path");
|
|
20294
|
+
for (let at = path;; at = dirname18(at)) {
|
|
20021
20295
|
pluginNeed(!lstatSync23(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
|
|
20022
|
-
if (at ===
|
|
20296
|
+
if (at === dirname18(at))
|
|
20023
20297
|
break;
|
|
20024
20298
|
}
|
|
20025
20299
|
}
|
|
@@ -20069,7 +20343,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
20069
20343
|
seen.add(target.pluginId);
|
|
20070
20344
|
const rows = plugins[target.pluginId];
|
|
20071
20345
|
pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
|
|
20072
|
-
const [name, market] = target.pluginId.split("@"), cacheRoot = join31(
|
|
20346
|
+
const [name, market] = target.pluginId.split("@"), cacheRoot = join31(dirname18(path), "cache", market, name);
|
|
20073
20347
|
const scopes = new Set, approvals = new Map, currentFiles = new Map;
|
|
20074
20348
|
plugins[target.pluginId] = rows.map((row) => {
|
|
20075
20349
|
pluginNeed(pluginObject(row), "Invalid managed plugin registration");
|
|
@@ -20085,7 +20359,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
20085
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");
|
|
20086
20360
|
const approval = (producer) => {
|
|
20087
20361
|
pluginText(producer);
|
|
20088
|
-
const digest = `sha256:${
|
|
20362
|
+
const digest = `sha256:${basename9(producer)}`;
|
|
20089
20363
|
const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest);
|
|
20090
20364
|
pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
|
|
20091
20365
|
if (!verified.has(producer)) {
|
|
@@ -20147,39 +20421,39 @@ function captureManagedPluginRegistry(path, managedPlugins) {
|
|
|
20147
20421
|
}
|
|
20148
20422
|
|
|
20149
20423
|
// src/lib/claude-marketplace-registry.ts
|
|
20150
|
-
import { createHash as
|
|
20424
|
+
import { createHash as createHash23 } from "crypto";
|
|
20151
20425
|
import { closeSync as closeSync14, constants as constants14, fstatSync as fstatSync14, lstatSync as lstatSync24, openSync as openSync14, readSync as readSync11 } from "fs";
|
|
20152
|
-
import { basename as
|
|
20426
|
+
import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute20, resolve as resolve19 } from "path";
|
|
20153
20427
|
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
20154
|
-
function
|
|
20428
|
+
function need7(value, reason2) {
|
|
20155
20429
|
if (!value)
|
|
20156
|
-
throw new Error(`Claude marketplace registry ${
|
|
20430
|
+
throw new Error(`Claude marketplace registry ${reason2}`);
|
|
20157
20431
|
}
|
|
20158
20432
|
function absolutePath3(value) {
|
|
20159
|
-
|
|
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");
|
|
20160
20434
|
}
|
|
20161
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;
|
|
20162
20436
|
function parents2(path) {
|
|
20163
20437
|
const result = [];
|
|
20164
|
-
for (let at =
|
|
20438
|
+
for (let at = dirname19(path);; at = dirname19(at)) {
|
|
20165
20439
|
const stat2 = lstatSync24(at, { bigint: true });
|
|
20166
|
-
|
|
20440
|
+
need7(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
20167
20441
|
result.push([at, stat2]);
|
|
20168
|
-
if (at ===
|
|
20442
|
+
if (at === dirname19(at))
|
|
20169
20443
|
return result;
|
|
20170
20444
|
}
|
|
20171
20445
|
}
|
|
20172
20446
|
function readRegistry2(path, budget2) {
|
|
20173
20447
|
absolutePath3(path);
|
|
20174
|
-
|
|
20175
|
-
|
|
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");
|
|
20176
20450
|
const ancestors2 = parents2(path), initial = lstatSync24(path, { bigint: true });
|
|
20177
|
-
|
|
20178
|
-
|
|
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");
|
|
20179
20453
|
const fd = openSync14(path, constants14.O_RDONLY | constants14.O_NOFOLLOW | constants14.O_NONBLOCK);
|
|
20180
20454
|
try {
|
|
20181
20455
|
const opened = fstatSync14(fd, { bigint: true });
|
|
20182
|
-
|
|
20456
|
+
need7(opened.isFile() && sameFile2(initial, opened), "changed during open");
|
|
20183
20457
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
20184
20458
|
let length = 0;
|
|
20185
20459
|
while (length < bytes.length) {
|
|
@@ -20189,10 +20463,10 @@ function readRegistry2(path, budget2) {
|
|
|
20189
20463
|
length += count;
|
|
20190
20464
|
}
|
|
20191
20465
|
const after = fstatSync14(fd, { bigint: true }), current = lstatSync24(path, { bigint: true });
|
|
20192
|
-
|
|
20466
|
+
need7(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
20193
20467
|
for (const [ancestor, before] of ancestors2) {
|
|
20194
20468
|
const now = lstatSync24(ancestor, { bigint: true });
|
|
20195
|
-
|
|
20469
|
+
need7(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
20196
20470
|
}
|
|
20197
20471
|
budget2.remaining -= length;
|
|
20198
20472
|
try {
|
|
@@ -20212,7 +20486,7 @@ function parse7(text2) {
|
|
|
20212
20486
|
at++;
|
|
20213
20487
|
};
|
|
20214
20488
|
function string() {
|
|
20215
|
-
|
|
20489
|
+
need7(text2[at] === '"', "contains invalid JSON");
|
|
20216
20490
|
const start = at++;
|
|
20217
20491
|
while (at < text2.length) {
|
|
20218
20492
|
const character = text2[at++];
|
|
@@ -20223,7 +20497,7 @@ function parse7(text2) {
|
|
|
20223
20497
|
} catch {
|
|
20224
20498
|
throw new Error("Claude marketplace registry contains invalid JSON string");
|
|
20225
20499
|
}
|
|
20226
|
-
|
|
20500
|
+
need7(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
|
|
20227
20501
|
return value2;
|
|
20228
20502
|
}
|
|
20229
20503
|
if (character === "\\")
|
|
@@ -20232,7 +20506,7 @@ function parse7(text2) {
|
|
|
20232
20506
|
throw new Error("Claude marketplace registry contains an unterminated JSON string");
|
|
20233
20507
|
}
|
|
20234
20508
|
function value(depth) {
|
|
20235
|
-
|
|
20509
|
+
need7(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
20236
20510
|
space();
|
|
20237
20511
|
const character = text2[at];
|
|
20238
20512
|
if (character === '"')
|
|
@@ -20246,17 +20520,17 @@ function parse7(text2) {
|
|
|
20246
20520
|
if (object6) {
|
|
20247
20521
|
space();
|
|
20248
20522
|
const key = string();
|
|
20249
|
-
|
|
20523
|
+
need7(!keys.has(key), "contains a duplicate JSON key");
|
|
20250
20524
|
keys.add(key);
|
|
20251
20525
|
space();
|
|
20252
|
-
|
|
20526
|
+
need7(text2[at++] === ":", "contains invalid JSON");
|
|
20253
20527
|
entries.push([key, value(depth + 1)]);
|
|
20254
20528
|
} else
|
|
20255
20529
|
items.push(value(depth + 1));
|
|
20256
20530
|
space();
|
|
20257
20531
|
if (text2[at] === end)
|
|
20258
20532
|
break;
|
|
20259
|
-
|
|
20533
|
+
need7(text2[at++] === ",", "contains invalid JSON");
|
|
20260
20534
|
}
|
|
20261
20535
|
at++;
|
|
20262
20536
|
return object6 ? { kind: "object", entries } : { kind: "array", items };
|
|
@@ -20268,67 +20542,67 @@ function parse7(text2) {
|
|
|
20268
20542
|
}
|
|
20269
20543
|
numberPattern.lastIndex = at;
|
|
20270
20544
|
const number = numberPattern.exec(text2);
|
|
20271
|
-
|
|
20545
|
+
need7(number, "contains invalid JSON");
|
|
20272
20546
|
at += number[0].length;
|
|
20273
20547
|
return { kind: "number", value: number[0] };
|
|
20274
20548
|
}
|
|
20275
20549
|
const root2 = value(0);
|
|
20276
20550
|
space();
|
|
20277
|
-
|
|
20278
|
-
|
|
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");
|
|
20279
20553
|
return root2;
|
|
20280
20554
|
}
|
|
20281
20555
|
function exactKeys(value, keys) {
|
|
20282
20556
|
return value?.kind === "object" && value.entries.length === keys.length && value.entries.every(([key]) => keys.includes(key));
|
|
20283
20557
|
}
|
|
20284
|
-
var
|
|
20558
|
+
var field2 = (object6, key) => object6.entries.find(([name]) => name === key)?.[1];
|
|
20285
20559
|
function textField(value) {
|
|
20286
|
-
|
|
20560
|
+
need7(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
|
|
20287
20561
|
return value.value;
|
|
20288
20562
|
}
|
|
20289
20563
|
function normalizeRow(row, version = 1) {
|
|
20290
20564
|
if (!exactKeys(row, ["source", "installLocation", "lastUpdated"])) {
|
|
20291
20565
|
if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
20292
20566
|
return;
|
|
20293
|
-
const autoUpdate =
|
|
20294
|
-
|
|
20567
|
+
const autoUpdate = field2(row, "autoUpdate");
|
|
20568
|
+
need7(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
20295
20569
|
}
|
|
20296
|
-
const source =
|
|
20570
|
+
const source = field2(row, "source");
|
|
20297
20571
|
if (source?.kind !== "object")
|
|
20298
20572
|
return;
|
|
20299
|
-
const type =
|
|
20573
|
+
const type = field2(source, "source");
|
|
20300
20574
|
if (type?.kind !== "string" || !["github", "directory"].includes(type.value))
|
|
20301
20575
|
return;
|
|
20302
20576
|
const location = type.value === "github" ? "repo" : "path";
|
|
20303
20577
|
if (!exactKeys(source, ["source", location]))
|
|
20304
20578
|
return;
|
|
20305
|
-
const sourceLocation = textField(
|
|
20579
|
+
const sourceLocation = textField(field2(source, location));
|
|
20306
20580
|
if (type.value === "directory")
|
|
20307
20581
|
absolutePath3(sourceLocation);
|
|
20308
|
-
absolutePath3(textField(
|
|
20309
|
-
const updated = textField(
|
|
20310
|
-
|
|
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");
|
|
20311
20585
|
const millis = Date.parse(updated);
|
|
20312
|
-
|
|
20586
|
+
need7(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
20313
20587
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
20314
20588
|
}
|
|
20315
20589
|
function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
20316
20590
|
const value = parse7(readRegistry2(path, budget2));
|
|
20317
20591
|
for (const [, row] of value.entries)
|
|
20318
20592
|
normalizeRow(row);
|
|
20319
|
-
const sha2563 =
|
|
20593
|
+
const sha2563 = createHash23("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
20320
20594
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2563 };
|
|
20321
20595
|
}
|
|
20322
20596
|
function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
20323
20597
|
const value = parse7(readRegistry2(path, budget2));
|
|
20324
20598
|
for (const [, row] of value.entries)
|
|
20325
20599
|
normalizeRow(row, 2);
|
|
20326
|
-
const sha2563 =
|
|
20600
|
+
const sha2563 = createHash23("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
20327
20601
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2563 };
|
|
20328
20602
|
}
|
|
20329
20603
|
|
|
20330
20604
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
20331
|
-
import { createHash as
|
|
20605
|
+
import { createHash as createHash24 } from "crypto";
|
|
20332
20606
|
|
|
20333
20607
|
// src/lib/codex-hook-additions-review.ts
|
|
20334
20608
|
var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
@@ -20336,12 +20610,12 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
|
20336
20610
|
var proofs = new WeakMap;
|
|
20337
20611
|
|
|
20338
20612
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
20339
|
-
var sha4 = (text2) =>
|
|
20613
|
+
var sha4 = (text2) => createHash24("sha256").update(text2).digest("hex");
|
|
20340
20614
|
var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
|
|
20341
20615
|
var proofs2 = new WeakMap;
|
|
20342
20616
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
20343
20617
|
const proof = proofs2.get(witness);
|
|
20344
|
-
|
|
20618
|
+
need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
20345
20619
|
unchanged2(proof.before);
|
|
20346
20620
|
unchanged2(proof.current);
|
|
20347
20621
|
proof.recheckAdditions();
|
|
@@ -20350,12 +20624,43 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
20350
20624
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
20351
20625
|
return false;
|
|
20352
20626
|
assertCodexHookDiscoveryRecovery(binding, witness);
|
|
20353
|
-
|
|
20627
|
+
need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
20354
20628
|
return true;
|
|
20355
20629
|
}
|
|
20356
20630
|
|
|
20357
20631
|
// src/lib/agent-discovery.ts
|
|
20358
|
-
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");
|
|
20359
20664
|
function parseConfig(text2, path, toml = false) {
|
|
20360
20665
|
try {
|
|
20361
20666
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -20367,12 +20672,12 @@ function parseConfig(text2, path, toml = false) {
|
|
|
20367
20672
|
}
|
|
20368
20673
|
}
|
|
20369
20674
|
function safe4(path) {
|
|
20370
|
-
if (!
|
|
20675
|
+
if (!isAbsolute21(path) || path.includes("\x00"))
|
|
20371
20676
|
throw new Error("Expected an absolute native discovery path");
|
|
20372
|
-
for (let cursor = path;; cursor =
|
|
20677
|
+
for (let cursor = path;; cursor = dirname20(cursor)) {
|
|
20373
20678
|
if (lstatSync25(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
20374
20679
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
20375
|
-
if (
|
|
20680
|
+
if (dirname20(cursor) === cursor)
|
|
20376
20681
|
break;
|
|
20377
20682
|
}
|
|
20378
20683
|
}
|
|
@@ -20391,7 +20696,7 @@ function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
|
20391
20696
|
const object6 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
|
|
20392
20697
|
if (!object6 || typeof object6 !== "object" || Array.isArray(object6))
|
|
20393
20698
|
throw new Error("Expected native discovery configuration object");
|
|
20394
|
-
return digest(JSON.stringify(Object.fromEntries(fields2.map((
|
|
20699
|
+
return digest(JSON.stringify(Object.fromEntries(fields2.map((field3) => [field3, object6[field3] ?? null]))));
|
|
20395
20700
|
}
|
|
20396
20701
|
function isSupersededSettingsProjection(agent, configPath, source) {
|
|
20397
20702
|
if (agent === "sumi")
|
|
@@ -20436,6 +20741,14 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
|
20436
20741
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
20437
20742
|
return sha2563;
|
|
20438
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
|
+
}
|
|
20439
20752
|
if (source.hashMode === "claude-plugin-registry") {
|
|
20440
20753
|
if (source.format !== undefined || source.fields !== undefined || !source.managedPlugins || changes?.has(source.path))
|
|
20441
20754
|
throw new Error("Managed plugin registry witnesses require verified native registrations");
|
|
@@ -20473,7 +20786,7 @@ function absentCodexSkillRoots(binding) {
|
|
|
20473
20786
|
throw new Error("Invalid disabled Codex discovery proof");
|
|
20474
20787
|
if (binding.method !== "reviewed" || controls.every((control) => lstatSync25(control.pluginParent, { throwIfNoEntry: false })))
|
|
20475
20788
|
return [];
|
|
20476
|
-
const cache =
|
|
20789
|
+
const cache = dirname20(dirname20(controls[0].pluginParent)), config = join32(dirname20(dirname20(cache)), "config.toml");
|
|
20477
20790
|
const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
20478
20791
|
if (!source || projected(source) !== source.sha256)
|
|
20479
20792
|
throw new Error("Missing current disabled Codex configuration witness");
|
|
@@ -20481,7 +20794,7 @@ function absentCodexSkillRoots(binding) {
|
|
|
20481
20794
|
return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
|
|
20482
20795
|
}
|
|
20483
20796
|
function codexDirectHookSelects(cache, root2) {
|
|
20484
|
-
const codex =
|
|
20797
|
+
const codex = dirname20(dirname20(cache)), config = Bun.TOML.parse(read2(join32(codex, "config.toml")) ?? "");
|
|
20485
20798
|
const hooks = read2(join32(codex, "hooks.json"));
|
|
20486
20799
|
if (hooks !== null)
|
|
20487
20800
|
hashNativeJsonControls(hooks);
|
|
@@ -20494,7 +20807,7 @@ function retiredCodexRoots(binding) {
|
|
|
20494
20807
|
const controls = binding.codexDisabledPluginSkills;
|
|
20495
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)
|
|
20496
20809
|
throw new Error("Invalid retired Codex materialization proof");
|
|
20497
|
-
const cache =
|
|
20810
|
+
const cache = dirname20(dirname20(controls[0].pluginParent)), config = join32(dirname20(dirname20(cache)), "config.toml");
|
|
20498
20811
|
const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
20499
20812
|
if (!source || projected(source) !== source.sha256)
|
|
20500
20813
|
throw new Error("Missing current retired Codex configuration witness");
|
|
@@ -20511,8 +20824,8 @@ function retiredCodexRoots(binding) {
|
|
|
20511
20824
|
function captureRetiredCodexDiscovery(binding, controls, rules) {
|
|
20512
20825
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !controls.length)
|
|
20513
20826
|
return binding;
|
|
20514
|
-
const cache =
|
|
20515
|
-
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)));
|
|
20516
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();
|
|
20517
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() : [];
|
|
20518
20831
|
const { codexRetiredMaterializations: previous, ...current } = binding;
|
|
@@ -20541,7 +20854,7 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
20541
20854
|
safe4(root2);
|
|
20542
20855
|
if (lstatSync25(root2, { throwIfNoEntry: false }))
|
|
20543
20856
|
continue;
|
|
20544
|
-
const claude =
|
|
20857
|
+
const claude = dirname20(dirname20(cache)), settings = join32(claude, "settings.json"), registry = join32(claude, "plugins/installed_plugins.json");
|
|
20545
20858
|
const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
|
|
20546
20859
|
const configSource = full(settings), registrySource = full(registry);
|
|
20547
20860
|
if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
|
|
@@ -20558,7 +20871,7 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
20558
20871
|
if (!Array.isArray(rows) || rows.length !== 1 || rows[0]?.scope !== "user" || typeof rows[0]?.installPath !== "string")
|
|
20559
20872
|
continue;
|
|
20560
20873
|
const active = rows[0].installPath;
|
|
20561
|
-
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)
|
|
20562
20875
|
continue;
|
|
20563
20876
|
const activeManifest = full(join32(active, ".claude-plugin/plugin.json"));
|
|
20564
20877
|
if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
|
|
@@ -20609,7 +20922,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
20609
20922
|
assertMarketplaceBinding(binding, source);
|
|
20610
20923
|
if (source.hashMode === "claude-plugin-registry" && binding.agent !== "claude")
|
|
20611
20924
|
throw new Error("Managed Claude registry witnesses cannot apply to another agent");
|
|
20612
|
-
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)))
|
|
20613
20926
|
throw new Error("Invalid native discovery projection");
|
|
20614
20927
|
if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
20615
20928
|
throw new Error("Invalid native discovery digest");
|
|
@@ -20637,15 +20950,15 @@ function codexInstallationRoots(binding) {
|
|
|
20637
20950
|
const proof = binding.codexInstallationInputs;
|
|
20638
20951
|
if (proof === undefined)
|
|
20639
20952
|
return [];
|
|
20640
|
-
if (binding.agent !== "codex" || binding.method !== "reviewed" || proof.version
|
|
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)
|
|
20641
20954
|
throw new Error("Invalid native installation input proof");
|
|
20642
|
-
const cache =
|
|
20643
|
-
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)));
|
|
20644
20957
|
const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
|
|
20645
20958
|
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join32(home, path));
|
|
20646
|
-
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)))))
|
|
20647
20960
|
throw new Error("Native installation input overlaps configuration or loading roots");
|
|
20648
|
-
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)))
|
|
20649
20962
|
throw new Error("Missing native installation input source witness");
|
|
20650
20963
|
return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
|
|
20651
20964
|
const text2 = read2(path);
|
|
@@ -20655,16 +20968,18 @@ function codexInstallationRoots(binding) {
|
|
|
20655
20968
|
});
|
|
20656
20969
|
}
|
|
20657
20970
|
function assertMarketplaceBinding(binding, source) {
|
|
20658
|
-
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"))))
|
|
20659
20972
|
throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
|
|
20660
|
-
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"))
|
|
20661
20974
|
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
20662
|
-
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"))
|
|
20663
20976
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
20664
|
-
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"))
|
|
20665
20978
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
20666
20979
|
if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
20667
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");
|
|
20668
20983
|
}
|
|
20669
20984
|
function rebindAgentDiscovery(binding, changes) {
|
|
20670
20985
|
const budget2 = discoveryByteBudget();
|
|
@@ -20697,10 +21012,10 @@ function captureDiscoveryPathSources(paths) {
|
|
|
20697
21012
|
const budget2 = discoveryByteBudget();
|
|
20698
21013
|
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget2) }));
|
|
20699
21014
|
}
|
|
20700
|
-
function assertProjectDiscovery(agent, directories, home,
|
|
20701
|
-
const homeConfigPath =
|
|
21015
|
+
function assertProjectDiscovery(agent, directories, home, canonical8 = resolve20, reviewed) {
|
|
21016
|
+
const homeConfigPath = canonical8(agentDiscoveryConfigPath(home, agent));
|
|
20702
21017
|
for (const directory of directories) {
|
|
20703
|
-
const isHome =
|
|
21018
|
+
const isHome = resolve20(directory) === resolve20(home);
|
|
20704
21019
|
if (agent === "hermes") {
|
|
20705
21020
|
assertHermesEnvironment(home);
|
|
20706
21021
|
continue;
|
|
@@ -20709,7 +21024,7 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
|
|
|
20709
21024
|
continue;
|
|
20710
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"];
|
|
20711
21026
|
for (const suffix of paths) {
|
|
20712
|
-
const path =
|
|
21027
|
+
const path = canonical8(join32(directory, suffix)), raw = read2(path);
|
|
20713
21028
|
if (raw === null)
|
|
20714
21029
|
continue;
|
|
20715
21030
|
const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
|
|
@@ -20725,7 +21040,7 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
|
|
|
20725
21040
|
const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
|
|
20726
21041
|
if (exact && projected(exact) === exact.sha256 && managed.length) {
|
|
20727
21042
|
const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
|
|
20728
|
-
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))));
|
|
20729
21044
|
}
|
|
20730
21045
|
}
|
|
20731
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)))
|
|
@@ -20733,13 +21048,13 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
|
|
|
20733
21048
|
}
|
|
20734
21049
|
if (agent === "sumi")
|
|
20735
21050
|
for (const name of ["plugin", "plugins"]) {
|
|
20736
|
-
const path =
|
|
21051
|
+
const path = canonical8(join32(directory, ".sumi", name));
|
|
20737
21052
|
safe4(path);
|
|
20738
21053
|
if (lstatSync25(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(path).length)
|
|
20739
21054
|
throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
|
|
20740
21055
|
}
|
|
20741
21056
|
if (agent === "claude") {
|
|
20742
|
-
const commands =
|
|
21057
|
+
const commands = canonical8(join32(directory, ".claude/commands"));
|
|
20743
21058
|
safe4(commands);
|
|
20744
21059
|
if (lstatSync25(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
20745
21060
|
throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
|
|
@@ -20751,11 +21066,37 @@ function agentDiscoveryConfigPath(home, agent) {
|
|
|
20751
21066
|
return sumiConfigPath(home);
|
|
20752
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"}`);
|
|
20753
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
|
+
}
|
|
20754
21095
|
function resolveAgentDiscovery(options) {
|
|
20755
|
-
const
|
|
21096
|
+
const canonical8 = options.canonical ?? resolve20, home = resolve20(options.home), agent = options.agent;
|
|
20756
21097
|
const sources = [], roots = new Set, builtinNames = [];
|
|
20757
21098
|
const witness = (path, format, fields2, hashMode) => {
|
|
20758
|
-
const source = { path:
|
|
21099
|
+
const source = { path: canonical8(path), sha256: null, ...format ? { format, fields: fields2 } : {}, ...hashMode ? { hashMode } : {} };
|
|
20759
21100
|
source.sha256 = projected(source);
|
|
20760
21101
|
sources.push(source);
|
|
20761
21102
|
return read2(source.path);
|
|
@@ -20777,13 +21118,13 @@ function resolveAgentDiscovery(options) {
|
|
|
20777
21118
|
unresolved("JSONC configuration requires a supported format adapter");
|
|
20778
21119
|
for (const root2 of [directory, join32(home, ".claude"), join32(home, ".agents")])
|
|
20779
21120
|
for (const name of ["skill", "skills"])
|
|
20780
|
-
roots.add(
|
|
21121
|
+
roots.add(canonical8(join32(root2, name)));
|
|
20781
21122
|
if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
|
|
20782
21123
|
unresolved("skills must be a string array");
|
|
20783
21124
|
for (const value of config.skills ?? []) {
|
|
20784
|
-
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(
|
|
21125
|
+
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute21(value) || value.startsWith("~/")))
|
|
20785
21126
|
unresolved("remote or relative skill sources need a dedicated discovery adapter");
|
|
20786
|
-
roots.add(
|
|
21127
|
+
roots.add(canonical8(value.startsWith("~/") ? join32(home, value.slice(2)) : value));
|
|
20787
21128
|
}
|
|
20788
21129
|
for (const key of ["plugin", "plugins"])
|
|
20789
21130
|
if (config[key] !== undefined && !Array.isArray(config[key]))
|
|
@@ -20822,23 +21163,23 @@ function resolveAgentDiscovery(options) {
|
|
|
20822
21163
|
continue;
|
|
20823
21164
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
20824
21165
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
20825
|
-
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);
|
|
20826
21167
|
safe4(path);
|
|
20827
21168
|
roots.add(path);
|
|
20828
21169
|
}
|
|
20829
21170
|
}
|
|
20830
21171
|
if (agent === "gemini") {
|
|
20831
|
-
const executable2 =
|
|
21172
|
+
const executable2 = options.executable === undefined ? discoveryExecutable(agent) : options.executable;
|
|
20832
21173
|
if (executable2) {
|
|
20833
21174
|
let packageRoot;
|
|
20834
|
-
for (let directory =
|
|
21175
|
+
for (let directory = dirname20(executable2.target), depth = 0;depth < 8; directory = dirname20(directory), depth++) {
|
|
20835
21176
|
const path = join32(directory, "package.json"), raw = read2(path);
|
|
20836
21177
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
20837
21178
|
packageRoot = directory;
|
|
20838
21179
|
witness(path);
|
|
20839
21180
|
break;
|
|
20840
21181
|
}
|
|
20841
|
-
if (
|
|
21182
|
+
if (dirname20(directory) === directory)
|
|
20842
21183
|
break;
|
|
20843
21184
|
}
|
|
20844
21185
|
if (!packageRoot)
|
|
@@ -20861,11 +21202,11 @@ function resolveAgentDiscovery(options) {
|
|
|
20861
21202
|
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
20862
21203
|
unresolved("invalid retained review");
|
|
20863
21204
|
verifyAgentDiscovery(retained);
|
|
20864
|
-
if (!retained.sources.some((source) => source.path ===
|
|
21205
|
+
if (!retained.sources.some((source) => source.path === canonical8(configPath) && source.format === undefined && source.fields === undefined))
|
|
20865
21206
|
unresolved("retained review is missing the full agent configuration source");
|
|
20866
|
-
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)))
|
|
20867
21208
|
unresolved("retained settings witness names another configuration source");
|
|
20868
|
-
const covered = supersedesSettingsProjection(agent, retained.sources,
|
|
21209
|
+
const covered = supersedesSettingsProjection(agent, retained.sources, canonical8(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical8(configPath), source)) : sources;
|
|
20869
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))
|
|
20870
21211
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
20871
21212
|
return retained;
|
|
@@ -20877,15 +21218,15 @@ function resolveAgentDiscovery(options) {
|
|
|
20877
21218
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
20878
21219
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
20879
21220
|
verifyAgentDiscovery(supplied);
|
|
20880
|
-
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)))
|
|
20881
21222
|
throw new Error("Semantic settings witnesses must name the configured agent configuration source");
|
|
20882
|
-
if (!review.sources.some((source) => source.path ===
|
|
21223
|
+
if (!review.sources.some((source) => source.path === canonical8(configPath)))
|
|
20883
21224
|
throw new Error("Discovery review must include the agent configuration source");
|
|
20884
|
-
const reviewedSources = supersedesSettingsProjection(agent, review.sources,
|
|
21225
|
+
const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical8(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical8(configPath), source)) : sources;
|
|
20885
21226
|
const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
|
|
20886
|
-
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")))
|
|
20887
21228
|
return source;
|
|
20888
|
-
const pluginRoot =
|
|
21229
|
+
const pluginRoot = dirname20(dirname20(source.path)), hooksPath = join32(pluginRoot, "hooks/hooks.json");
|
|
20889
21230
|
const hookStat = lstatSync25(hooksPath, { throwIfNoEntry: false });
|
|
20890
21231
|
const text2 = read2(source.path);
|
|
20891
21232
|
if (text2 === null)
|
|
@@ -20900,13 +21241,13 @@ function resolveAgentDiscovery(options) {
|
|
|
20900
21241
|
if (manifest.hooks !== undefined) {
|
|
20901
21242
|
if (typeof manifest.hooks !== "string")
|
|
20902
21243
|
throw new Error("Reviewed Claude plugin manifest has an unsupported hooks target");
|
|
20903
|
-
const declaredHookPath =
|
|
21244
|
+
const declaredHookPath = resolve20(pluginRoot, manifest.hooks);
|
|
20904
21245
|
if (!declaredHookPath.startsWith(pluginRoot + sep8))
|
|
20905
21246
|
throw new Error("Reviewed Claude plugin hooks target escapes its root");
|
|
20906
21247
|
reviewedHookPaths.add(declaredHookPath);
|
|
20907
21248
|
}
|
|
20908
21249
|
for (const hooksFile of reviewedHookPaths)
|
|
20909
|
-
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")))
|
|
20910
21251
|
throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
|
|
20911
21252
|
return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
|
|
20912
21253
|
});
|
|
@@ -20915,7 +21256,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20915
21256
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
|
20916
21257
|
}
|
|
20917
21258
|
function plugin(root2) {
|
|
20918
|
-
root2 =
|
|
21259
|
+
root2 = canonical8(root2);
|
|
20919
21260
|
safe4(root2);
|
|
20920
21261
|
const manifestPath = join32(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
20921
21262
|
const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
|
|
@@ -20938,16 +21279,16 @@ function resolveAgentDiscovery(options) {
|
|
|
20938
21279
|
if (agent === "claude")
|
|
20939
21280
|
roots.add(join32(root2, "skills"));
|
|
20940
21281
|
for (const value of declared) {
|
|
20941
|
-
const path =
|
|
21282
|
+
const path = resolve20(root2, value);
|
|
20942
21283
|
if (path !== root2 && !path.startsWith(root2 + sep8))
|
|
20943
21284
|
unresolved("plugin skill path escapes its root");
|
|
20944
|
-
roots.add(path.endsWith(`${sep8}SKILL.md`) ?
|
|
21285
|
+
roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname20(path) : path);
|
|
20945
21286
|
}
|
|
20946
21287
|
}
|
|
20947
21288
|
if (agent === "claude") {
|
|
20948
21289
|
const registrations = witness(join32(home, ".claude/plugins/installed_plugins.json"));
|
|
20949
21290
|
const installed = registrations === null ? {} : parseConfig(registrations, join32(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
20950
|
-
const commands =
|
|
21291
|
+
const commands = canonical8(join32(home, ".claude/commands"));
|
|
20951
21292
|
safe4(commands);
|
|
20952
21293
|
if (lstatSync25(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
20953
21294
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
@@ -20969,7 +21310,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20969
21310
|
unresolved("unsupported plugin enablement");
|
|
20970
21311
|
const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
|
|
20971
21312
|
const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
20972
|
-
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))
|
|
20973
21314
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
20974
21315
|
const catalogText = witness(join32(market.source, ".agents/plugins/marketplace.json"));
|
|
20975
21316
|
if (catalogText === null)
|
|
@@ -20977,7 +21318,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20977
21318
|
const catalog = parseConfig(catalogText, join32(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
20978
21319
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
20979
21320
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
20980
|
-
plugin(
|
|
21321
|
+
plugin(resolve20(market.source, matches[0].source.path));
|
|
20981
21322
|
}
|
|
20982
21323
|
} else if (agent === "opencode") {
|
|
20983
21324
|
if (witness(join32(home, ".config/opencode/opencode.jsonc")) !== null || witness(join32(home, ".config/opencode/config.json")) !== null)
|
|
@@ -20985,7 +21326,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20985
21326
|
if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
|
|
20986
21327
|
unresolved("external plugin or remote skill source");
|
|
20987
21328
|
for (const path of config.skills?.paths ?? []) {
|
|
20988
|
-
if (typeof path !== "string" || !
|
|
21329
|
+
if (typeof path !== "string" || !isAbsolute21(path))
|
|
20989
21330
|
unresolved("relative or malformed added skill root");
|
|
20990
21331
|
roots.add(path);
|
|
20991
21332
|
}
|
|
@@ -20994,7 +21335,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20994
21335
|
if (lstatSync25(localPlugins, { throwIfNoEntry: false }) && readdirSync10(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
20995
21336
|
unresolved("additional local plugin hooks require review");
|
|
20996
21337
|
} else if (agent === "sumi") {
|
|
20997
|
-
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))
|
|
20998
21339
|
unresolved("external or foreign local plugins require review");
|
|
20999
21340
|
} else if (agent === "hermes") {
|
|
21000
21341
|
for (const directory of [join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")]) {
|
|
@@ -21024,13 +21365,13 @@ function resolveAgentDiscovery(options) {
|
|
|
21024
21365
|
roots.add(join32(root2, "skills"));
|
|
21025
21366
|
}
|
|
21026
21367
|
}
|
|
21027
|
-
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))) } : {} };
|
|
21028
21369
|
}
|
|
21029
21370
|
|
|
21030
21371
|
// src/lib/authoring-path.ts
|
|
21031
21372
|
var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
|
|
21032
21373
|
function pathParts(path) {
|
|
21033
|
-
return
|
|
21374
|
+
return resolve21(path).split(sep9).filter(Boolean);
|
|
21034
21375
|
}
|
|
21035
21376
|
function nativeRootMatch(path) {
|
|
21036
21377
|
const parts = pathParts(path);
|
|
@@ -21044,16 +21385,16 @@ function nativeRootMatch(path) {
|
|
|
21044
21385
|
return;
|
|
21045
21386
|
}
|
|
21046
21387
|
function physicalPath(path) {
|
|
21047
|
-
let cursor =
|
|
21388
|
+
let cursor = resolve21(path);
|
|
21048
21389
|
const suffix = [];
|
|
21049
21390
|
while (!lstatSync26(cursor, { throwIfNoEntry: false })) {
|
|
21050
|
-
suffix.unshift(
|
|
21051
|
-
const parent =
|
|
21391
|
+
suffix.unshift(basename12(cursor));
|
|
21392
|
+
const parent = dirname21(cursor);
|
|
21052
21393
|
if (parent === cursor)
|
|
21053
|
-
return
|
|
21394
|
+
return resolve21(path);
|
|
21054
21395
|
cursor = parent;
|
|
21055
21396
|
}
|
|
21056
|
-
return
|
|
21397
|
+
return resolve21(realpathSync12(cursor), ...suffix);
|
|
21057
21398
|
}
|
|
21058
21399
|
function contains(root2, path) {
|
|
21059
21400
|
return path === root2 || path.startsWith(root2 + sep9);
|
|
@@ -21061,7 +21402,7 @@ function contains(root2, path) {
|
|
|
21061
21402
|
function configuredRoots(home, explicitHome) {
|
|
21062
21403
|
const homeData = join33(home, ".hasna", "skills");
|
|
21063
21404
|
const homePolicy = readManagedSkillPolicySnapshot(homeData);
|
|
21064
|
-
const configuredPolicy = !explicitHome &&
|
|
21405
|
+
const configuredPolicy = !explicitHome && resolve21(getDataDirReadOnly()) !== resolve21(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
|
|
21065
21406
|
const policy = homePolicy ?? configuredPolicy;
|
|
21066
21407
|
const bridge = policy?.value.bridge;
|
|
21067
21408
|
if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
|
|
@@ -21100,22 +21441,22 @@ function configuredRoots(home, explicitHome) {
|
|
|
21100
21441
|
function assertPortableAuthoringPath(path, options = {}) {
|
|
21101
21442
|
if (typeof path !== "string" || !path || path.includes("\x00"))
|
|
21102
21443
|
throw new Error("Invalid portable authoring path");
|
|
21103
|
-
const lexical =
|
|
21444
|
+
const lexical = resolve21(path);
|
|
21104
21445
|
const physical = physicalPath(lexical);
|
|
21105
21446
|
const candidates = [lexical, physical];
|
|
21106
21447
|
let native = candidates.map(nativeRootMatch).find(Boolean);
|
|
21107
|
-
const home =
|
|
21448
|
+
const home = resolve21(options.homeDir ?? homedir6());
|
|
21108
21449
|
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join33(home, ...parts));
|
|
21109
21450
|
defaults.push(join33(home, ".codex/plugins/cache"), join33(home, ".claude/plugins/cache"), join33(home, ".claude/plugins/synced"), join33(home, ".gemini/extensions"));
|
|
21110
21451
|
for (const root2 of defaults) {
|
|
21111
|
-
if (candidates.some((candidate2) => contains(
|
|
21452
|
+
if (candidates.some((candidate2) => contains(resolve21(root2), candidate2) || contains(physicalPath(root2), candidate2)))
|
|
21112
21453
|
native ??= root2;
|
|
21113
21454
|
}
|
|
21114
21455
|
if (native) {
|
|
21115
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.`);
|
|
21116
21457
|
}
|
|
21117
21458
|
for (const root2 of configuredRoots(home, options.homeDir !== undefined)) {
|
|
21118
|
-
if (candidates.some((candidate2) => contains(
|
|
21459
|
+
if (candidates.some((candidate2) => contains(resolve21(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
|
|
21119
21460
|
throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
|
|
21120
21461
|
}
|
|
21121
21462
|
}
|
|
@@ -21263,20 +21604,20 @@ function isSkillCandidate(dir) {
|
|
|
21263
21604
|
return existsSync14(join34(dir, "SKILL.md")) || existsSync14(join34(dir, "skill.json")) || existsSync14(join34(dir, "package.json"));
|
|
21264
21605
|
}
|
|
21265
21606
|
function physicalImportPath(path) {
|
|
21266
|
-
let ancestor =
|
|
21607
|
+
let ancestor = resolve22(path);
|
|
21267
21608
|
const suffix = [];
|
|
21268
21609
|
while (!existsSync14(ancestor)) {
|
|
21269
|
-
suffix.unshift(
|
|
21270
|
-
ancestor =
|
|
21610
|
+
suffix.unshift(basename13(ancestor));
|
|
21611
|
+
ancestor = dirname22(ancestor);
|
|
21271
21612
|
}
|
|
21272
21613
|
return join34(realpathSync13(ancestor), ...suffix);
|
|
21273
21614
|
}
|
|
21274
21615
|
function assertDisjointImportPaths(source, destination) {
|
|
21275
21616
|
const sourcePath = realpathSync13(source);
|
|
21276
21617
|
const destinationPath = physicalImportPath(destination);
|
|
21277
|
-
const contains2 = (parent,
|
|
21278
|
-
const path = relative7(parent,
|
|
21279
|
-
return path === "" || !
|
|
21618
|
+
const contains2 = (parent, child2) => {
|
|
21619
|
+
const path = relative7(parent, child2);
|
|
21620
|
+
return path === "" || !isAbsolute22(path) && path !== ".." && !path.startsWith(`..${sep10}`);
|
|
21280
21621
|
};
|
|
21281
21622
|
if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
|
|
21282
21623
|
throw new Error("Source and destination skill directories must not overlap");
|
|
@@ -21291,8 +21632,8 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
21291
21632
|
const explicitName = options.name != null;
|
|
21292
21633
|
const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
|
|
21293
21634
|
if (isOfficialSkillName(skillName) && !options.allowShadow) {
|
|
21294
|
-
const sourceSlug = safeNormalizeName(
|
|
21295
|
-
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.`;
|
|
21296
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.`);
|
|
21297
21638
|
}
|
|
21298
21639
|
const readOnlyDestination = join34(resolvePortableSkillsRoot(options, false), skillName);
|
|
@@ -21307,7 +21648,7 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
21307
21648
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
21308
21649
|
rmSync4(destination, { recursive: true, force: true });
|
|
21309
21650
|
}
|
|
21310
|
-
mkdirSync10(
|
|
21651
|
+
mkdirSync10(dirname22(destination), { recursive: true });
|
|
21311
21652
|
copySkillDirectory(absoluteSource, destination);
|
|
21312
21653
|
const base = {
|
|
21313
21654
|
...inferred,
|
|
@@ -21557,7 +21898,7 @@ function safeIsDirectory(path) {
|
|
|
21557
21898
|
}
|
|
21558
21899
|
}
|
|
21559
21900
|
function isSafeRelativePath2(value) {
|
|
21560
|
-
if (!value.trim() ||
|
|
21901
|
+
if (!value.trim() || isAbsolute22(value))
|
|
21561
21902
|
return false;
|
|
21562
21903
|
const normalized = normalize4(value).replace(/\\/g, "/");
|
|
21563
21904
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
@@ -21873,7 +22214,7 @@ function getAllTags(catalog) {
|
|
|
21873
22214
|
return Array.from(tagSet).sort();
|
|
21874
22215
|
}
|
|
21875
22216
|
// src/lib/run-state.ts
|
|
21876
|
-
import { createHash as
|
|
22217
|
+
import { createHash as createHash26, randomBytes } from "crypto";
|
|
21877
22218
|
import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync23, readdirSync as readdirSync13, statSync as statSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
21878
22219
|
import { extname, join as join36, relative as relative8 } from "path";
|
|
21879
22220
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
@@ -22000,7 +22341,7 @@ function collectRunArtifacts(context) {
|
|
|
22000
22341
|
artifacts.push({
|
|
22001
22342
|
path: toProjectRelative(context.targetDir, path),
|
|
22002
22343
|
mime: mimeForPath(path),
|
|
22003
|
-
sha256:
|
|
22344
|
+
sha256: createHash26("sha256").update(bytes).digest("hex"),
|
|
22004
22345
|
sizeBytes: stat2.size
|
|
22005
22346
|
});
|
|
22006
22347
|
}
|
|
@@ -25529,14 +25870,14 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
25529
25870
|
const shape = zod.def.shape;
|
|
25530
25871
|
const propValues = {};
|
|
25531
25872
|
for (const key in shape) {
|
|
25532
|
-
const
|
|
25533
|
-
if (
|
|
25873
|
+
const field3 = shape[key]._zod;
|
|
25874
|
+
if (field3.values) {
|
|
25534
25875
|
if (!Object.prototype.hasOwnProperty.call(propValues, key)) {
|
|
25535
25876
|
assignProp(propValues, key, new Set);
|
|
25536
25877
|
}
|
|
25537
|
-
for (const v of
|
|
25878
|
+
for (const v of field3.values)
|
|
25538
25879
|
propValues[key].add(v);
|
|
25539
|
-
if (
|
|
25880
|
+
if (field3.optin !== undefined)
|
|
25540
25881
|
propValues[key].add(undefined);
|
|
25541
25882
|
}
|
|
25542
25883
|
}
|
|
@@ -26949,8 +27290,8 @@ function isRecursive(inst, stack) {
|
|
|
26949
27290
|
return true;
|
|
26950
27291
|
stack.add(inst);
|
|
26951
27292
|
let result = false;
|
|
26952
|
-
const check = (
|
|
26953
|
-
if (!result &&
|
|
27293
|
+
const check = (child2) => {
|
|
27294
|
+
if (!result && child2?._zod && isRecursive(child2, stack))
|
|
26954
27295
|
result = true;
|
|
26955
27296
|
};
|
|
26956
27297
|
const def = inst._zod.def;
|
|
@@ -37729,11 +38070,11 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
37729
38070
|
}
|
|
37730
38071
|
const allKeys = new Set(Object.keys(shape));
|
|
37731
38072
|
const requiredKeys = new Set([...allKeys].filter((key) => {
|
|
37732
|
-
const
|
|
38073
|
+
const field3 = def.shape[key];
|
|
37733
38074
|
if (ctx.io === "input") {
|
|
37734
|
-
return inputOptin(
|
|
38075
|
+
return inputOptin(field3) === undefined;
|
|
37735
38076
|
} else {
|
|
37736
|
-
return
|
|
38077
|
+
return field3._zod.optout === undefined;
|
|
37737
38078
|
}
|
|
37738
38079
|
}));
|
|
37739
38080
|
if (requiredKeys.size > 0) {
|
|
@@ -41419,7 +41760,7 @@ function clone2(value) {
|
|
|
41419
41760
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
41420
41761
|
}
|
|
41421
41762
|
// src/lib/canonical-json.ts
|
|
41422
|
-
import { createHash as
|
|
41763
|
+
import { createHash as createHash27 } from "crypto";
|
|
41423
41764
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
41424
41765
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
41425
41766
|
}
|
|
@@ -41463,7 +41804,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
41463
41804
|
return visit3(value, 0);
|
|
41464
41805
|
}
|
|
41465
41806
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
41466
|
-
return
|
|
41807
|
+
return createHash27("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
41467
41808
|
}
|
|
41468
41809
|
|
|
41469
41810
|
// src/lib/remote-recurring-contract.ts
|
|
@@ -42287,17 +42628,17 @@ function runQuoteReceipt(value) {
|
|
|
42287
42628
|
return value;
|
|
42288
42629
|
}
|
|
42289
42630
|
function parseRemoteRunQuote(value) {
|
|
42290
|
-
const
|
|
42291
|
-
runQuoteReceipt(
|
|
42292
|
-
const pricing = object7(
|
|
42293
|
-
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))
|
|
42294
42635
|
throw new Error("Invalid quoted skill");
|
|
42295
42636
|
const costCents = creditCount(pricing.costCredits ?? pricing.costCents);
|
|
42296
42637
|
if (pricing.costCredits !== undefined && pricing.costCents !== undefined && pricing.costCents !== costCents)
|
|
42297
42638
|
throw new Error("Inconsistent quoted credit count");
|
|
42298
|
-
if (
|
|
42639
|
+
if (quote3.availability && object7(quote3.availability).status !== "available")
|
|
42299
42640
|
throw new Error("This skill is unavailable for remote execution");
|
|
42300
|
-
return { ...
|
|
42641
|
+
return { ...quote3, skill: quote3.skill, pricing: { ...pricing, costCredits: costCents, costCents, formattedCost: `${costCents} credits` } };
|
|
42301
42642
|
}
|
|
42302
42643
|
function parseRemoteCreditPacks(value) {
|
|
42303
42644
|
if (!Array.isArray(value))
|
|
@@ -42372,14 +42713,14 @@ function creditCheckoutFailure(value, status, key) {
|
|
|
42372
42713
|
}
|
|
42373
42714
|
|
|
42374
42715
|
// src/lib/remote-profile.ts
|
|
42375
|
-
function customerNamePatch(input2,
|
|
42376
|
-
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))
|
|
42377
42718
|
throw new Error("Provide only the requested name field.");
|
|
42378
|
-
const value = input2[
|
|
42719
|
+
const value = input2[field3];
|
|
42379
42720
|
if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
|
|
42380
42721
|
throw new Error("Use a name of 1\u2013100 characters without control characters or newlines.");
|
|
42381
42722
|
}
|
|
42382
|
-
return { [
|
|
42723
|
+
return { [field3]: value.trim() };
|
|
42383
42724
|
}
|
|
42384
42725
|
function isRecord5(value) {
|
|
42385
42726
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -42424,10 +42765,10 @@ async function readQuoteUnavailableCode(response) {
|
|
|
42424
42765
|
let timer;
|
|
42425
42766
|
let deadlineExceeded = false;
|
|
42426
42767
|
const expired = Symbol("quote body deadline");
|
|
42427
|
-
const deadline = new Promise((
|
|
42768
|
+
const deadline = new Promise((resolve23) => {
|
|
42428
42769
|
timer = setTimeout(() => {
|
|
42429
42770
|
deadlineExceeded = true;
|
|
42430
|
-
|
|
42771
|
+
resolve23(expired);
|
|
42431
42772
|
reader.cancel().catch(() => {});
|
|
42432
42773
|
}, 1500);
|
|
42433
42774
|
});
|
|
@@ -42774,18 +43115,18 @@ class RemoteSkillsClient {
|
|
|
42774
43115
|
const maximum = creditCount(approval.maxCredits ?? approval.maxCostCents ?? 0);
|
|
42775
43116
|
if (approval.maxCostCents !== undefined && approval.maxCostCents !== maximum)
|
|
42776
43117
|
throw new Error("Credit approval fields disagree");
|
|
42777
|
-
const
|
|
42778
|
-
if (
|
|
42779
|
-
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);
|
|
42780
43121
|
const capabilities = await this.getCapabilities();
|
|
42781
43122
|
if (!capabilities.capabilities.includes("runs.submit") || capabilities.billing?.boundedRunApproval !== true || capabilities.billing.unit !== "credits") {
|
|
42782
43123
|
throw new Error("The configured server does not support bounded credit approval; refusing remote submission");
|
|
42783
43124
|
}
|
|
42784
|
-
return this.submitRun(
|
|
43125
|
+
return this.submitRun(quote3?.skill ?? slug2, input2, args, {
|
|
42785
43126
|
...approval,
|
|
42786
43127
|
maxCredits: maximum,
|
|
42787
43128
|
maxCostCents: maximum,
|
|
42788
|
-
...(
|
|
43129
|
+
...(quote3?.quoteReceipt ?? approval.quoteReceipt) === undefined ? {} : { quoteReceipt: quote3?.quoteReceipt ?? approval.quoteReceipt }
|
|
42789
43130
|
});
|
|
42790
43131
|
}
|
|
42791
43132
|
async getIdentity() {
|
|
@@ -43386,13 +43727,13 @@ class RemoteSkillsClient {
|
|
|
43386
43727
|
return normalizeUpdatedSincePage(await response.json());
|
|
43387
43728
|
}
|
|
43388
43729
|
}
|
|
43389
|
-
function requireOptionalString(record8,
|
|
43390
|
-
if (record8[
|
|
43730
|
+
function requireOptionalString(record8, field3) {
|
|
43731
|
+
if (record8[field3] === undefined)
|
|
43391
43732
|
return;
|
|
43392
|
-
if (typeof record8[
|
|
43393
|
-
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)`);
|
|
43394
43735
|
}
|
|
43395
|
-
return record8[
|
|
43736
|
+
return record8[field3];
|
|
43396
43737
|
}
|
|
43397
43738
|
var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
43398
43739
|
function isVersionRecord(value) {
|
|
@@ -43730,7 +44071,7 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
43730
44071
|
saveSchedules(data, targetDir);
|
|
43731
44072
|
}
|
|
43732
44073
|
// src/lib/revision.ts
|
|
43733
|
-
import { createHash as
|
|
44074
|
+
import { createHash as createHash28 } from "crypto";
|
|
43734
44075
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
43735
44076
|
function revisionIdOf(content) {
|
|
43736
44077
|
const canonicalContent = {
|
|
@@ -43751,15 +44092,15 @@ function revisionIdOf(content) {
|
|
|
43751
44092
|
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
43752
44093
|
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
43753
44094
|
}
|
|
43754
|
-
const
|
|
43755
|
-
return
|
|
44095
|
+
const canonical8 = JSON.stringify(canonicalContent);
|
|
44096
|
+
return createHash28("sha256").update(canonical8).digest("hex");
|
|
43756
44097
|
}
|
|
43757
44098
|
function revisionIdOfRecord(record8) {
|
|
43758
44099
|
return revisionIdOf(record8);
|
|
43759
44100
|
}
|
|
43760
44101
|
// src/lib/pull.ts
|
|
43761
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";
|
|
43762
|
-
import { dirname as
|
|
44103
|
+
import { dirname as dirname23, join as join38 } from "path";
|
|
43763
44104
|
|
|
43764
44105
|
// src/lib/skill-bundles.ts
|
|
43765
44106
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -44101,7 +44442,7 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
44101
44442
|
try {
|
|
44102
44443
|
for (const entry of entries) {
|
|
44103
44444
|
const destination = join38(staging, entry.path);
|
|
44104
|
-
mkdirSync13(
|
|
44445
|
+
mkdirSync13(dirname23(destination), { recursive: true });
|
|
44105
44446
|
writeFileSync12(destination, entry.bytes, { mode: entry.mode });
|
|
44106
44447
|
}
|
|
44107
44448
|
writePullMarker(staging, {
|
|
@@ -44195,7 +44536,7 @@ function splitNameVersion(raw) {
|
|
|
44195
44536
|
return version2 ? { name, version: version2 } : { name };
|
|
44196
44537
|
}
|
|
44197
44538
|
// src/lib/recurring-surface.ts
|
|
44198
|
-
import { isAbsolute as
|
|
44539
|
+
import { isAbsolute as isAbsolute23 } from "path";
|
|
44199
44540
|
|
|
44200
44541
|
// src/lib/remote-invitation-recovery.ts
|
|
44201
44542
|
class InvitationEmailInputError extends Error {
|
|
@@ -44293,7 +44634,7 @@ async function requestInvitationEmail(origin, action, input2) {
|
|
|
44293
44634
|
}
|
|
44294
44635
|
|
|
44295
44636
|
// src/lib/remote-private-publications.ts
|
|
44296
|
-
import { createHash as
|
|
44637
|
+
import { createHash as createHash29 } from "crypto";
|
|
44297
44638
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
44298
44639
|
var failures2 = {
|
|
44299
44640
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -44340,7 +44681,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
|
44340
44681
|
var record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
44341
44682
|
var exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
|
|
44342
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));
|
|
44343
|
-
var publicationSha256 = (bytes) =>
|
|
44684
|
+
var publicationSha256 = (bytes) => createHash29("sha256").update(bytes).digest("hex");
|
|
44344
44685
|
function checkedPublicationDeclaration(value) {
|
|
44345
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)
|
|
44346
44687
|
return bad();
|
|
@@ -44585,12 +44926,12 @@ class RemotePrivatePublicationsClient {
|
|
|
44585
44926
|
previous = view;
|
|
44586
44927
|
if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
|
|
44587
44928
|
return view;
|
|
44588
|
-
await new Promise((
|
|
44929
|
+
await new Promise((resolve23) => {
|
|
44589
44930
|
const timer = setTimeout(done, Math.min(1000, until - Date.now()));
|
|
44590
44931
|
function done() {
|
|
44591
44932
|
clearTimeout(timer);
|
|
44592
44933
|
options.signal?.removeEventListener("abort", done);
|
|
44593
|
-
|
|
44934
|
+
resolve23();
|
|
44594
44935
|
}
|
|
44595
44936
|
options.signal?.addEventListener("abort", done, { once: true });
|
|
44596
44937
|
});
|
|
@@ -44854,7 +45195,7 @@ var stateSchema = exports_external.object({
|
|
|
44854
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}$/);
|
|
44855
45196
|
var context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
|
|
44856
45197
|
var page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
|
|
44857
|
-
var directory = exports_external.string().max(4096).refine(
|
|
45198
|
+
var directory = exports_external.string().max(4096).refine(isAbsolute23, "Use an absolute recovery directory");
|
|
44858
45199
|
var email4 = exports_external.string().trim().toLowerCase().email().max(254);
|
|
44859
45200
|
var code = exports_external.string().regex(/^\d{6}$/);
|
|
44860
45201
|
var recurringSurfaceSchemas = {
|
|
@@ -45005,7 +45346,7 @@ function findSkillsParityForMcpTool(tool) {
|
|
|
45005
45346
|
}
|
|
45006
45347
|
// src/lib/registry-sync.ts
|
|
45007
45348
|
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
|
|
45008
|
-
import { dirname as
|
|
45349
|
+
import { dirname as dirname24, relative as relative9 } from "path";
|
|
45009
45350
|
function createRegistrySyncArtifact(options = {}) {
|
|
45010
45351
|
const profile = options.profile ?? "all";
|
|
45011
45352
|
const includeDocs = options.includeDocs ?? true;
|
|
@@ -45062,7 +45403,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
45062
45403
|
};
|
|
45063
45404
|
}
|
|
45064
45405
|
function writeRegistrySyncArtifact(path, artifact) {
|
|
45065
|
-
mkdirSync14(
|
|
45406
|
+
mkdirSync14(dirname24(path), { recursive: true });
|
|
45066
45407
|
writeFileSync13(path, `${JSON.stringify(artifact, null, 2)}
|
|
45067
45408
|
`);
|
|
45068
45409
|
}
|
|
@@ -46147,14 +46488,14 @@ function clone3(value) {
|
|
|
46147
46488
|
}
|
|
46148
46489
|
// src/lib/feedback.ts
|
|
46149
46490
|
import { appendFileSync, existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
|
|
46150
|
-
import { dirname as
|
|
46491
|
+
import { dirname as dirname25, join as join39 } from "path";
|
|
46151
46492
|
import { Database } from "bun:sqlite";
|
|
46152
46493
|
function getFeedbackDbPath() {
|
|
46153
46494
|
return join39(getDataDir(), "skills.db");
|
|
46154
46495
|
}
|
|
46155
46496
|
function getFeedbackDb() {
|
|
46156
46497
|
const dbPath = getFeedbackDbPath();
|
|
46157
|
-
const dir =
|
|
46498
|
+
const dir = dirname25(dbPath);
|
|
46158
46499
|
if (!existsSync19(dir))
|
|
46159
46500
|
mkdirSync15(dir, { recursive: true });
|
|
46160
46501
|
const db = new Database(dbPath);
|
|
@@ -46183,7 +46524,7 @@ function saveFeedback(input2) {
|
|
|
46183
46524
|
const category = input2.category ?? "general";
|
|
46184
46525
|
if (isApiMode()) {
|
|
46185
46526
|
const path = join39(getDataDir(), "feedback.jsonl");
|
|
46186
|
-
const dir =
|
|
46527
|
+
const dir = dirname25(path);
|
|
46187
46528
|
if (!existsSync19(dir))
|
|
46188
46529
|
mkdirSync15(dir, { recursive: true });
|
|
46189
46530
|
appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
@@ -46206,7 +46547,7 @@ function isApiMode(env = process.env) {
|
|
|
46206
46547
|
}
|
|
46207
46548
|
}
|
|
46208
46549
|
// src/lib/native-storage.ts
|
|
46209
|
-
import { createHash as
|
|
46550
|
+
import { createHash as createHash30, createHmac as createHmac2 } from "crypto";
|
|
46210
46551
|
import {
|
|
46211
46552
|
existsSync as existsSync20,
|
|
46212
46553
|
mkdirSync as mkdirSync16,
|
|
@@ -46215,7 +46556,7 @@ import {
|
|
|
46215
46556
|
statSync as statSync13,
|
|
46216
46557
|
writeFileSync as writeFileSync14
|
|
46217
46558
|
} from "fs";
|
|
46218
|
-
import { dirname as
|
|
46559
|
+
import { dirname as dirname26, join as join40, normalize as normalize5, relative as relative10, sep as sep11 } from "path";
|
|
46219
46560
|
var SKILLS_STORAGE_TABLES = [
|
|
46220
46561
|
"skills_sync_records",
|
|
46221
46562
|
"skills_sync_cursors"
|
|
@@ -46382,7 +46723,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
46382
46723
|
files.push({
|
|
46383
46724
|
path: relativePath,
|
|
46384
46725
|
sizeBytes: bytes.byteLength,
|
|
46385
|
-
sha256:
|
|
46726
|
+
sha256: createHash30("sha256").update(bytes).digest("hex"),
|
|
46386
46727
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
46387
46728
|
});
|
|
46388
46729
|
}
|
|
@@ -46407,11 +46748,11 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
|
|
|
46407
46748
|
continue;
|
|
46408
46749
|
}
|
|
46409
46750
|
const bytes = Buffer.from(file2.contentBase64, "base64");
|
|
46410
|
-
const hash4 =
|
|
46751
|
+
const hash4 = createHash30("sha256").update(bytes).digest("hex");
|
|
46411
46752
|
if (hash4 !== file2.sha256) {
|
|
46412
46753
|
throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
|
|
46413
46754
|
}
|
|
46414
|
-
mkdirSync16(
|
|
46755
|
+
mkdirSync16(dirname26(absolutePath4), { recursive: true });
|
|
46415
46756
|
writeFileSync14(absolutePath4, bytes);
|
|
46416
46757
|
written += 1;
|
|
46417
46758
|
}
|
|
@@ -46739,7 +47080,7 @@ function toIsoString(value) {
|
|
|
46739
47080
|
return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
|
|
46740
47081
|
}
|
|
46741
47082
|
function sha256Hex2(value) {
|
|
46742
|
-
return
|
|
47083
|
+
return createHash30("sha256").update(value).digest("hex");
|
|
46743
47084
|
}
|
|
46744
47085
|
function normalizeHeaders(headers) {
|
|
46745
47086
|
const result = {};
|
|
@@ -46791,7 +47132,7 @@ function toArrayBuffer(bytes) {
|
|
|
46791
47132
|
return buffer;
|
|
46792
47133
|
}
|
|
46793
47134
|
// src/lib/station-snapshot.ts
|
|
46794
|
-
import { createHash as
|
|
47135
|
+
import { createHash as createHash31 } from "crypto";
|
|
46795
47136
|
import {
|
|
46796
47137
|
copyFileSync,
|
|
46797
47138
|
mkdirSync as mkdirSync17,
|
|
@@ -46799,7 +47140,7 @@ import {
|
|
|
46799
47140
|
statSync as statSync15,
|
|
46800
47141
|
writeFileSync as writeFileSync15
|
|
46801
47142
|
} from "fs";
|
|
46802
|
-
import { dirname as
|
|
47143
|
+
import { dirname as dirname27, isAbsolute as isAbsolute24, relative as relative11, resolve as resolve23, sep as sep13 } from "path";
|
|
46803
47144
|
|
|
46804
47145
|
// src/lib/portable-snapshot-filter.ts
|
|
46805
47146
|
import { readdirSync as readdirSync15, statSync as statSync14 } from "fs";
|
|
@@ -46966,7 +47307,7 @@ function validateStationId(stationId) {
|
|
|
46966
47307
|
}
|
|
46967
47308
|
}
|
|
46968
47309
|
function sha256File(filePath) {
|
|
46969
|
-
return
|
|
47310
|
+
return createHash31("sha256").update(readFileSync28(filePath)).digest("hex");
|
|
46970
47311
|
}
|
|
46971
47312
|
function scanHome(definition, homesRoot) {
|
|
46972
47313
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -47037,7 +47378,7 @@ function humanHomes(scanned) {
|
|
|
47037
47378
|
}));
|
|
47038
47379
|
}
|
|
47039
47380
|
function writeStationSnapshot(options) {
|
|
47040
|
-
const repoRoot =
|
|
47381
|
+
const repoRoot = resolve23(options.repoRoot ?? process.cwd());
|
|
47041
47382
|
const { scanned, plans, totalBytes } = planStationSnapshot(options);
|
|
47042
47383
|
const manifestFiles = plans.map((plan) => ({
|
|
47043
47384
|
relativePath: plan.source.relativePath,
|
|
@@ -47063,9 +47404,9 @@ function writeStationSnapshot(options) {
|
|
|
47063
47404
|
const conflicts = [];
|
|
47064
47405
|
const untouched = [];
|
|
47065
47406
|
for (const plan of plans) {
|
|
47066
|
-
const destination =
|
|
47407
|
+
const destination = resolve23(repoRoot, plan.destination);
|
|
47067
47408
|
const destinationRelative = relative11(repoRoot, destination);
|
|
47068
|
-
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) ||
|
|
47409
|
+
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) || isAbsolute24(destinationRelative)) {
|
|
47069
47410
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
47070
47411
|
}
|
|
47071
47412
|
let existingDigest = null;
|
|
@@ -47086,8 +47427,8 @@ function writeStationSnapshot(options) {
|
|
|
47086
47427
|
}
|
|
47087
47428
|
let written = 0;
|
|
47088
47429
|
for (const plan of untouched) {
|
|
47089
|
-
const destination =
|
|
47090
|
-
mkdirSync17(
|
|
47430
|
+
const destination = resolve23(repoRoot, plan.destination);
|
|
47431
|
+
mkdirSync17(dirname27(destination), { recursive: true });
|
|
47091
47432
|
copyFileSync(plan.source.fullPath, destination);
|
|
47092
47433
|
written += 1;
|
|
47093
47434
|
}
|
|
@@ -47105,8 +47446,8 @@ function writeStationSnapshot(options) {
|
|
|
47105
47446
|
},
|
|
47106
47447
|
files: manifestFiles
|
|
47107
47448
|
};
|
|
47108
|
-
const manifestPath =
|
|
47109
|
-
mkdirSync17(
|
|
47449
|
+
const manifestPath = resolve23(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
|
|
47450
|
+
mkdirSync17(dirname27(manifestPath), { recursive: true });
|
|
47110
47451
|
writeFileSync15(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
47111
47452
|
`);
|
|
47112
47453
|
return {
|
|
@@ -47117,7 +47458,7 @@ function writeStationSnapshot(options) {
|
|
|
47117
47458
|
};
|
|
47118
47459
|
}
|
|
47119
47460
|
// src/lib/station-hydrate.ts
|
|
47120
|
-
import { createHash as
|
|
47461
|
+
import { createHash as createHash32 } from "crypto";
|
|
47121
47462
|
import {
|
|
47122
47463
|
copyFileSync as copyFileSync2,
|
|
47123
47464
|
mkdirSync as mkdirSync18,
|
|
@@ -47126,7 +47467,7 @@ import {
|
|
|
47126
47467
|
statSync as statSync16,
|
|
47127
47468
|
writeFileSync as writeFileSync16
|
|
47128
47469
|
} from "fs";
|
|
47129
|
-
import { dirname as
|
|
47470
|
+
import { dirname as dirname28, join as join42, resolve as resolve24, sep as sep14 } from "path";
|
|
47130
47471
|
var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
|
|
47131
47472
|
var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
|
|
47132
47473
|
var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
|
|
@@ -47323,12 +47664,12 @@ function skillSha256(skill) {
|
|
|
47323
47664
|
return sha256File(skill.files[0].winner.fullPath);
|
|
47324
47665
|
}
|
|
47325
47666
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
47326
|
-
return
|
|
47667
|
+
return createHash32("sha256").update(joined.sort().join(`
|
|
47327
47668
|
`)).digest("hex");
|
|
47328
47669
|
}
|
|
47329
47670
|
function writeStationHydration(options) {
|
|
47330
|
-
const repoRoot =
|
|
47331
|
-
const cacheRoot =
|
|
47671
|
+
const repoRoot = resolve24(options.repoRoot ?? process.cwd());
|
|
47672
|
+
const cacheRoot = resolve24(options.cacheRoot ?? resolveCorpusRoot());
|
|
47332
47673
|
const plan = planStationHydration(options.stationId, repoRoot);
|
|
47333
47674
|
const resultSkills = plan.winners.map((skill) => ({
|
|
47334
47675
|
ident: skill.ident,
|
|
@@ -47382,7 +47723,7 @@ function writeStationHydration(options) {
|
|
|
47382
47723
|
}
|
|
47383
47724
|
let written = 0;
|
|
47384
47725
|
for (const entry of toWrite) {
|
|
47385
|
-
mkdirSync18(
|
|
47726
|
+
mkdirSync18(dirname28(entry.destination), { recursive: true });
|
|
47386
47727
|
copyFileSync2(entry.fullPath, entry.destination);
|
|
47387
47728
|
written += 1;
|
|
47388
47729
|
}
|
|
@@ -47403,8 +47744,8 @@ function writeStationHydration(options) {
|
|
|
47403
47744
|
},
|
|
47404
47745
|
skills: resultSkills
|
|
47405
47746
|
};
|
|
47406
|
-
const hydrationManifestPath = join42(
|
|
47407
|
-
mkdirSync18(
|
|
47747
|
+
const hydrationManifestPath = join42(dirname28(cacheRoot), `hydration-${options.stationId}.json`);
|
|
47748
|
+
mkdirSync18(dirname28(hydrationManifestPath), { recursive: true });
|
|
47408
47749
|
writeFileSync16(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
47409
47750
|
`);
|
|
47410
47751
|
return {
|
|
@@ -47416,19 +47757,19 @@ function writeStationHydration(options) {
|
|
|
47416
47757
|
}
|
|
47417
47758
|
// src/lib/private-publication-recovery.ts
|
|
47418
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";
|
|
47419
|
-
import { dirname as
|
|
47760
|
+
import { dirname as dirname29, isAbsolute as isAbsolute25, join as join43, resolve as resolve25 } from "path";
|
|
47420
47761
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
47421
47762
|
var fail3 = () => {
|
|
47422
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.");
|
|
47423
47764
|
};
|
|
47424
47765
|
function safeDirectory(directory2) {
|
|
47425
|
-
if (!
|
|
47766
|
+
if (!isAbsolute25(directory2) || directory2 !== resolve25(directory2) || realpathSync14(directory2) !== directory2)
|
|
47426
47767
|
return fail3();
|
|
47427
|
-
for (let path = directory2;; path =
|
|
47768
|
+
for (let path = directory2;; path = dirname29(path)) {
|
|
47428
47769
|
const stat2 = lstatSync27(path);
|
|
47429
47770
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
47430
47771
|
return fail3();
|
|
47431
|
-
if (path ===
|
|
47772
|
+
if (path === dirname29(path))
|
|
47432
47773
|
break;
|
|
47433
47774
|
}
|
|
47434
47775
|
const own2 = lstatSync27(directory2);
|
|
@@ -47560,11 +47901,11 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
|
|
|
47560
47901
|
phase: "prepared",
|
|
47561
47902
|
intent: null
|
|
47562
47903
|
};
|
|
47563
|
-
if (!
|
|
47904
|
+
if (!isAbsolute25(recoveryDirectory) || resolve25(recoveryDirectory) !== recoveryDirectory || realpathSync14(dirname29(recoveryDirectory)) !== dirname29(recoveryDirectory))
|
|
47564
47905
|
return fail3();
|
|
47565
47906
|
mkdirSync19(recoveryDirectory, { mode: 448 });
|
|
47566
47907
|
safeDirectory(recoveryDirectory);
|
|
47567
|
-
const parent = openSync15(
|
|
47908
|
+
const parent = openSync15(dirname29(recoveryDirectory), constants15.O_RDONLY | constants15.O_NOFOLLOW);
|
|
47568
47909
|
try {
|
|
47569
47910
|
fsyncSync7(parent);
|
|
47570
47911
|
} finally {
|
|
@@ -47651,8 +47992,8 @@ async function inspectLocked(client2, directory2, cancel) {
|
|
|
47651
47992
|
return privatePublicationResult(directory2, receipt);
|
|
47652
47993
|
}
|
|
47653
47994
|
// src/lib/skill-context.ts
|
|
47654
|
-
import { createHash as
|
|
47655
|
-
import { resolve as
|
|
47995
|
+
import { createHash as createHash33 } from "crypto";
|
|
47996
|
+
import { resolve as resolve26 } from "path";
|
|
47656
47997
|
function words(value) {
|
|
47657
47998
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
47658
47999
|
}
|
|
@@ -47691,7 +48032,7 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
47691
48032
|
const promptWords = words(prompt);
|
|
47692
48033
|
const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
|
|
47693
48034
|
const loaded = new Set(resolved.session?.loaded ?? []);
|
|
47694
|
-
const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd,
|
|
48035
|
+
const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve26(input2.cwd)] : []];
|
|
47695
48036
|
const candidates = profile.selections.map((selection) => {
|
|
47696
48037
|
const key = selectionKey(selection);
|
|
47697
48038
|
if (input2.restore && loaded.has(key))
|
|
@@ -47718,7 +48059,7 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
47718
48059
|
const omitted = [];
|
|
47719
48060
|
let chars = 0;
|
|
47720
48061
|
for (const candidate2 of candidates) {
|
|
47721
|
-
const { selection, reason } = candidate2;
|
|
48062
|
+
const { selection, reason: reason2 } = candidate2;
|
|
47722
48063
|
if (!input2.restore && !explicit.has(selectionKey(selection)) && loaded.has(selectionKey(selection)))
|
|
47723
48064
|
continue;
|
|
47724
48065
|
const loadCommand = `skills load ${selection.slug}@${selection.version} --selection-profile ${profile.profileId}`;
|
|
@@ -47728,7 +48069,7 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
47728
48069
|
}
|
|
47729
48070
|
const entries = await readSelectedEntries(selection, resolved, resolverOptions);
|
|
47730
48071
|
const { content } = readSelectedDocument(entries);
|
|
47731
|
-
const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${
|
|
48072
|
+
const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${reason2})
|
|
47732
48073
|
${content}`;
|
|
47733
48074
|
if (maxChars !== undefined && chars + section.length + (sections.length ? 2 : 0) > maxChars) {
|
|
47734
48075
|
omitted.push({ slug: selection.slug, version: selection.version, reason: "context-budget", loadCommand });
|
|
@@ -47736,7 +48077,7 @@ ${content}`;
|
|
|
47736
48077
|
}
|
|
47737
48078
|
chars += section.length + (sections.length ? 2 : 0);
|
|
47738
48079
|
sections.push(section);
|
|
47739
|
-
selections.push({ ...selection, reason });
|
|
48080
|
+
selections.push({ ...selection, reason: reason2 });
|
|
47740
48081
|
loaded.add(selectionKey(selection));
|
|
47741
48082
|
}
|
|
47742
48083
|
const context2 = sections.join(`
|
|
@@ -47744,7 +48085,7 @@ ${content}`;
|
|
|
47744
48085
|
`);
|
|
47745
48086
|
const receipt = {
|
|
47746
48087
|
schemaVersion: 1,
|
|
47747
|
-
id:
|
|
48088
|
+
id: createHash33("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
47748
48089
|
profileId: profile.profileId,
|
|
47749
48090
|
profileRevision: profile.profileRevision,
|
|
47750
48091
|
authority: profile.authority,
|
|
@@ -47760,9 +48101,9 @@ ${content}`;
|
|
|
47760
48101
|
return { context: context2, selections, omitted, receipt };
|
|
47761
48102
|
}
|
|
47762
48103
|
// src/lib/session-reconciliation.ts
|
|
47763
|
-
import { createHash as
|
|
48104
|
+
import { createHash as createHash34 } from "crypto";
|
|
47764
48105
|
var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
|
|
47765
|
-
var digest3 = (value) =>
|
|
48106
|
+
var digest3 = (value) => createHash34("sha256").update(JSON.stringify(value)).digest("hex");
|
|
47766
48107
|
function exactTimestamp(value) {
|
|
47767
48108
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
47768
48109
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -47795,6 +48136,8 @@ function inspectSkillSession(sessionId, options = {}) {
|
|
|
47795
48136
|
loadedCount: receipt.loaded.length
|
|
47796
48137
|
};
|
|
47797
48138
|
}
|
|
48139
|
+
var SESSION_RENEWAL_REFUSAL_TTL_MS = 5 * 60 * 1000;
|
|
48140
|
+
var SESSION_RENEWAL_REFUSAL_MAX_BYTES = 4 * 1024;
|
|
47798
48141
|
async function reconcileSkillSession(input2, options = {}) {
|
|
47799
48142
|
if (![input2.fromProfile, input2.fromRevision, input2.selectionProfile, input2.profileRevision].every((value) => typeof value === "string" && value.trim()) || !/^[a-f0-9]{64}$/.test(input2.receiptSha256)) {
|
|
47800
48143
|
throw new SkillSelectionError("INVALID_SESSION_RECONCILIATION", "Name the exact old receipt SHA256, old profile/revision and intended target profile/revision.");
|
|
@@ -47982,6 +48325,7 @@ export {
|
|
|
47982
48325
|
isPortableWithinSkill,
|
|
47983
48326
|
isGatewayBackedSkill,
|
|
47984
48327
|
isExcludedSkillFileName,
|
|
48328
|
+
isDiscoveryRootUnresolved,
|
|
47985
48329
|
isCodexNativeSkillDisabled,
|
|
47986
48330
|
isBasicSkillName,
|
|
47987
48331
|
inventoryNativeSkills,
|
|
@@ -47994,6 +48338,7 @@ export {
|
|
|
47994
48338
|
inspectPrivatePublication,
|
|
47995
48339
|
importSkillsLocalSnapshot,
|
|
47996
48340
|
homePathFor,
|
|
48341
|
+
hashClaudeMarketplaceEntry,
|
|
47997
48342
|
getToolPrimitive,
|
|
47998
48343
|
getStorageStatus,
|
|
47999
48344
|
getStorageDatabaseUrl,
|
|
@@ -48078,6 +48423,7 @@ export {
|
|
|
48078
48423
|
captureClaudeSettings,
|
|
48079
48424
|
captureClaudeMarketplaceRegistryV2,
|
|
48080
48425
|
captureClaudeMarketplaceRegistry,
|
|
48426
|
+
captureClaudeMarketplaceEntry,
|
|
48081
48427
|
canonicalizeManifest,
|
|
48082
48428
|
buildSkillsS3ObjectUrl,
|
|
48083
48429
|
buildSkillsApiUrl,
|
|
@@ -48159,9 +48505,11 @@ export {
|
|
|
48159
48505
|
InvitationEmailInputError,
|
|
48160
48506
|
HttpProfileClient,
|
|
48161
48507
|
HostedApiError,
|
|
48508
|
+
DISCOVERY_ROOT_UNRESOLVED,
|
|
48162
48509
|
DEFAULT_EXPORT_DIR,
|
|
48163
48510
|
ContentHashInputError,
|
|
48164
48511
|
CONTENT_HASH_LIMITS,
|
|
48512
|
+
CLAUDE_MARKETPLACE_ENTRY_HASH_MODE,
|
|
48165
48513
|
CLAUDE_COORDINATED_HOOK_EVENTS,
|
|
48166
48514
|
CATEGORIES,
|
|
48167
48515
|
BASIC_SKILL_NAMES,
|