@hasna/skills 0.10.23 → 0.10.25
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 +23 -2
- package/bin/index.js +375 -53
- package/bin/mcp.js +426 -312
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +367 -50
- package/dist/lib/agent-discovery-directories.d.ts +2 -2
- package/dist/lib/agent-discovery.d.ts +7 -0
- package/dist/lib/agent-integration.d.ts +1 -0
- package/dist/lib/codex-native-skill-catalog.d.ts +9 -1
- package/dist/lib/codex-plugin-skill-controls.d.ts +28 -1
- package/dist/sdk/index.js +367 -50
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9051,6 +9051,18 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9051
9051
|
if (control.appSha256 !== undefined)
|
|
9052
9052
|
requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
|
|
9053
9053
|
}
|
|
9054
|
+
if (bridge.codexInactivePlugins !== undefined) {
|
|
9055
|
+
const controls = array(bridge.codexInactivePlugins, 4096), ids = new Set;
|
|
9056
|
+
requireBound(controls.length === 0 || object(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === "codex-cli 0.160.0" && typeof bridge.codexPluginSkillReview.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(bridge.codexPluginSkillReview.catalogSha256));
|
|
9057
|
+
for (const control of controls) {
|
|
9058
|
+
requireBound(object(control) && Object.keys(control).every((key) => ["pluginId", "namespace", "pluginParent", "sourceType", "sourceSha256"].includes(key)));
|
|
9059
|
+
text(control.pluginId, 1024);
|
|
9060
|
+
text(control.namespace, 64);
|
|
9061
|
+
text(control.pluginParent);
|
|
9062
|
+
requireBound(!ids.has(control.pluginId) && isAbsolute2(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
|
|
9063
|
+
ids.add(control.pluginId);
|
|
9064
|
+
}
|
|
9065
|
+
}
|
|
9054
9066
|
if (bridge.disabledBuiltins !== undefined)
|
|
9055
9067
|
for (const builtin of array(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
|
|
9056
9068
|
requireBound(object(builtin));
|
|
@@ -9063,6 +9075,25 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9063
9075
|
text(agent, 128);
|
|
9064
9076
|
requireBound(object(value));
|
|
9065
9077
|
text(value.agent, 128);
|
|
9078
|
+
if (value.codexInstallationInputs !== undefined) {
|
|
9079
|
+
const proof = record(value.codexInstallationInputs, 4), inputs = array(proof.plugins, 4096), ids = new Set;
|
|
9080
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && proof.version === "codex-cli 0.160.0" && 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);
|
|
9081
|
+
for (const input of inputs) {
|
|
9082
|
+
requireBound(object(input) && Object.keys(input).every((key) => ["pluginId", "namespace", "pluginParent", "sourceRoot", "sourceSha256"].includes(key)));
|
|
9083
|
+
text(input.pluginId, 1024);
|
|
9084
|
+
text(input.namespace, 64);
|
|
9085
|
+
text(input.pluginParent);
|
|
9086
|
+
text(input.sourceRoot);
|
|
9087
|
+
requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute2(path) && resolve2(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
|
|
9088
|
+
ids.add(input.pluginId);
|
|
9089
|
+
}
|
|
9090
|
+
if (proof.directories !== undefined)
|
|
9091
|
+
for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
9092
|
+
requireBound(object(directory));
|
|
9093
|
+
text(directory.path);
|
|
9094
|
+
requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
|
|
9095
|
+
}
|
|
9096
|
+
}
|
|
9066
9097
|
for (const root of array(value.roots, AGENT_POLICY_LIMITS.discoveryRoots))
|
|
9067
9098
|
text(root);
|
|
9068
9099
|
for (const source of array(value.sources, AGENT_POLICY_LIMITS.discoverySources)) {
|
|
@@ -9384,7 +9415,7 @@ function safe(path) {
|
|
|
9384
9415
|
function same(before, after) {
|
|
9385
9416
|
return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
|
|
9386
9417
|
}
|
|
9387
|
-
function capture(path, budget) {
|
|
9418
|
+
function capture(path, budget, excludedRoots) {
|
|
9388
9419
|
safe(path);
|
|
9389
9420
|
const root = lstatSync4(path, { throwIfNoEntry: false });
|
|
9390
9421
|
if (!root)
|
|
@@ -9413,7 +9444,7 @@ function capture(path, budget) {
|
|
|
9413
9444
|
budget.bytes += Buffer.byteLength(row);
|
|
9414
9445
|
need(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
|
|
9415
9446
|
hash.update(row);
|
|
9416
|
-
if (stat.isDirectory())
|
|
9447
|
+
if (stat.isDirectory() && !excludedRoots.includes(child))
|
|
9417
9448
|
walk(child, childRelative, stat, depth + 1);
|
|
9418
9449
|
}
|
|
9419
9450
|
need(same(before, lstatSync4(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
|
|
@@ -9422,16 +9453,18 @@ function capture(path, budget) {
|
|
|
9422
9453
|
safe(path);
|
|
9423
9454
|
return { path, sha256: hash.digest("hex") };
|
|
9424
9455
|
}
|
|
9425
|
-
function captureDiscoveryDirectories(paths) {
|
|
9456
|
+
function captureDiscoveryDirectories(paths, excludedRoots = []) {
|
|
9426
9457
|
need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
|
|
9427
9458
|
const budget = { entries: 0, bytes: 0 };
|
|
9428
|
-
|
|
9459
|
+
for (const root of excludedRoots)
|
|
9460
|
+
safe(root);
|
|
9461
|
+
return paths.map((path) => capture(path, budget, excludedRoots));
|
|
9429
9462
|
}
|
|
9430
|
-
function verifyDiscoveryDirectories(directories) {
|
|
9463
|
+
function verifyDiscoveryDirectories(directories, excludedRoots = []) {
|
|
9431
9464
|
need(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
|
|
9432
9465
|
for (const directory of directories)
|
|
9433
9466
|
need(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
|
|
9434
|
-
const current = captureDiscoveryDirectories(directories.map((directory) => directory.path));
|
|
9467
|
+
const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
|
|
9435
9468
|
for (let i = 0;i < directories.length; i++)
|
|
9436
9469
|
need(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
|
|
9437
9470
|
}
|
|
@@ -10881,7 +10914,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
10881
10914
|
// package.json
|
|
10882
10915
|
var package_default = {
|
|
10883
10916
|
name: "@hasna/skills",
|
|
10884
|
-
version: "0.10.
|
|
10917
|
+
version: "0.10.25",
|
|
10885
10918
|
description: "Skills library for AI coding agents",
|
|
10886
10919
|
type: "module",
|
|
10887
10920
|
bin: {
|
|
@@ -13197,6 +13230,10 @@ async function connectCodexHookRpc(options) {
|
|
|
13197
13230
|
}
|
|
13198
13231
|
|
|
13199
13232
|
// src/lib/codex-native-skill-catalog.ts
|
|
13233
|
+
function codexPluginSourceIsConfigControlled(plugin) {
|
|
13234
|
+
const remoteRealms = ["openai-curated-remote", "created-by-me-remote", "workspace-directory", "workspace-shared-with-me", "workspace-shared-with-me-private", "workspace-shared-with-me-unlisted"];
|
|
13235
|
+
return Boolean(plugin.sourceType && ["local", "git", "npm"].includes(plugin.sourceType) && plugin.sourceSha256 && /^[a-f0-9]{64}$/.test(plugin.sourceSha256) && !plugin.remotePluginId && !remoteRealms.includes(plugin.id.slice(plugin.id.lastIndexOf("@") + 1)));
|
|
13236
|
+
}
|
|
13200
13237
|
var MAX_SKILLS = 4096;
|
|
13201
13238
|
var MAX_NAME_BYTES = 1024;
|
|
13202
13239
|
var MAX_PATH_BYTES = 16384;
|
|
@@ -13208,6 +13245,15 @@ var object4 = (value) => value !== null && typeof value === "object" && !Array.i
|
|
|
13208
13245
|
var scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value);
|
|
13209
13246
|
var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
|
|
13210
13247
|
var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute10(value);
|
|
13248
|
+
function projectPluginSource(source) {
|
|
13249
|
+
if (!object4(source))
|
|
13250
|
+
return {};
|
|
13251
|
+
if (source.type === "remote")
|
|
13252
|
+
return { sourceType: "remote" };
|
|
13253
|
+
const optionalText = (value) => value === undefined || value === null || scalar(value, MAX_PATH_BYTES);
|
|
13254
|
+
const valid = source.type === "local" ? Object.keys(source).every((key) => ["type", "path"].includes(key)) && absolutePath2(source.path) : source.type === "git" ? Object.keys(source).every((key) => ["type", "url", "path", "refName", "sha"].includes(key)) && scalar(source.url, MAX_PATH_BYTES) && [source.path, source.refName, source.sha].every(optionalText) : source.type === "npm" ? Object.keys(source).every((key) => ["type", "package", "version", "registry"].includes(key)) && scalar(source.package, MAX_NAME_BYTES) && [source.version, source.registry].every(optionalText) : false;
|
|
13255
|
+
return valid ? { sourceType: source.type, sourceSha256: hashNativeJsonControls(JSON.stringify(source)), ...source.type === "local" ? { sourcePath: source.path } : {} } : {};
|
|
13256
|
+
}
|
|
13211
13257
|
function projectCodexNativeSkillCatalog(response, cwd) {
|
|
13212
13258
|
if (!absolutePath2(cwd) || !object4(response) || !Array.isArray(response.data) || response.data.length !== 1)
|
|
13213
13259
|
refuse();
|
|
@@ -13237,7 +13283,10 @@ function projectCodexInstalledPlugins(response) {
|
|
|
13237
13283
|
plugins.push({
|
|
13238
13284
|
...value,
|
|
13239
13285
|
marketplace: marketplace.name,
|
|
13240
|
-
sourceType:
|
|
13286
|
+
sourceType: undefined,
|
|
13287
|
+
sourceSha256: undefined,
|
|
13288
|
+
sourcePath: undefined,
|
|
13289
|
+
...projectPluginSource(value.source)
|
|
13241
13290
|
});
|
|
13242
13291
|
}
|
|
13243
13292
|
}
|
|
@@ -13248,7 +13297,7 @@ function projectCodexInstalledPluginEntries(value) {
|
|
|
13248
13297
|
refuse();
|
|
13249
13298
|
const ids = new Set;
|
|
13250
13299
|
return value.map((item) => {
|
|
13251
|
-
if (!object4(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && item.sourceType !== "
|
|
13300
|
+
if (!object4(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && !["remote", "local", "git", "npm"].includes(item.sourceType) || (["local", "git", "npm"].includes(item.sourceType) ? typeof item.sourceSha256 !== "string" || !/^[a-f0-9]{64}$/.test(item.sourceSha256) : item.sourceSha256 !== undefined) || (item.sourceType === "local" ? !absolutePath2(item.sourcePath) || normalize2(item.sourcePath) !== item.sourcePath || item.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: item.sourcePath })) : item.sourcePath !== undefined))
|
|
13252
13301
|
refuse();
|
|
13253
13302
|
const at = item.id.lastIndexOf("@");
|
|
13254
13303
|
if (at < 1 || item.id.slice(0, at) !== item.name || at === item.id.length - 1 || ids.has(item.id))
|
|
@@ -13260,7 +13309,9 @@ function projectCodexInstalledPluginEntries(value) {
|
|
|
13260
13309
|
installed: item.installed,
|
|
13261
13310
|
enabled: item.enabled,
|
|
13262
13311
|
localVersion: item.localVersion ?? null,
|
|
13263
|
-
...item.sourceType ===
|
|
13312
|
+
...item.sourceType === undefined ? {} : { sourceType: item.sourceType },
|
|
13313
|
+
...item.sourceSha256 === undefined ? {} : { sourceSha256: item.sourceSha256 },
|
|
13314
|
+
...item.sourcePath === undefined ? {} : { sourcePath: item.sourcePath },
|
|
13264
13315
|
...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
|
|
13265
13316
|
};
|
|
13266
13317
|
});
|
|
@@ -13381,27 +13432,62 @@ function mcpControls(path, read2) {
|
|
|
13381
13432
|
const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
13382
13433
|
const textField = (v, empty = false) => typeof v === "string" && (empty || Boolean(v.trim())) && Buffer.byteLength(v) <= 16384 && !/[\x00-\x1f\x7f]/.test(v);
|
|
13383
13434
|
const envName = (v) => typeof v === "string" && /^[A-Za-z_][A-Za-z0-9_]{0,255}$/.test(v);
|
|
13435
|
+
const optional = (v, valid) => v === undefined || v === null || valid(v);
|
|
13436
|
+
const stringList = (v) => Array.isArray(v) && v.length <= 256 && v.every((item) => textField(item));
|
|
13437
|
+
const approval = (v) => ["auto", "prompt", "writes", "approve"].includes(v);
|
|
13438
|
+
const duration = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
|
|
13439
|
+
const positiveInteger = (v) => Number.isSafeInteger(v) && v > 0;
|
|
13440
|
+
const fields = [
|
|
13441
|
+
"command",
|
|
13442
|
+
"args",
|
|
13443
|
+
"cwd",
|
|
13444
|
+
"env",
|
|
13445
|
+
"env_vars",
|
|
13446
|
+
"enabled",
|
|
13447
|
+
"required",
|
|
13448
|
+
"environment_id",
|
|
13449
|
+
"startup_readiness",
|
|
13450
|
+
"supports_parallel_tool_calls",
|
|
13451
|
+
"tool_input_schema_max_bytes",
|
|
13452
|
+
"startup_timeout_sec",
|
|
13453
|
+
"startup_timeout_ms",
|
|
13454
|
+
"tool_timeout_sec",
|
|
13455
|
+
"default_tools_approval_mode",
|
|
13456
|
+
"enabled_tools",
|
|
13457
|
+
"disabled_tools",
|
|
13458
|
+
"omit_tools_from",
|
|
13459
|
+
"tools",
|
|
13460
|
+
"name"
|
|
13461
|
+
];
|
|
13384
13462
|
if (!object5(value) || Object.keys(value).length !== 1 || !object5(value.mcpServers))
|
|
13385
13463
|
refuse2();
|
|
13386
13464
|
const servers = Object.entries(value.mcpServers);
|
|
13387
13465
|
if (!servers.length || servers.length > 256)
|
|
13388
13466
|
refuse2();
|
|
13389
13467
|
for (const [name, server] of servers) {
|
|
13390
|
-
if (!identifier2(name) || !object5(server) || !Object.keys(server).every((key) =>
|
|
13468
|
+
if (!identifier2(name) || !object5(server) || !Object.keys(server).every((key) => fields.includes(key)) || !textField(server.command) || !optional(server.args, (v) => Array.isArray(v) && v.length <= 256 && v.every((arg) => textField(arg, true))) || !optional(server.cwd, textField) || !optional(server.env, (v) => object5(v) && Object.keys(v).length <= 256 && Object.entries(v).every(([key, value2]) => envName(key) && textField(value2, true))) || !["enabled", "required", "supports_parallel_tool_calls"].every((key) => optional(server[key], (v) => typeof v === "boolean")) || !optional(server.environment_id, (v) => v === "local") || !optional(server.startup_readiness, (v) => ["connection", "catalog"].includes(v)) || !optional(server.tool_input_schema_max_bytes, positiveInteger) || !["startup_timeout_sec", "tool_timeout_sec"].every((key) => optional(server[key], duration)) || !optional(server.startup_timeout_ms, (v) => Number.isSafeInteger(v) && v >= 0) || !optional(server.default_tools_approval_mode, approval) || !["enabled_tools", "disabled_tools"].every((key) => optional(server[key], stringList)) || !optional(server.omit_tools_from, (v) => Array.isArray(v) && v.length <= 256 && v.every((item) => ["code_mode", "deferred", "direct"].includes(item))) || !optional(server.name, textField))
|
|
13391
13469
|
refuse2();
|
|
13392
|
-
if (server.env_vars !== undefined) {
|
|
13470
|
+
if (server.env_vars !== undefined && server.env_vars !== null) {
|
|
13393
13471
|
if (!Array.isArray(server.env_vars) || server.env_vars.length > 256)
|
|
13394
13472
|
refuse2();
|
|
13395
13473
|
const names = server.env_vars.map((entry) => {
|
|
13396
13474
|
if (envName(entry))
|
|
13397
13475
|
return entry;
|
|
13398
|
-
if (!object5(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || entry.source
|
|
13476
|
+
if (!object5(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || !optional(entry.source, (v) => v === "local"))
|
|
13399
13477
|
refuse2();
|
|
13400
13478
|
return entry.name;
|
|
13401
13479
|
});
|
|
13402
13480
|
if (new Set(names).size !== names.length)
|
|
13403
13481
|
refuse2();
|
|
13404
13482
|
}
|
|
13483
|
+
if (server.tools !== undefined && server.tools !== null) {
|
|
13484
|
+
if (!object5(server.tools) || Object.keys(server.tools).length > 256)
|
|
13485
|
+
refuse2();
|
|
13486
|
+
for (const [tool, config] of Object.entries(server.tools)) {
|
|
13487
|
+
if (!textField(tool) || !object5(config) || !Object.keys(config).every((key) => ["approval_mode", "output_token_limit"].includes(key)) || !optional(config.approval_mode, approval) || !optional(config.output_token_limit, positiveInteger))
|
|
13488
|
+
refuse2();
|
|
13489
|
+
}
|
|
13490
|
+
}
|
|
13405
13491
|
}
|
|
13406
13492
|
return sha2562;
|
|
13407
13493
|
}
|
|
@@ -13438,7 +13524,7 @@ function rootControls(root, read2) {
|
|
|
13438
13524
|
const appSha256 = present ? appControls(app, read2) : undefined;
|
|
13439
13525
|
return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
|
|
13440
13526
|
}
|
|
13441
|
-
function
|
|
13527
|
+
function documentIdentity(document2, cache, read2) {
|
|
13442
13528
|
if (!document2.startsWith(cache + sep4) || !document2.endsWith(sep4 + "SKILL.md"))
|
|
13443
13529
|
refuse2();
|
|
13444
13530
|
const text2 = read2(document2);
|
|
@@ -13466,11 +13552,33 @@ function identity3(document2, cache, read2) {
|
|
|
13466
13552
|
const path = join14(root, ".codex-plugin", "plugin.json");
|
|
13467
13553
|
if (!lstatSync12(path, { throwIfNoEntry: false }))
|
|
13468
13554
|
continue;
|
|
13469
|
-
const
|
|
13470
|
-
|
|
13555
|
+
const manifestFile = lstatSync12(path);
|
|
13556
|
+
if (!lstatSync12(join14(root, ".codex-plugin")).isDirectory() || !manifestFile.isFile() || manifestFile.size > 1024 * 1024)
|
|
13557
|
+
refuse2();
|
|
13558
|
+
let manifest;
|
|
13559
|
+
try {
|
|
13560
|
+
const text3 = read2(path);
|
|
13561
|
+
hashNativeJsonControls(text3);
|
|
13562
|
+
manifest = JSON.parse(text3);
|
|
13563
|
+
} catch {
|
|
13564
|
+
refuse2();
|
|
13565
|
+
}
|
|
13566
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
13567
|
+
refuse2();
|
|
13568
|
+
const declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
13569
|
+
if (declaredVersion === null || (declaredVersion ? declaredVersion !== basename5(root) : basename5(root) !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
13570
|
+
refuse2();
|
|
13571
|
+
return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent: dirname10(root), root };
|
|
13471
13572
|
}
|
|
13472
13573
|
refuse2();
|
|
13473
13574
|
}
|
|
13575
|
+
function identity3(document2, cache, read2) {
|
|
13576
|
+
const parsed = documentIdentity(document2, cache, read2);
|
|
13577
|
+
const controls = rootControls(parsed.root, read2);
|
|
13578
|
+
if (controls.namespace !== parsed.namespace)
|
|
13579
|
+
refuse2();
|
|
13580
|
+
return { name: parsed.name, pluginParent: parsed.pluginParent, ...controls };
|
|
13581
|
+
}
|
|
13474
13582
|
function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
|
|
13475
13583
|
if (!Array.isArray(controls) || controls.length > 4096)
|
|
13476
13584
|
return false;
|
|
@@ -13508,11 +13616,13 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
|
|
|
13508
13616
|
return false;
|
|
13509
13617
|
}
|
|
13510
13618
|
}
|
|
13511
|
-
function
|
|
13619
|
+
function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules, pluginSettings) {
|
|
13512
13620
|
if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
|
|
13513
13621
|
refuse2();
|
|
13514
13622
|
const skills = projectCodexNativeSkillCatalog({ data: [{ cwd, errors: [], skills: catalog.skills }] }, cwd);
|
|
13515
|
-
const allowed = new Set(documents), result = [];
|
|
13623
|
+
const allowed = new Set(documents), result = [], inertDocuments = new Set;
|
|
13624
|
+
const inactivePlugins = new Map;
|
|
13625
|
+
const sourceInputs = new Map;
|
|
13516
13626
|
let installedPlugins;
|
|
13517
13627
|
try {
|
|
13518
13628
|
installedPlugins = projectCodexInstalledPluginEntries(catalog.plugins ?? []);
|
|
@@ -13523,25 +13633,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, r
|
|
|
13523
13633
|
for (const document2 of candidates) {
|
|
13524
13634
|
if (!document2.startsWith(cache + sep4))
|
|
13525
13635
|
continue;
|
|
13526
|
-
const
|
|
13527
|
-
if (
|
|
13636
|
+
const located = documentIdentity(document2, cache, read2);
|
|
13637
|
+
if (located.name === "skills-cli" || located.name.endsWith(":skills-cli"))
|
|
13528
13638
|
refuse2();
|
|
13529
|
-
const cacheParts = relative4(cache,
|
|
13639
|
+
const cacheParts = relative4(cache, located.pluginParent).split(sep4);
|
|
13530
13640
|
if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier2(part)))
|
|
13531
13641
|
refuse2();
|
|
13532
13642
|
const [marketplace, pluginName] = cacheParts;
|
|
13533
|
-
if (pluginName !==
|
|
13643
|
+
if (pluginName !== located.namespace)
|
|
13534
13644
|
refuse2();
|
|
13535
13645
|
const expectedPluginId = `${pluginName}@${marketplace}`;
|
|
13536
13646
|
const listed = skills.filter((skill) => skill.path === document2);
|
|
13537
13647
|
if (listed.length > 1)
|
|
13538
13648
|
refuse2();
|
|
13649
|
+
const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
|
|
13650
|
+
if (catalog.version === "codex-cli 0.160.0" && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename5(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
|
|
13651
|
+
const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
|
|
13652
|
+
if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache + sep4) && (lstatSync12(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep4)))) {
|
|
13653
|
+
if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep4)) || !sourceInputUnchanged(input, cache, read2))
|
|
13654
|
+
refuse2();
|
|
13655
|
+
sourceInputs.set(expectedPluginId, input);
|
|
13656
|
+
}
|
|
13657
|
+
}
|
|
13658
|
+
const settings = pluginSettings && typeof pluginSettings === "object" && !Array.isArray(pluginSettings) ? pluginSettings : undefined;
|
|
13659
|
+
if (catalog.version === "codex-cli 0.160.0" && !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) {
|
|
13660
|
+
if (!Array.isArray(rules))
|
|
13661
|
+
refuse2();
|
|
13662
|
+
const exactPathDeny = rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
|
|
13663
|
+
if (!lstatSync12(document2).isFile() || realpathSync5(document2) !== document2 || realpathSync5(located.root) !== located.root || realpathSync5(located.pluginParent) !== located.pluginParent || realpathSync5(join14(located.root, ".codex-plugin/plugin.json")) !== join14(located.root, ".codex-plugin/plugin.json") || !exactPathDeny || rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === located.name && rule?.enabled !== false) || !isCodexNativeSkillDisabled({ name: located.name, path: document2, pluginId: expectedPluginId, enabled: true }, rules))
|
|
13664
|
+
refuse2();
|
|
13665
|
+
inertDocuments.add(document2);
|
|
13666
|
+
const plugin = installed[0];
|
|
13667
|
+
inactivePlugins.set(expectedPluginId, { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceType: plugin.sourceType, sourceSha256: plugin.sourceSha256 });
|
|
13668
|
+
continue;
|
|
13669
|
+
}
|
|
13670
|
+
const parsed = { name: located.name, pluginParent: located.pluginParent, ...rootControls(located.root, read2) };
|
|
13671
|
+
if (parsed.namespace !== located.namespace)
|
|
13672
|
+
refuse2();
|
|
13539
13673
|
let remotePluginId;
|
|
13540
13674
|
if (listed.length === 1) {
|
|
13541
13675
|
if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
|
|
13542
13676
|
refuse2();
|
|
13543
13677
|
} else {
|
|
13544
|
-
const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
|
|
13545
13678
|
if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
|
|
13546
13679
|
refuse2();
|
|
13547
13680
|
const plugin = installed[0];
|
|
@@ -13559,9 +13692,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, r
|
|
|
13559
13692
|
}
|
|
13560
13693
|
result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
|
|
13561
13694
|
}
|
|
13562
|
-
if (!result.length && documents.some((document2) => document2.startsWith(cache + sep4)) || new Set(result.map((item) => item.name)).size !== result.length)
|
|
13695
|
+
if (!result.length && documents.some((document2) => document2.startsWith(cache + sep4) && !inertDocuments.has(document2)) || new Set(result.map((item) => item.name)).size !== result.length)
|
|
13563
13696
|
refuse2();
|
|
13564
|
-
return result;
|
|
13697
|
+
return { skills: result, inactivePlugins: [...inactivePlugins.values()], sourceInputs: [...sourceInputs.values()] };
|
|
13698
|
+
}
|
|
13699
|
+
function sourceInputUnchanged(input, cache, read2) {
|
|
13700
|
+
try {
|
|
13701
|
+
const parts = relative4(cache, input.pluginParent).split(sep4);
|
|
13702
|
+
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 })) || resolve9(input.sourceRoot) !== input.sourceRoot || !lstatSync12(input.sourceRoot).isDirectory() || realpathSync5(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache || input.sourceRoot.startsWith(cache + sep4) || cache.startsWith(input.sourceRoot + sep4))
|
|
13703
|
+
return false;
|
|
13704
|
+
const path = join14(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync12(path);
|
|
13705
|
+
if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync5(path) !== path)
|
|
13706
|
+
return false;
|
|
13707
|
+
const text2 = read2(path);
|
|
13708
|
+
hashNativeJsonControls(text2);
|
|
13709
|
+
const manifest = JSON.parse(text2);
|
|
13710
|
+
return Boolean(manifest && typeof manifest === "object" && !Array.isArray(manifest) && manifest.name === input.namespace);
|
|
13711
|
+
} catch {
|
|
13712
|
+
return false;
|
|
13713
|
+
}
|
|
13714
|
+
}
|
|
13715
|
+
function reviewedCodexPluginSourceRoots(cache, inputs, read2) {
|
|
13716
|
+
if (!Array.isArray(inputs) || inputs.length > 4096 || new Set(inputs.map((input) => input.pluginId)).size !== inputs.length || new Set(inputs.map((input) => input.sourceRoot)).size !== inputs.length || inputs.some((input) => !sourceInputUnchanged(input, cache, read2)))
|
|
13717
|
+
refuse2();
|
|
13718
|
+
const roots = inputs.map((input) => input.sourceRoot);
|
|
13719
|
+
if (roots.some((root) => roots.some((other) => root !== other && root.startsWith(other + sep4))))
|
|
13720
|
+
refuse2();
|
|
13721
|
+
return roots;
|
|
13722
|
+
}
|
|
13723
|
+
function isReviewedCodexPluginInactive(document2, cache, controls, pluginSettings, rules, read2) {
|
|
13724
|
+
if (!Array.isArray(controls) || controls.length > 4096 || !pluginSettings || typeof pluginSettings !== "object" || Array.isArray(pluginSettings) || !Array.isArray(rules))
|
|
13725
|
+
return false;
|
|
13726
|
+
try {
|
|
13727
|
+
const parsed = documentIdentity(document2, cache, read2), matches = controls.filter((control2) => control2.pluginParent === parsed.pluginParent && control2.namespace === parsed.namespace);
|
|
13728
|
+
if (matches.length !== 1 || !lstatSync12(document2).isFile() || realpathSync5(document2) !== document2 || realpathSync5(parsed.root) !== parsed.root || realpathSync5(parsed.pluginParent) !== parsed.pluginParent || realpathSync5(join14(parsed.root, ".codex-plugin/plugin.json")) !== join14(parsed.root, ".codex-plugin/plugin.json"))
|
|
13729
|
+
return false;
|
|
13730
|
+
const control = matches[0], parts = relative4(cache, parsed.pluginParent).split(sep4);
|
|
13731
|
+
if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== parsed.namespace || control.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ ...control, id: control.pluginId }) || !Object.hasOwn(pluginSettings, control.pluginId) || pluginSettings[control.pluginId]?.enabled !== false)
|
|
13732
|
+
return false;
|
|
13733
|
+
return !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule?.enabled !== false);
|
|
13734
|
+
} catch {
|
|
13735
|
+
return false;
|
|
13736
|
+
}
|
|
13565
13737
|
}
|
|
13566
13738
|
function isReviewedCodexPluginSkillDisabled(document2, cache, controls, rules, read2) {
|
|
13567
13739
|
if (!document2.startsWith(cache + sep4) || !Array.isArray(controls) || controls.length > 4096)
|
|
@@ -14245,10 +14417,75 @@ function disableCodexBundledSkills(text2) {
|
|
|
14245
14417
|
// src/lib/agent-integration.ts
|
|
14246
14418
|
var sha3 = (data) => createHash12("sha256").update(data).digest("hex");
|
|
14247
14419
|
var ROOTS = NATIVE_SKILL_ROOTS;
|
|
14420
|
+
function hasNoDarwinAcl(fd) {
|
|
14421
|
+
try {
|
|
14422
|
+
const { dlopen, read: read2 } = __require("bun:ffi");
|
|
14423
|
+
const library = dlopen("/usr/lib/libSystem.B.dylib", {
|
|
14424
|
+
acl_get_fd: { args: ["i32"], returns: "ptr" },
|
|
14425
|
+
acl_free: { args: ["ptr"], returns: "i32" },
|
|
14426
|
+
__error: { args: [], returns: "ptr" }
|
|
14427
|
+
});
|
|
14428
|
+
try {
|
|
14429
|
+
const errno = library.symbols.__error();
|
|
14430
|
+
if (!errno)
|
|
14431
|
+
return false;
|
|
14432
|
+
const acl = library.symbols.acl_get_fd(fd);
|
|
14433
|
+
if (acl) {
|
|
14434
|
+
library.symbols.acl_free(acl);
|
|
14435
|
+
return false;
|
|
14436
|
+
}
|
|
14437
|
+
return (acl === null || acl === 0) && read2.i32(errno) === 2;
|
|
14438
|
+
} finally {
|
|
14439
|
+
library.close();
|
|
14440
|
+
}
|
|
14441
|
+
} catch {
|
|
14442
|
+
return false;
|
|
14443
|
+
}
|
|
14444
|
+
}
|
|
14445
|
+
function isSystemRootAlias(path) {
|
|
14446
|
+
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(path))
|
|
14447
|
+
return false;
|
|
14448
|
+
const target = `/private${path}`, link = `private${path}`;
|
|
14449
|
+
const identity4 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}`;
|
|
14450
|
+
try {
|
|
14451
|
+
const before = lstatSync15(path, { bigint: true });
|
|
14452
|
+
if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync4(path) !== link)
|
|
14453
|
+
return false;
|
|
14454
|
+
const directories = ["/", "/private", target].map((directory) => {
|
|
14455
|
+
const stat = lstatSync15(directory, { bigint: true });
|
|
14456
|
+
if (!stat.isDirectory() || stat.uid !== 0n)
|
|
14457
|
+
throw new Error("Untrusted system alias directory");
|
|
14458
|
+
if (directory !== "/private/tmp") {
|
|
14459
|
+
if ((stat.mode & 0o022n) !== 0n)
|
|
14460
|
+
throw new Error("Writable system alias directory");
|
|
14461
|
+
} else if ((stat.mode & 0o1000n) === 0n)
|
|
14462
|
+
throw new Error("Unprotected system temporary directory");
|
|
14463
|
+
const fd = openSync8(directory, constants7.O_RDONLY | constants7.O_DIRECTORY | constants7.O_NOFOLLOW);
|
|
14464
|
+
try {
|
|
14465
|
+
if (identity4(fstatSync8(fd, { bigint: true })) !== identity4(stat) || !hasNoDarwinAcl(fd) || identity4(fstatSync8(fd, { bigint: true })) !== identity4(stat))
|
|
14466
|
+
throw new Error("Unverified system alias directory ACL");
|
|
14467
|
+
} finally {
|
|
14468
|
+
closeSync8(fd);
|
|
14469
|
+
}
|
|
14470
|
+
return { directory, identity: identity4(stat) };
|
|
14471
|
+
});
|
|
14472
|
+
return realpathSync6(path) === target && readlinkSync4(path) === link && identity4(lstatSync15(path, { bigint: true })) === identity4(before) && directories.every((item) => identity4(lstatSync15(item.directory, { bigint: true })) === item.identity);
|
|
14473
|
+
} catch {
|
|
14474
|
+
return false;
|
|
14475
|
+
}
|
|
14476
|
+
}
|
|
14477
|
+
function canonicalSystemPath(path) {
|
|
14478
|
+
const absolute = resolve10(path), root = `/${absolute.split(sep5)[1]}`;
|
|
14479
|
+
if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync15(root, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
14480
|
+
return absolute;
|
|
14481
|
+
if (!isSystemRootAlias(root))
|
|
14482
|
+
throw new Error(`Refusing symlink path: ${root}`);
|
|
14483
|
+
return `/private${absolute}`;
|
|
14484
|
+
}
|
|
14248
14485
|
function assertSafePath(path) {
|
|
14249
14486
|
let cursor = resolve10(path);
|
|
14250
14487
|
while (true) {
|
|
14251
|
-
if (lstatSync15(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
14488
|
+
if (lstatSync15(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
|
|
14252
14489
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
14253
14490
|
const parent = dirname11(cursor);
|
|
14254
14491
|
if (parent === cursor)
|
|
@@ -14284,7 +14521,7 @@ function rootAliases(home, allowed = false) {
|
|
|
14284
14521
|
return result;
|
|
14285
14522
|
}
|
|
14286
14523
|
function canonicalAgentPath(path, aliases) {
|
|
14287
|
-
const absolute =
|
|
14524
|
+
const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
|
|
14288
14525
|
return binding ? join17(binding.target, relative5(binding.alias, absolute)) : absolute;
|
|
14289
14526
|
}
|
|
14290
14527
|
function disabledCodexSkillPaths(config, aliases) {
|
|
@@ -14382,17 +14619,19 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
14382
14619
|
const identities = new Set;
|
|
14383
14620
|
const selected = [];
|
|
14384
14621
|
for (const target of manifest.targets) {
|
|
14385
|
-
|
|
14386
|
-
|
|
14622
|
+
if (!isAbsolute11(target.projectRoot) || resolve10(target.projectRoot) !== target.projectRoot)
|
|
14623
|
+
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
14624
|
+
const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative5(projectRoot, resolve10(projectRoot, target.path));
|
|
14625
|
+
if (!existsSync8(projectRoot) || !lstatSync15(projectRoot).isDirectory())
|
|
14387
14626
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
14388
14627
|
assertSafePath(projectRoot);
|
|
14389
14628
|
if (isAbsolute11(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep5}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
|
|
14390
14629
|
throw new Error(`Invalid native migration target: ${target.path}`);
|
|
14391
|
-
const identity4 = `${target.agent}\x00${projectRoot}\x00${normalized}`;
|
|
14630
|
+
const identity4 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
|
|
14392
14631
|
if (identities.has(identity4))
|
|
14393
14632
|
throw new Error(`Duplicate native migration target: ${target.path}`);
|
|
14394
14633
|
identities.add(identity4);
|
|
14395
|
-
const expected = resolve10(target.projectRoot, target.path);
|
|
14634
|
+
const expected = canonicalSystemPath(resolve10(target.projectRoot, target.path));
|
|
14396
14635
|
const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve10(entry2.path) === expected);
|
|
14397
14636
|
if (matches.length !== 1)
|
|
14398
14637
|
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
@@ -14408,7 +14647,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
14408
14647
|
function projectAncestorDirectories(projects) {
|
|
14409
14648
|
const directories = new Set;
|
|
14410
14649
|
for (const project of projects) {
|
|
14411
|
-
for (let path =
|
|
14650
|
+
for (let path = canonicalSystemPath(project), depth = 0;; depth++) {
|
|
14412
14651
|
if (depth >= 100)
|
|
14413
14652
|
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
14414
14653
|
directories.add(path);
|
|
@@ -14421,11 +14660,13 @@ function projectAncestorDirectories(projects) {
|
|
|
14421
14660
|
return [...directories];
|
|
14422
14661
|
}
|
|
14423
14662
|
function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
14663
|
+
home = canonicalSystemPath(home);
|
|
14424
14664
|
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute11(options.reviewedCacheAlias) || resolve10(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
|
|
14425
14665
|
throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
|
|
14426
14666
|
if (options.disabledVendorPaths?.some((path) => !isAbsolute11(path) || resolve10(path) !== path))
|
|
14427
14667
|
throw new Error("Disabled vendor paths must be exact absolute paths");
|
|
14428
|
-
const
|
|
14668
|
+
const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
|
|
14669
|
+
const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
|
|
14429
14670
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
14430
14671
|
const selectedAgents = options.agents ? new Set(options.agents) : undefined;
|
|
14431
14672
|
const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
|
|
@@ -14436,6 +14677,9 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
14436
14677
|
for (const [agent, path] of rootDefinitions)
|
|
14437
14678
|
roots.push([agent, canonicalAgentPath(join17(project, path), aliases)]);
|
|
14438
14679
|
}
|
|
14680
|
+
const installationInputs = options.codexInstallationInputRoots ?? [];
|
|
14681
|
+
if (installationInputs.some((input) => resolve10(input) !== input || realpathSync6(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep5) || input.startsWith(path + sep5)))))
|
|
14682
|
+
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
14439
14683
|
const entries = [], seen = new Set;
|
|
14440
14684
|
const cacheScans = new Map, maxAliasProofEntries = 1e4;
|
|
14441
14685
|
let discoveryEntries = 0, discoveryPathBytes = 0;
|
|
@@ -14448,8 +14692,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
14448
14692
|
}
|
|
14449
14693
|
let reviewedCacheAliasFound = false;
|
|
14450
14694
|
function verifiedSiblingCacheAlias(agent, path, parent) {
|
|
14451
|
-
const link = readlinkSync4(path),
|
|
14452
|
-
if (
|
|
14695
|
+
const link = readlinkSync4(path), lexicalTarget = resolve10(parent, link);
|
|
14696
|
+
if (isAbsolute11(link) ? link !== lexicalTarget : dirname11(link) !== ".")
|
|
14697
|
+
return false;
|
|
14698
|
+
const target = canonicalSystemPath(lexicalTarget);
|
|
14699
|
+
if (dirname11(target) !== parent)
|
|
14453
14700
|
return false;
|
|
14454
14701
|
if (!lstatSync15(target, { throwIfNoEntry: false })?.isDirectory())
|
|
14455
14702
|
return false;
|
|
@@ -14458,20 +14705,22 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
14458
14705
|
return false;
|
|
14459
14706
|
if (scan.hasSkills) {
|
|
14460
14707
|
const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep5)));
|
|
14461
|
-
if (path ===
|
|
14708
|
+
if (path === reviewedCacheAlias && targetSkills.length > 0)
|
|
14462
14709
|
reviewedCacheAliasFound = true;
|
|
14463
14710
|
if (agent === "codex" && targetSkills.length > 0 && targetSkills.every((entry) => disabledVendorPaths.has(entry.path) || options.disabledVendorSkill?.(entry) === true))
|
|
14464
14711
|
return true;
|
|
14465
|
-
if (path !==
|
|
14712
|
+
if (path !== reviewedCacheAlias)
|
|
14466
14713
|
return false;
|
|
14467
14714
|
}
|
|
14468
14715
|
return true;
|
|
14469
14716
|
}
|
|
14470
|
-
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
|
|
14717
|
+
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false, configuredRoot = false) {
|
|
14471
14718
|
if (!admitted)
|
|
14472
14719
|
admit(path);
|
|
14473
14720
|
const scan = { complete: true, hasSkills: false, entries: 1 };
|
|
14474
14721
|
assertSafePath(path);
|
|
14722
|
+
if (configuredRoot && agent === "codex" && installationInputs.includes(path))
|
|
14723
|
+
return scan;
|
|
14475
14724
|
if (!existsSync8(path))
|
|
14476
14725
|
return scan;
|
|
14477
14726
|
const stat = lstatSync15(path);
|
|
@@ -14527,7 +14776,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
14527
14776
|
continue;
|
|
14528
14777
|
}
|
|
14529
14778
|
}
|
|
14530
|
-
const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true);
|
|
14779
|
+
const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true, configuredRoot);
|
|
14531
14780
|
scan.complete &&= childScan.complete;
|
|
14532
14781
|
scan.hasSkills ||= childScan.hasSkills;
|
|
14533
14782
|
scan.entries += childScan.entries;
|
|
@@ -14536,10 +14785,10 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
14536
14785
|
cacheScans.set(path, scan);
|
|
14537
14786
|
return scan;
|
|
14538
14787
|
}
|
|
14539
|
-
function scanRoot(agent, path, vendor = false, pluginCache = false) {
|
|
14788
|
+
function scanRoot(agent, path, vendor = false, pluginCache = false, configuredRoot = false) {
|
|
14540
14789
|
if (agent === "hermes" || options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`))
|
|
14541
14790
|
assertNoHermesLegacyShadow(path);
|
|
14542
|
-
if (!visit2(agent, path, vendor, 0, pluginCache).complete)
|
|
14791
|
+
if (!visit2(agent, path, vendor, 0, pluginCache, false, configuredRoot).complete)
|
|
14543
14792
|
throw new Error(`Native skill discovery limit exceeded; review this root before continuing: ${path}`);
|
|
14544
14793
|
}
|
|
14545
14794
|
for (const [agent, path] of roots)
|
|
@@ -14555,7 +14804,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
14555
14804
|
const configured = options.configured ? INTEGRATION_AGENTS.filter(includesAgent).flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
|
|
14556
14805
|
for (const root of [...options.agentRoots ?? [], ...configured])
|
|
14557
14806
|
if (includesAgent(root.agent))
|
|
14558
|
-
scanRoot(root.agent, canonicalAgentPath(root.path, aliases), true);
|
|
14807
|
+
scanRoot(root.agent, canonicalAgentPath(root.path, aliases), true, false, true);
|
|
14559
14808
|
if (options.reviewedCacheAlias !== undefined && !reviewedCacheAliasFound)
|
|
14560
14809
|
throw new Error("Reviewed cache alias was not an inventoried skill-containing sibling alias");
|
|
14561
14810
|
recheckRootAliases(aliases);
|
|
@@ -14695,6 +14944,11 @@ enabled = false
|
|
|
14695
14944
|
Bun.TOML.parse(result);
|
|
14696
14945
|
return result;
|
|
14697
14946
|
}
|
|
14947
|
+
function assertCodexInstallationInputs(home, projectDirs, inputs) {
|
|
14948
|
+
const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve10(directory, path)));
|
|
14949
|
+
if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep5) || input.startsWith(root + sep5))))
|
|
14950
|
+
throw new Error("Native installation input overlaps an unconditional skill-loading root");
|
|
14951
|
+
}
|
|
14698
14952
|
function planAgentIntegration(options) {
|
|
14699
14953
|
const home = options.home ?? homedir2(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
14700
14954
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
@@ -14733,11 +14987,16 @@ function planAgentIntegration(options) {
|
|
|
14733
14987
|
const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join17(home, ".codex", "config.toml"), aliases)) ?? "") : {};
|
|
14734
14988
|
const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
|
|
14735
14989
|
const observedNativeSources = [];
|
|
14736
|
-
const
|
|
14990
|
+
const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join17(entry.path, "SKILL.md")), canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), resolve10(options.projectDir ?? home), (path) => {
|
|
14737
14991
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
14738
14992
|
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
14739
14993
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
14740
|
-
}, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
|
|
14994
|
+
}, codexConfig.skills?.config ?? [], codexConfig.plugins) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
|
|
14995
|
+
const codexPluginSkills = codexPluginControls.skills;
|
|
14996
|
+
const codexInactivePlugins = codexPluginControls.inactivePlugins;
|
|
14997
|
+
const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
|
|
14998
|
+
const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root + sep5)) && codexDiscovery.sources.some((source) => source.path === join17(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
|
|
14999
|
+
assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
|
|
14741
15000
|
const changes = [];
|
|
14742
15001
|
for (const agent of [...new Set(options.agents)]) {
|
|
14743
15002
|
if (!INTEGRATION_AGENTS.includes(agent))
|
|
@@ -14802,7 +15061,16 @@ function planAgentIntegration(options) {
|
|
|
14802
15061
|
changes.push({ path: configPath, before: previous, after: next });
|
|
14803
15062
|
}
|
|
14804
15063
|
}
|
|
14805
|
-
const
|
|
15064
|
+
const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join17(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join17(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
|
|
15065
|
+
const discoveryAfter = discoveries.map((binding) => {
|
|
15066
|
+
const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
|
|
15067
|
+
if (binding.agent !== "codex")
|
|
15068
|
+
return rebound;
|
|
15069
|
+
const { codexInstallationInputs: previousInputs, ...current } = rebound;
|
|
15070
|
+
const inputs = codexPluginSourceInputs.map((input) => input.sourceRoot);
|
|
15071
|
+
const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root) => root.startsWith(directory.path + sep5))).map((directory) => directory.path), inputs) : previousInputs.directories;
|
|
15072
|
+
return codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: "codex-cli 0.160.0", catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
|
|
15073
|
+
});
|
|
14806
15074
|
const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
|
|
14807
15075
|
...policy.bridge,
|
|
14808
15076
|
version: CLI_BRIDGE_VERSION,
|
|
@@ -14815,7 +15083,7 @@ function planAgentIntegration(options) {
|
|
|
14815
15083
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
14816
15084
|
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join17(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
14817
15085
|
rootAliases: aliases,
|
|
14818
|
-
...
|
|
15086
|
+
...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
|
|
14819
15087
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
14820
15088
|
} };
|
|
14821
15089
|
const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
|
|
@@ -15306,7 +15574,12 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
15306
15574
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
15307
15575
|
if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024))))
|
|
15308
15576
|
throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
|
|
15309
|
-
const disabledPlugin = (entry) =>
|
|
15577
|
+
const disabledPlugin = (entry) => {
|
|
15578
|
+
if (agent !== "codex" || !entry.vendor)
|
|
15579
|
+
return false;
|
|
15580
|
+
const document2 = join17(entry.path, "SKILL.md"), cache = canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
|
|
15581
|
+
return isReviewedCodexPluginInactive(document2, cache, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read2) || isReviewedCodexPluginSkillDisabled(document2, cache, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read2);
|
|
15582
|
+
};
|
|
15310
15583
|
const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
|
|
15311
15584
|
const disabledVendorSkill = (entry) => {
|
|
15312
15585
|
if (agent !== "codex" || !entry.vendor)
|
|
@@ -15343,7 +15616,15 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
15343
15616
|
} catch (error) {
|
|
15344
15617
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
15345
15618
|
}
|
|
15346
|
-
|
|
15619
|
+
let installationInputRoots = [];
|
|
15620
|
+
if (agent === "codex") {
|
|
15621
|
+
try {
|
|
15622
|
+
installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
|
|
15623
|
+
} catch {
|
|
15624
|
+
throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
|
|
15625
|
+
}
|
|
15626
|
+
}
|
|
15627
|
+
const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0, disabledVendorPaths: agent === "codex" ? disabledCodexSkillPaths(codexConfig, aliases) : [], disabledVendorSkill: disabledPlugin, codexInstallationInputRoots: installationInputRoots });
|
|
15347
15628
|
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
|
|
15348
15629
|
if (unexpected.length) {
|
|
15349
15630
|
const paths = unexpected.slice(0, 3).map((entry) => {
|
|
@@ -18027,8 +18308,16 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
18027
18308
|
throw new Error("Invalid native discovery binding");
|
|
18028
18309
|
if (binding.agent === "hermes" && !binding.directories?.length)
|
|
18029
18310
|
throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
|
|
18030
|
-
|
|
18031
|
-
|
|
18311
|
+
const installationRoots = codexInstallationRoots(binding);
|
|
18312
|
+
const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 + sep6));
|
|
18313
|
+
if (binding.directories !== undefined) {
|
|
18314
|
+
const projected2 = binding.codexInstallationInputs?.directories ?? [];
|
|
18315
|
+
const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root2) => root2.startsWith(directory.path + sep6)));
|
|
18316
|
+
if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
|
|
18317
|
+
throw new Error("Missing installation input directory projection");
|
|
18318
|
+
verifyDiscoveryDirectories(binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory)));
|
|
18319
|
+
verifyDiscoveryDirectories(projected2, installationRoots);
|
|
18320
|
+
}
|
|
18032
18321
|
const budget = discoveryByteBudget();
|
|
18033
18322
|
for (const source of binding.sources) {
|
|
18034
18323
|
assertMarketplaceBinding(binding, source);
|
|
@@ -18038,6 +18327,10 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
18038
18327
|
throw new Error("Invalid native discovery projection");
|
|
18039
18328
|
if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
18040
18329
|
throw new Error("Invalid native discovery digest");
|
|
18330
|
+
if (installationInput(source.path)) {
|
|
18331
|
+
safe4(source.path);
|
|
18332
|
+
continue;
|
|
18333
|
+
}
|
|
18041
18334
|
const current = projected(source, undefined, budget);
|
|
18042
18335
|
if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
|
|
18043
18336
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
@@ -18045,6 +18338,27 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
18045
18338
|
for (const root2 of binding.roots)
|
|
18046
18339
|
safe4(root2);
|
|
18047
18340
|
}
|
|
18341
|
+
function codexInstallationRoots(binding) {
|
|
18342
|
+
const proof = binding.codexInstallationInputs;
|
|
18343
|
+
if (proof === undefined)
|
|
18344
|
+
return [];
|
|
18345
|
+
if (binding.agent !== "codex" || binding.method !== "reviewed" || proof.version !== "codex-cli 0.160.0" || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
|
|
18346
|
+
throw new Error("Invalid native installation input proof");
|
|
18347
|
+
const cache = dirname17(dirname17(proof.plugins[0].pluginParent));
|
|
18348
|
+
const config = join27(dirname17(dirname17(cache)), "config.toml"), home = dirname17(dirname17(dirname17(cache)));
|
|
18349
|
+
const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
|
|
18350
|
+
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join27(home, path));
|
|
18351
|
+
if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep6) || input.sourceRoot.startsWith(root2 + sep6)) || binding.sources.some((source) => basename9(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep6)))))
|
|
18352
|
+
throw new Error("Native installation input overlaps configuration or loading roots");
|
|
18353
|
+
if (proof.plugins.some((input) => dirname17(dirname17(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join27(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
|
|
18354
|
+
throw new Error("Missing native installation input source witness");
|
|
18355
|
+
return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
|
|
18356
|
+
const text2 = read2(path);
|
|
18357
|
+
if (text2 === null)
|
|
18358
|
+
throw new Error("Missing native installation input");
|
|
18359
|
+
return text2;
|
|
18360
|
+
});
|
|
18361
|
+
}
|
|
18048
18362
|
function assertMarketplaceBinding(binding, source) {
|
|
18049
18363
|
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
18050
18364
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
@@ -18055,8 +18369,11 @@ function assertMarketplaceBinding(binding, source) {
|
|
|
18055
18369
|
}
|
|
18056
18370
|
function rebindAgentDiscovery(binding, changes) {
|
|
18057
18371
|
const budget = discoveryByteBudget();
|
|
18372
|
+
const installationRoots = codexInstallationRoots(binding);
|
|
18058
18373
|
return { ...binding, sources: binding.sources.map((source) => {
|
|
18059
18374
|
assertMarketplaceBinding(binding, source);
|
|
18375
|
+
if (!changes.has(source.path) && installationRoots.some((root2) => source.path.startsWith(root2 + sep6)))
|
|
18376
|
+
return source;
|
|
18060
18377
|
const sha2562 = projected(source, changes, budget);
|
|
18061
18378
|
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
18062
18379
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|