@hasna/skills 0.10.36 → 0.10.39
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 +35 -0
- package/bin/index.js +2149 -1552
- package/bin/mcp.js +411 -11
- package/bin/migrate.js +5 -5
- package/bin/server.js +5 -5
- package/bin/worker.js +5 -5
- package/dist/index.js +1383 -844
- package/dist/lib/agent-discovery-directories.d.ts +9 -0
- package/dist/lib/agent-discovery.d.ts +12 -1
- package/dist/lib/agent-integration.d.ts +7 -3
- package/dist/lib/agent-sync.d.ts +5 -4
- package/dist/lib/codex-corpus-write.d.ts +16 -0
- package/dist/lib/codex-plugin-skill-controls.d.ts +15 -2
- package/dist/lib/home-adoption.d.ts +2 -1
- package/dist/lib/installer.d.ts +2 -10
- package/dist/sdk/index.js +1789 -1250
- package/package.json +5 -5
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.10.
|
|
20022
|
+
version: "0.10.39",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -20069,8 +20069,8 @@ var init_package = __esm(() => {
|
|
|
20069
20069
|
types: "./dist/index.d.ts",
|
|
20070
20070
|
scripts: {
|
|
20071
20071
|
clean: "rm -rf bin/ dist/",
|
|
20072
|
-
build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
20073
|
-
"build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
20072
|
+
build: "bun run clean && bun build --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @hasna/contracts/kernel-lock ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
20073
|
+
"build:js": "rm -rf dist && bun build --external @hasna/contracts/kernel-lock ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
20074
20074
|
test: "bun test --timeout 30000",
|
|
20075
20075
|
dev: "bun run ./src/cli/index.tsx",
|
|
20076
20076
|
"dev:watch": "bun --watch run ./src/cli/index.tsx",
|
|
@@ -20108,7 +20108,6 @@ var init_package = __esm(() => {
|
|
|
20108
20108
|
author: "Hasna",
|
|
20109
20109
|
license: "Apache-2.0",
|
|
20110
20110
|
devDependencies: {
|
|
20111
|
-
"@hasna/contracts": "1.1.0",
|
|
20112
20111
|
"@types/bun": "1.3.14",
|
|
20113
20112
|
"@types/node": "25.2.3",
|
|
20114
20113
|
"@types/react": "^18.2.0",
|
|
@@ -20130,7 +20129,8 @@ var init_package = __esm(() => {
|
|
|
20130
20129
|
"ink-spinner": "^5.0.0",
|
|
20131
20130
|
"ink-text-input": "^6.0.0",
|
|
20132
20131
|
react: "^18.2.0",
|
|
20133
|
-
zod: "^4.3.6"
|
|
20132
|
+
zod: "^4.3.6",
|
|
20133
|
+
"@hasna/contracts": "1.3.5"
|
|
20134
20134
|
},
|
|
20135
20135
|
engines: {
|
|
20136
20136
|
bun: ">=1.0.0"
|
|
@@ -20424,11 +20424,14 @@ function readCredentialFile(path, apiKeyKeys, pointerKey) {
|
|
|
20424
20424
|
}
|
|
20425
20425
|
return values[0] === undefined ? null : { apiKey: values[0] };
|
|
20426
20426
|
}
|
|
20427
|
-
function appConfigDiskValue(name, env, keys) {
|
|
20427
|
+
function appConfigDiskValue(name, env, keys, profile = null) {
|
|
20428
|
+
if (profile !== null && !SAFE_PROFILE.test(profile)) {
|
|
20429
|
+
throw new CredentialResolutionError(name, "The selected configuration profile is invalid.", ["profile"]);
|
|
20430
|
+
}
|
|
20428
20431
|
const wanted = keys.filter((key) => !CREDENTIAL_SHAPED_KEY.test(key));
|
|
20429
20432
|
if (wanted.length === 0)
|
|
20430
20433
|
return null;
|
|
20431
|
-
for (const path of
|
|
20434
|
+
for (const path of profileDiskSources(name, env, profile)) {
|
|
20432
20435
|
const parsed = readAppConfigFile(path);
|
|
20433
20436
|
if (!parsed)
|
|
20434
20437
|
continue;
|
|
@@ -38255,7 +38258,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
38255
38258
|
}
|
|
38256
38259
|
if (bridge.codexPluginSkills !== undefined)
|
|
38257
38260
|
for (const control of array3(bridge.codexPluginSkills, 4096)) {
|
|
38258
|
-
requireBound(object4(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "mcpSha256", "remotePluginId"].includes(key)));
|
|
38261
|
+
requireBound(object4(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "mcpSha256", "remotePluginId", "skillsOnly"].includes(key)));
|
|
38259
38262
|
text(control.name, 129);
|
|
38260
38263
|
text(control.pluginId, 1024);
|
|
38261
38264
|
text(control.namespace, 64);
|
|
@@ -38263,6 +38266,8 @@ function assertAgentPolicyCollections(policy) {
|
|
|
38263
38266
|
requireBound(isAbsolute3(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
|
|
38264
38267
|
if (control.remotePluginId !== undefined)
|
|
38265
38268
|
requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
|
|
38269
|
+
if (control.skillsOnly !== undefined)
|
|
38270
|
+
requireBound(control.skillsOnly === true && control.appSha256 === undefined && control.mcpSha256 === undefined);
|
|
38266
38271
|
if (control.mcpSha256 !== undefined)
|
|
38267
38272
|
requireBound(typeof control.mcpSha256 === "string" && /^[a-f0-9]{64}$/.test(control.mcpSha256));
|
|
38268
38273
|
if (control.appSha256 !== undefined)
|
|
@@ -38292,6 +38297,26 @@ function assertAgentPolicyCollections(policy) {
|
|
|
38292
38297
|
text(agent, 128);
|
|
38293
38298
|
requireBound(object4(value));
|
|
38294
38299
|
text(value.agent, 128);
|
|
38300
|
+
if (value.codexRetiredMaterializations !== undefined) {
|
|
38301
|
+
const proof = record3(value.codexRetiredMaterializations, 3), roots = array3(proof.roots, AGENT_POLICY_LIMITS.discoveryRoots), parents = proof.parents === undefined ? [] : array3(proof.parents, AGENT_POLICY_LIMITS.discoveryRoots);
|
|
38302
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).every((key) => ["roots", "parents", "directories"].includes(key)) && roots.length + parents.length > 0 && new Set(roots).size === roots.length && new Set(parents).size === parents.length);
|
|
38303
|
+
for (const root of roots) {
|
|
38304
|
+
text(root);
|
|
38305
|
+
requireBound(isAbsolute3(root) && resolve2(root) === root);
|
|
38306
|
+
}
|
|
38307
|
+
for (const parent of parents) {
|
|
38308
|
+
text(parent);
|
|
38309
|
+
requireBound(isAbsolute3(parent) && resolve2(parent) === parent);
|
|
38310
|
+
}
|
|
38311
|
+
for (const directory of array3(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
38312
|
+
requireBound(object4(directory));
|
|
38313
|
+
text(directory.path);
|
|
38314
|
+
requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
|
|
38315
|
+
if (directory.entries !== undefined)
|
|
38316
|
+
for (const entry of array3(directory.entries, AGENT_POLICY_LIMITS.discoveryDirectoryEntries))
|
|
38317
|
+
text(entry, AGENT_POLICY_LIMITS.pathCharacters * 6 + 100);
|
|
38318
|
+
}
|
|
38319
|
+
}
|
|
38295
38320
|
if (value.codexInstallationInputs !== undefined) {
|
|
38296
38321
|
const proof = record3(value.codexInstallationInputs, 4), inputs = array3(proof.plugins, 4096), ids = new Set;
|
|
38297
38322
|
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 && object4(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === proof.version && bridge.codexPluginSkillReview.catalogSha256 === proof.catalogSha256);
|
|
@@ -45411,8 +45436,10 @@ function safe(path) {
|
|
|
45411
45436
|
function same(before, after) {
|
|
45412
45437
|
return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
|
|
45413
45438
|
}
|
|
45414
|
-
function capture(path, budget, excludedRoots) {
|
|
45439
|
+
function capture(path, budget, excludedRoots, omittedRoots = [], rows) {
|
|
45415
45440
|
safe(path);
|
|
45441
|
+
if (omittedRoots.some((root2) => path === root2 || path.startsWith(root2 + "/")))
|
|
45442
|
+
return { path, sha256: null };
|
|
45416
45443
|
const root = lstatSync5(path, { throwIfNoEntry: false });
|
|
45417
45444
|
if (!root)
|
|
45418
45445
|
return { path, sha256: null };
|
|
@@ -45432,6 +45459,8 @@ function capture(path, budget, excludedRoots) {
|
|
|
45432
45459
|
need(new Set(names).size === names.length, "Ambiguous native discovery directory names");
|
|
45433
45460
|
for (const name of names.sort()) {
|
|
45434
45461
|
const child = join11(current, name), childRelative = relative3 ? `${relative3}/${name}` : name;
|
|
45462
|
+
if (omittedRoots.includes(child))
|
|
45463
|
+
continue;
|
|
45435
45464
|
need(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
|
|
45436
45465
|
const stat = lstatSync5(child);
|
|
45437
45466
|
need(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
|
|
@@ -45440,6 +45469,7 @@ function capture(path, budget, excludedRoots) {
|
|
|
45440
45469
|
budget.bytes += Buffer.byteLength(row);
|
|
45441
45470
|
need(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
|
|
45442
45471
|
hash2.update(row);
|
|
45472
|
+
rows?.push(row);
|
|
45443
45473
|
if (stat.isDirectory() && !excludedRoots.includes(child))
|
|
45444
45474
|
walk(child, childRelative, stat, depth + 1);
|
|
45445
45475
|
}
|
|
@@ -45449,6 +45479,44 @@ function capture(path, budget, excludedRoots) {
|
|
|
45449
45479
|
safe(path);
|
|
45450
45480
|
return { path, sha256: hash2.digest("hex") };
|
|
45451
45481
|
}
|
|
45482
|
+
function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
|
|
45483
|
+
need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid projected discovery directory collection");
|
|
45484
|
+
need(omittedRoots.length <= AGENT_POLICY_LIMITS.discoveryRoots && new Set(omittedRoots).size === omittedRoots.length, "Invalid inactive discovery roots");
|
|
45485
|
+
for (const root of omittedRoots)
|
|
45486
|
+
safe(root);
|
|
45487
|
+
const budget = { entries: 0, bytes: 0 };
|
|
45488
|
+
return paths.map((path) => {
|
|
45489
|
+
const entries = [];
|
|
45490
|
+
return { ...capture(path, budget, [], omittedRoots, entries), entries };
|
|
45491
|
+
});
|
|
45492
|
+
}
|
|
45493
|
+
function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
|
|
45494
|
+
need(directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid projected discovery directory collection");
|
|
45495
|
+
let count = 0, bytes = 0;
|
|
45496
|
+
const expected = directories.map((directory) => {
|
|
45497
|
+
safe(directory.path);
|
|
45498
|
+
need(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
|
|
45499
|
+
const hash2 = createHash2("sha256");
|
|
45500
|
+
const kept = [];
|
|
45501
|
+
for (const row of directory.entries) {
|
|
45502
|
+
need(typeof row === "string" && ++count <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Invalid reviewed directory membership rows");
|
|
45503
|
+
bytes += Buffer.byteLength(row);
|
|
45504
|
+
need(bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Reviewed directory membership byte limit exceeded");
|
|
45505
|
+
const value = JSON.parse(row);
|
|
45506
|
+
need(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute5(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
|
|
45507
|
+
`, "Invalid reviewed directory member");
|
|
45508
|
+
hash2.update(row);
|
|
45509
|
+
const path = join11(directory.path, value[0]);
|
|
45510
|
+
if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
|
|
45511
|
+
kept.push(row);
|
|
45512
|
+
}
|
|
45513
|
+
need(directory.sha256 === null ? directory.entries.length === 0 : hash2.digest("hex") === directory.sha256, "Reviewed directory inventory does not match its fingerprint");
|
|
45514
|
+
const absent = omittedRoots.some((root) => directory.path === root || directory.path.startsWith(root + "/"));
|
|
45515
|
+
return { path: directory.path, sha256: absent || directory.sha256 === null ? null : createHash2("sha256").update(kept.join("")).digest("hex") };
|
|
45516
|
+
});
|
|
45517
|
+
const current = captureDiscoveryDirectoryProjection(directories.map((item) => item.path), omittedRoots);
|
|
45518
|
+
need(current.every((item, index) => item.sha256 === expected[index].sha256), "Native discovery directory membership changed outside retired Codex roots");
|
|
45519
|
+
}
|
|
45452
45520
|
function captureDiscoveryDirectories(paths, excludedRoots = []) {
|
|
45453
45521
|
need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
|
|
45454
45522
|
const budget = { entries: 0, bytes: 0 };
|
|
@@ -47297,6 +47365,10 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
|
|
|
47297
47365
|
var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation", RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.", DEFAULT_PREPARATION_TIMEOUT_MS = 60000;
|
|
47298
47366
|
var init_dependency_preparation = () => {};
|
|
47299
47367
|
|
|
47368
|
+
// src/lib/codex-corpus-write.ts
|
|
47369
|
+
import { KernelLock } from "@hasna/contracts/kernel-lock";
|
|
47370
|
+
var init_codex_corpus_write = () => {};
|
|
47371
|
+
|
|
47300
47372
|
// src/lib/codex-local-mcp-controls.ts
|
|
47301
47373
|
function isCodexLocalStdioMcpServer(name, server) {
|
|
47302
47374
|
const identifier2 = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(value);
|
|
@@ -47664,6 +47736,7 @@ var init_codex_native_compatibility = __esm(() => {
|
|
|
47664
47736
|
|
|
47665
47737
|
// src/lib/codex-hook-rpc.ts
|
|
47666
47738
|
var init_codex_hook_rpc = __esm(() => {
|
|
47739
|
+
init_codex_corpus_write();
|
|
47667
47740
|
init_codex_native_compatibility();
|
|
47668
47741
|
init_codex_native_compatibility();
|
|
47669
47742
|
});
|
|
@@ -47673,6 +47746,7 @@ function codexPluginSourceIsConfigControlled(plugin) {
|
|
|
47673
47746
|
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"];
|
|
47674
47747
|
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)));
|
|
47675
47748
|
}
|
|
47749
|
+
var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
|
|
47676
47750
|
var init_codex_native_skill_catalog = __esm(() => {
|
|
47677
47751
|
init_codex_hook_rpc();
|
|
47678
47752
|
init_codex_native_compatibility();
|
|
@@ -47685,6 +47759,214 @@ import { lstatSync as lstatSync11, readdirSync as readdirSync6, readlinkSync as
|
|
|
47685
47759
|
function refuse() {
|
|
47686
47760
|
throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
|
|
47687
47761
|
}
|
|
47762
|
+
function remoteInstallationMatches(parent, remotePluginId, read) {
|
|
47763
|
+
try {
|
|
47764
|
+
if (!remotePluginIdentifier(remotePluginId) || !lstatSync11(parent).isDirectory() || realpathSync3(parent) !== parent)
|
|
47765
|
+
return false;
|
|
47766
|
+
const path = join16(parent, ".codex-remote-plugin-install.json"), stat = lstatSync11(path, { throwIfNoEntry: false });
|
|
47767
|
+
if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
|
|
47768
|
+
return false;
|
|
47769
|
+
const text2 = read(path);
|
|
47770
|
+
hashNativeJsonControls(text2);
|
|
47771
|
+
const value = JSON.parse(text2);
|
|
47772
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 2 && value.schema_version === 1 && value.remote_plugin_id === remotePluginId);
|
|
47773
|
+
} catch {
|
|
47774
|
+
return false;
|
|
47775
|
+
}
|
|
47776
|
+
}
|
|
47777
|
+
function appControls(path, read) {
|
|
47778
|
+
const text2 = read(path);
|
|
47779
|
+
let value, sha2562;
|
|
47780
|
+
try {
|
|
47781
|
+
sha2562 = hashNativeJsonControls(text2);
|
|
47782
|
+
value = JSON.parse(text2);
|
|
47783
|
+
} catch {
|
|
47784
|
+
refuse();
|
|
47785
|
+
}
|
|
47786
|
+
const object7 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
47787
|
+
if (!object7(value) || Object.keys(value).length !== 1 || !object7(value.apps))
|
|
47788
|
+
refuse();
|
|
47789
|
+
const apps = Object.entries(value.apps);
|
|
47790
|
+
if (!apps.length || apps.length > 256)
|
|
47791
|
+
refuse();
|
|
47792
|
+
for (const [name, entry] of apps) {
|
|
47793
|
+
if (!identifier2(name) || !object7(entry) || !Object.keys(entry).every((key) => ["id", "category", "required"].includes(key)) || typeof entry.id !== "string" || !/^[A-Za-z0-9_-]{1,256}$/.test(entry.id) || entry.category !== undefined && (typeof entry.category !== "string" || !entry.category.trim() || entry.category.length > 256 || /[\x00-\x1f\x7f]/.test(entry.category)) || entry.required !== undefined && typeof entry.required !== "boolean")
|
|
47794
|
+
refuse();
|
|
47795
|
+
}
|
|
47796
|
+
return sha2562;
|
|
47797
|
+
}
|
|
47798
|
+
function mcpControls(path, read) {
|
|
47799
|
+
const text2 = read(path);
|
|
47800
|
+
let value, sha2562;
|
|
47801
|
+
try {
|
|
47802
|
+
sha2562 = hashNativeJsonControls(text2);
|
|
47803
|
+
value = JSON.parse(text2);
|
|
47804
|
+
} catch {
|
|
47805
|
+
refuse();
|
|
47806
|
+
}
|
|
47807
|
+
const object7 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
47808
|
+
if (!object7(value) || Object.keys(value).length !== 1 || !object7(value.mcpServers))
|
|
47809
|
+
refuse();
|
|
47810
|
+
const servers = Object.entries(value.mcpServers);
|
|
47811
|
+
if (!servers.length || servers.length > 256)
|
|
47812
|
+
refuse();
|
|
47813
|
+
for (const [name, server] of servers)
|
|
47814
|
+
if (!isCodexLocalStdioMcpServer(name, server))
|
|
47815
|
+
refuse();
|
|
47816
|
+
return sha2562;
|
|
47817
|
+
}
|
|
47818
|
+
function isBundledCleanupHooks(root, namespace, value) {
|
|
47819
|
+
const object7 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
47820
|
+
const exactKeys = (v, keys) => Object.keys(v).length === keys.length && keys.every((key) => Object.hasOwn(v, key));
|
|
47821
|
+
if (basename5(dirname10(root)) !== namespace || basename5(dirname10(dirname10(root))) !== "openai-bundled")
|
|
47822
|
+
return false;
|
|
47823
|
+
const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
|
|
47824
|
+
if (!server || !object7(value) || !exactKeys(value, ["hooks"]) || !object7(value.hooks))
|
|
47825
|
+
return false;
|
|
47826
|
+
const events = Object.entries(value.hooks);
|
|
47827
|
+
if (!events.length || events.length > 3)
|
|
47828
|
+
return false;
|
|
47829
|
+
for (const [event, groups] of events) {
|
|
47830
|
+
if (!["Interrupt", "SubagentStop", "Stop"].includes(event) || !Array.isArray(groups) || groups.length !== 1)
|
|
47831
|
+
return false;
|
|
47832
|
+
const group = groups[0];
|
|
47833
|
+
if (!object7(group) || !exactKeys(group, ["hooks"]) || !Array.isArray(group.hooks) || group.hooks.length !== 1)
|
|
47834
|
+
return false;
|
|
47835
|
+
const handler = group.hooks[0];
|
|
47836
|
+
if (!object7(handler) || !exactKeys(handler, ["type", "server", "tool", "input"]) || handler.type !== "mcp_tool" || handler.server !== server || handler.tool !== "turn_ended" || !object7(handler.input) || !exactKeys(handler.input, ["hook_event_name", "session_id", "turn_id"]) || handler.input.hook_event_name !== "${hook_event_name}" || handler.input.turn_id !== "${turn_id}" || handler.input.session_id !== (event === "SubagentStop" ? "${agent_id}" : "${session_id}"))
|
|
47837
|
+
return false;
|
|
47838
|
+
}
|
|
47839
|
+
return true;
|
|
47840
|
+
}
|
|
47841
|
+
function rootControls(root, read) {
|
|
47842
|
+
const manifestPath = join16(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync11(manifestPath, { throwIfNoEntry: false });
|
|
47843
|
+
if (!lstatSync11(join16(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
|
|
47844
|
+
refuse();
|
|
47845
|
+
let manifest, manifestSha256;
|
|
47846
|
+
try {
|
|
47847
|
+
const text2 = read(manifestPath);
|
|
47848
|
+
manifestSha256 = hashNativeJsonControls(text2, "version");
|
|
47849
|
+
manifest = JSON.parse(text2);
|
|
47850
|
+
} catch {
|
|
47851
|
+
refuse();
|
|
47852
|
+
}
|
|
47853
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
47854
|
+
refuse();
|
|
47855
|
+
const cacheVersion = basename5(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
47856
|
+
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
47857
|
+
refuse();
|
|
47858
|
+
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" || lstatSync11(join16(root, "hooks"), { throwIfNoEntry: false }))
|
|
47859
|
+
refuse();
|
|
47860
|
+
const mcp = join16(root, ".mcp.json"), mcpPresent = lstatSync11(mcp, { throwIfNoEntry: false });
|
|
47861
|
+
if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
|
|
47862
|
+
refuse();
|
|
47863
|
+
if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync3(mcp) !== mcp))
|
|
47864
|
+
refuse();
|
|
47865
|
+
if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
|
|
47866
|
+
refuse();
|
|
47867
|
+
const mcpSha256 = mcpPresent ? mcpControls(mcp, read) : undefined;
|
|
47868
|
+
const app = join16(root, ".app.json"), present = lstatSync11(app, { throwIfNoEntry: false });
|
|
47869
|
+
if (present && (!present.isFile() || present.isSymbolicLink() || present.size > 1024 * 1024))
|
|
47870
|
+
refuse();
|
|
47871
|
+
if (manifest.apps !== undefined && !present)
|
|
47872
|
+
refuse();
|
|
47873
|
+
const appSha256 = present ? appControls(app, read) : undefined;
|
|
47874
|
+
return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
|
|
47875
|
+
}
|
|
47876
|
+
function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
47877
|
+
try {
|
|
47878
|
+
const parts = relative4(cache, parent).split(sep4), enrolled = controls.filter((control) => control.pluginParent === parent);
|
|
47879
|
+
if (resolve9(parent) !== parent || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !enrolled.length || !Array.isArray(rules))
|
|
47880
|
+
return false;
|
|
47881
|
+
for (let cursor = parent;; cursor = dirname10(cursor)) {
|
|
47882
|
+
const stat = lstatSync11(cursor, { throwIfNoEntry: false });
|
|
47883
|
+
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
47884
|
+
return false;
|
|
47885
|
+
if (dirname10(cursor) === cursor)
|
|
47886
|
+
break;
|
|
47887
|
+
}
|
|
47888
|
+
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 + sep4))) && rule.enabled !== false));
|
|
47889
|
+
} catch {
|
|
47890
|
+
return false;
|
|
47891
|
+
}
|
|
47892
|
+
}
|
|
47893
|
+
function absentDisabledCodexPluginParent(cache, parent, controls, rules) {
|
|
47894
|
+
return reviewedDisabledCodexPluginParent(cache, parent, controls, rules) && !lstatSync11(parent, { throwIfNoEntry: false });
|
|
47895
|
+
}
|
|
47896
|
+
function reviewedRetiredCodexRoot(cache, root, controls, rules, read, allowAbsent = false) {
|
|
47897
|
+
try {
|
|
47898
|
+
const parent = dirname10(root), parts = relative4(cache, parent).split(sep4), enrolled = controls.filter((control) => control.pluginParent === parent);
|
|
47899
|
+
if (resolve9(root) !== root || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !basename5(root) || basename5(root) === "latest" || !enrolled.length || !Array.isArray(rules))
|
|
47900
|
+
return false;
|
|
47901
|
+
for (let cursor = root;; cursor = dirname10(cursor)) {
|
|
47902
|
+
const stat = lstatSync11(cursor, { throwIfNoEntry: false });
|
|
47903
|
+
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
47904
|
+
return false;
|
|
47905
|
+
if (dirname10(cursor) === cursor)
|
|
47906
|
+
break;
|
|
47907
|
+
}
|
|
47908
|
+
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 + sep4))) && rule.enabled !== false)))
|
|
47909
|
+
return false;
|
|
47910
|
+
if (!lstatSync11(root, { throwIfNoEntry: false }))
|
|
47911
|
+
return allowAbsent && (lstatSync11(parent, { throwIfNoEntry: false }) ? enrolled.every((control) => remoteInstallationMatches(parent, control.remotePluginId, read)) : absentDisabledCodexPluginParent(cache, parent, controls, rules));
|
|
47912
|
+
if (!enrolled.every((control) => remoteInstallationMatches(parent, control.remotePluginId, read)))
|
|
47913
|
+
return false;
|
|
47914
|
+
const parsed = rootControls(root, read);
|
|
47915
|
+
if (JSON.parse(read(join16(root, ".codex-plugin/plugin.json"))).hooks !== undefined)
|
|
47916
|
+
return false;
|
|
47917
|
+
if (!enrolled.some((control) => control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && !parsed.appSha256 && !parsed.mcpSha256))
|
|
47918
|
+
return false;
|
|
47919
|
+
let entries = 0;
|
|
47920
|
+
const emptySkills = (directory, depth) => depth <= 64 && readdirSync6(directory).every((name) => {
|
|
47921
|
+
if (++entries > 4096 || name === "SKILL.md")
|
|
47922
|
+
return false;
|
|
47923
|
+
const path = join16(directory, name), stat = lstatSync11(path);
|
|
47924
|
+
return !stat.isSymbolicLink() && (stat.isFile() || stat.isDirectory() && emptySkills(path, depth + 1));
|
|
47925
|
+
});
|
|
47926
|
+
return emptySkills(root, 0);
|
|
47927
|
+
} catch {
|
|
47928
|
+
return false;
|
|
47929
|
+
}
|
|
47930
|
+
}
|
|
47931
|
+
function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read, rules) {
|
|
47932
|
+
if (!Array.isArray(controls) || controls.length > 4096)
|
|
47933
|
+
return false;
|
|
47934
|
+
let entries = 0;
|
|
47935
|
+
try {
|
|
47936
|
+
for (const parent of new Set(controls.map((control) => control.pluginParent))) {
|
|
47937
|
+
if (absentDisabledCodexPluginParent(cache, parent, controls, rules))
|
|
47938
|
+
continue;
|
|
47939
|
+
if (!parent.startsWith(cache + sep4) || !lstatSync11(parent, { throwIfNoEntry: false })?.isDirectory() || realpathSync3(parent) !== parent)
|
|
47940
|
+
return false;
|
|
47941
|
+
for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
|
|
47942
|
+
if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read))
|
|
47943
|
+
return false;
|
|
47944
|
+
}
|
|
47945
|
+
const names = readdirSync6(parent);
|
|
47946
|
+
entries += names.length;
|
|
47947
|
+
if (entries > 4096)
|
|
47948
|
+
return false;
|
|
47949
|
+
for (const name of names) {
|
|
47950
|
+
let root = join16(parent, name), stat = lstatSync11(root);
|
|
47951
|
+
if (stat.isSymbolicLink()) {
|
|
47952
|
+
const target = resolve9(parent, readlinkSync3(root));
|
|
47953
|
+
if (name !== "latest" || dirname10(target) !== parent || !lstatSync11(target).isDirectory() || realpathSync3(target) !== target)
|
|
47954
|
+
return false;
|
|
47955
|
+
root = target;
|
|
47956
|
+
stat = lstatSync11(root);
|
|
47957
|
+
}
|
|
47958
|
+
if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync11(join16(root, path), { throwIfNoEntry: false })))
|
|
47959
|
+
continue;
|
|
47960
|
+
const parsed = rootControls(root, read);
|
|
47961
|
+
if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256 && control.mcpSha256 === parsed.mcpSha256))
|
|
47962
|
+
return false;
|
|
47963
|
+
}
|
|
47964
|
+
}
|
|
47965
|
+
return true;
|
|
47966
|
+
} catch {
|
|
47967
|
+
return false;
|
|
47968
|
+
}
|
|
47969
|
+
}
|
|
47688
47970
|
function sourceInputUnchanged(input2, cache, read) {
|
|
47689
47971
|
try {
|
|
47690
47972
|
const parts = relative4(cache, input2.pluginParent).split(sep4);
|
|
@@ -47709,7 +47991,7 @@ function reviewedCodexPluginSourceRoots(cache, inputs, read) {
|
|
|
47709
47991
|
refuse();
|
|
47710
47992
|
return roots;
|
|
47711
47993
|
}
|
|
47712
|
-
var identifier2 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v);
|
|
47994
|
+
var identifier2 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v), CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
|
|
47713
47995
|
var init_codex_plugin_skill_controls = __esm(() => {
|
|
47714
47996
|
init_claude_settings_witness();
|
|
47715
47997
|
init_codex_native_compatibility();
|
|
@@ -48235,6 +48517,7 @@ var init_codex_hook_trust_files = () => {};
|
|
|
48235
48517
|
|
|
48236
48518
|
// src/lib/agent-integration.ts
|
|
48237
48519
|
var init_agent_integration = __esm(() => {
|
|
48520
|
+
init_codex_corpus_write();
|
|
48238
48521
|
init_codex_plugin_skill_controls();
|
|
48239
48522
|
init_codex_native_skill_catalog();
|
|
48240
48523
|
init_codex_settings_witness();
|
|
@@ -48261,6 +48544,7 @@ var init_home_migration = __esm(() => {
|
|
|
48261
48544
|
|
|
48262
48545
|
// src/lib/agent-sync.ts
|
|
48263
48546
|
var init_agent_sync = __esm(() => {
|
|
48547
|
+
init_codex_corpus_write();
|
|
48264
48548
|
init_agent_integration();
|
|
48265
48549
|
init_portable_skills();
|
|
48266
48550
|
init_home_migration();
|
|
@@ -48502,6 +48786,7 @@ function readBundledSkillVersion(name) {
|
|
|
48502
48786
|
}
|
|
48503
48787
|
var AGENT_TARGETS, AGENT_LABELS;
|
|
48504
48788
|
var init_installer = __esm(() => {
|
|
48789
|
+
init_codex_corpus_write();
|
|
48505
48790
|
init_agent_sync();
|
|
48506
48791
|
init_agent_integration();
|
|
48507
48792
|
init_portable_skills();
|
|
@@ -49921,6 +50206,94 @@ function projected(source, changes, budget = discoveryByteBudget()) {
|
|
|
49921
50206
|
return digest(text2);
|
|
49922
50207
|
return projectNativeDiscoveryFields(text2, source.format, source.fields ?? [], source.path);
|
|
49923
50208
|
}
|
|
50209
|
+
function absentCodexSkillRoots(binding) {
|
|
50210
|
+
const controls = binding.codexDisabledPluginSkills;
|
|
50211
|
+
if (controls === undefined || Array.isArray(controls) && !controls.length)
|
|
50212
|
+
return [];
|
|
50213
|
+
if (binding.agent !== "codex" || !Array.isArray(controls) || controls.length > 4096)
|
|
50214
|
+
throw new Error("Invalid disabled Codex discovery proof");
|
|
50215
|
+
if (binding.method !== "reviewed" || controls.every((control) => lstatSync18(control.pluginParent, { throwIfNoEntry: false })))
|
|
50216
|
+
return [];
|
|
50217
|
+
const cache = dirname15(dirname15(controls[0].pluginParent)), config2 = join25(dirname15(dirname15(cache)), "config.toml");
|
|
50218
|
+
const source = binding.sources.find((source2) => source2.path === config2 && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
50219
|
+
if (!source || projected(source) !== source.sha256)
|
|
50220
|
+
throw new Error("Missing current disabled Codex configuration witness");
|
|
50221
|
+
const rules = Bun.TOML.parse(read(config2)).skills?.config ?? [];
|
|
50222
|
+
return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
|
|
50223
|
+
}
|
|
50224
|
+
function codexDirectHookSelects(cache, root) {
|
|
50225
|
+
const codex = dirname15(dirname15(cache)), config2 = Bun.TOML.parse(read(join25(codex, "config.toml")) ?? "");
|
|
50226
|
+
const hooks = read(join25(codex, "hooks.json"));
|
|
50227
|
+
if (hooks !== null)
|
|
50228
|
+
hashNativeJsonControls(hooks);
|
|
50229
|
+
return JSON.stringify(config2.hooks ?? {}).includes(root) || hooks !== null && JSON.stringify(JSON.parse(hooks)).includes(root);
|
|
50230
|
+
}
|
|
50231
|
+
function retiredCodexRoots(binding) {
|
|
50232
|
+
const proof = binding.codexRetiredMaterializations;
|
|
50233
|
+
if (proof === undefined)
|
|
50234
|
+
return [];
|
|
50235
|
+
const controls = binding.codexDisabledPluginSkills;
|
|
50236
|
+
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)
|
|
50237
|
+
throw new Error("Invalid retired Codex materialization proof");
|
|
50238
|
+
const cache = dirname15(dirname15(controls[0].pluginParent)), config2 = join25(dirname15(dirname15(cache)), "config.toml");
|
|
50239
|
+
const source = binding.sources.find((source2) => source2.path === config2 && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
50240
|
+
if (!source || projected(source) !== source.sha256)
|
|
50241
|
+
throw new Error("Missing current retired Codex configuration witness");
|
|
50242
|
+
const rules = Bun.TOML.parse(read(config2)).skills?.config ?? [];
|
|
50243
|
+
if ((proof.parents ?? []).some((parent) => codexDirectHookSelects(cache, parent) || !reviewedDisabledCodexPluginParent(cache, parent, controls, rules)) || !reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read(path), rules))
|
|
50244
|
+
throw new Error("Retired Codex parent capabilities changed");
|
|
50245
|
+
for (const root of proof.roots) {
|
|
50246
|
+
const manifest = binding.sources.find((source2) => source2.path === join25(root, ".codex-plugin/plugin.json") && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && [undefined, "bytes"].includes(source2.hashMode));
|
|
50247
|
+
if (!manifest || codexDirectHookSelects(cache, root) || !reviewedRetiredCodexRoot(cache, root, controls, rules, (path) => read(path), true))
|
|
50248
|
+
throw new Error("Retired Codex materialization changed");
|
|
50249
|
+
}
|
|
50250
|
+
return proof.roots;
|
|
50251
|
+
}
|
|
50252
|
+
function absentRetiredClaudeRoots(binding) {
|
|
50253
|
+
if (binding.agent !== "claude" || binding.method !== "reviewed")
|
|
50254
|
+
return [];
|
|
50255
|
+
const result = new Set;
|
|
50256
|
+
for (const source of binding.sources) {
|
|
50257
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || ![undefined, "bytes"].includes(source.hashMode) || source.sha256 === null)
|
|
50258
|
+
continue;
|
|
50259
|
+
const marker = sep5 + ".claude" + sep5 + "plugins" + sep5 + "cache" + sep5, at = source.path.indexOf(marker);
|
|
50260
|
+
if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
|
|
50261
|
+
continue;
|
|
50262
|
+
const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(sep5);
|
|
50263
|
+
if (parts.length < 4 || parts.slice(0, 3).some((part) => !part || part === "." || part === ".."))
|
|
50264
|
+
continue;
|
|
50265
|
+
const root = join25(cache, ...parts.slice(0, 3));
|
|
50266
|
+
safe4(root);
|
|
50267
|
+
if (lstatSync18(root, { throwIfNoEntry: false }))
|
|
50268
|
+
continue;
|
|
50269
|
+
const claude = dirname15(dirname15(cache)), settings = join25(claude, "settings.json"), registry2 = join25(claude, "plugins/installed_plugins.json");
|
|
50270
|
+
const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
|
|
50271
|
+
const configSource = full(settings), registrySource = full(registry2);
|
|
50272
|
+
if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
|
|
50273
|
+
continue;
|
|
50274
|
+
const settingsText = read(settings), registryText = read(registry2);
|
|
50275
|
+
hashNativeJsonControls(settingsText);
|
|
50276
|
+
hashNativeJsonControls(registryText);
|
|
50277
|
+
const config2 = JSON.parse(settingsText), installed = JSON.parse(registryText), id = `${parts[1]}@${parts[0]}`;
|
|
50278
|
+
if (installed.version !== 2 || !installed.plugins || typeof installed.plugins !== "object" || Array.isArray(installed.plugins) || config2.enabledPlugins?.[id] !== true)
|
|
50279
|
+
continue;
|
|
50280
|
+
if (JSON.stringify(config2.hooks ?? {}).includes(root))
|
|
50281
|
+
continue;
|
|
50282
|
+
const rows = installed.plugins[id];
|
|
50283
|
+
if (!Array.isArray(rows) || rows.length !== 1 || rows[0]?.scope !== "user" || typeof rows[0]?.installPath !== "string")
|
|
50284
|
+
continue;
|
|
50285
|
+
const active = rows[0].installPath;
|
|
50286
|
+
if (rows[0].version !== basename8(active) || resolve15(active) !== active || dirname15(active) !== dirname15(root) || active === root || !lstatSync18(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync5(active) !== active)
|
|
50287
|
+
continue;
|
|
50288
|
+
const activeManifest = full(join25(active, ".claude-plugin/plugin.json"));
|
|
50289
|
+
if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
|
|
50290
|
+
continue;
|
|
50291
|
+
if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root || row.installPath.startsWith(root + sep5) || root.startsWith(row.installPath + sep5))))
|
|
50292
|
+
continue;
|
|
50293
|
+
result.add(root);
|
|
50294
|
+
}
|
|
50295
|
+
return [...result];
|
|
50296
|
+
}
|
|
49924
50297
|
function verifyAgentDiscovery(binding, codexRecovery) {
|
|
49925
50298
|
if (codexRecovery)
|
|
49926
50299
|
assertCodexHookDiscoveryRecovery(binding, codexRecovery);
|
|
@@ -49929,15 +50302,33 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
49929
50302
|
if (binding.agent === "hermes" && !binding.directories?.length)
|
|
49930
50303
|
throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
|
|
49931
50304
|
const installationRoots = codexInstallationRoots(binding);
|
|
50305
|
+
const retiredRoots = retiredCodexRoots(binding);
|
|
49932
50306
|
const installationInput = (path) => installationRoots.some((root) => path === root || path.startsWith(root + sep5));
|
|
49933
50307
|
if (binding.directories !== undefined) {
|
|
49934
50308
|
const projected2 = binding.codexInstallationInputs?.directories ?? [];
|
|
49935
50309
|
const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root) => root.startsWith(directory.path + sep5)));
|
|
49936
50310
|
if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
|
|
49937
50311
|
throw new Error("Missing installation input directory projection");
|
|
49938
|
-
|
|
50312
|
+
const ordinary = binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory));
|
|
50313
|
+
if (binding.codexRetiredMaterializations) {
|
|
50314
|
+
const projection = binding.codexRetiredMaterializations.directories;
|
|
50315
|
+
if (projection.length !== ordinary.length || projection.some((item, index) => item.path !== ordinary[index]?.path))
|
|
50316
|
+
throw new Error("Missing retired Codex directory projection");
|
|
50317
|
+
const omitted = [...retiredRoots, ...(binding.codexRetiredMaterializations.parents ?? []).filter((parent) => !lstatSync18(parent, { throwIfNoEntry: false }))];
|
|
50318
|
+
if (projection.every((item) => Array.isArray(item.entries)))
|
|
50319
|
+
verifyDiscoveryDirectoryProjection(projection, omitted);
|
|
50320
|
+
else {
|
|
50321
|
+
if (omitted.some((root) => !retiredRoots.includes(root)))
|
|
50322
|
+
throw new Error("Missing reviewed directory membership inventory; provide a fresh discovery review");
|
|
50323
|
+
const current = captureDiscoveryDirectoryProjection(ordinary.map((item) => item.path), retiredRoots).map(({ entries, ...item }) => item);
|
|
50324
|
+
if (!isDeepStrictEqual3(current, projection))
|
|
50325
|
+
throw new Error("Native discovery directory membership changed outside retired Codex roots");
|
|
50326
|
+
}
|
|
50327
|
+
} else
|
|
50328
|
+
verifyDiscoveryDirectories(ordinary);
|
|
49939
50329
|
verifyDiscoveryDirectories(projected2, installationRoots);
|
|
49940
50330
|
}
|
|
50331
|
+
const absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root) => !lstatSync18(root, { throwIfNoEntry: false }))];
|
|
49941
50332
|
const budget = discoveryByteBudget();
|
|
49942
50333
|
for (const source of binding.sources) {
|
|
49943
50334
|
assertMarketplaceBinding(binding, source);
|
|
@@ -49951,12 +50342,21 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
49951
50342
|
safe4(source.path);
|
|
49952
50343
|
continue;
|
|
49953
50344
|
}
|
|
50345
|
+
if (absentRoots.some((root) => source.path.startsWith(root + sep5)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)) {
|
|
50346
|
+
safe4(source.path);
|
|
50347
|
+
continue;
|
|
50348
|
+
}
|
|
49954
50349
|
const current = projected(source, undefined, budget);
|
|
49955
50350
|
if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
|
|
49956
50351
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
49957
50352
|
}
|
|
49958
50353
|
for (const root of binding.roots)
|
|
49959
50354
|
safe4(root);
|
|
50355
|
+
for (const root of absentRoots) {
|
|
50356
|
+
safe4(root);
|
|
50357
|
+
if (lstatSync18(root, { throwIfNoEntry: false }))
|
|
50358
|
+
throw new Error("Retired native materialization changed during discovery verification");
|
|
50359
|
+
}
|
|
49960
50360
|
}
|
|
49961
50361
|
function codexInstallationRoots(binding) {
|
|
49962
50362
|
const proof = binding.codexInstallationInputs;
|
package/bin/migrate.js
CHANGED
|
@@ -7,7 +7,7 @@ import { join as join5 } from "path";
|
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "@hasna/skills",
|
|
10
|
-
version: "0.10.
|
|
10
|
+
version: "0.10.39",
|
|
11
11
|
description: "Skills library for AI coding agents",
|
|
12
12
|
type: "module",
|
|
13
13
|
bin: {
|
|
@@ -57,8 +57,8 @@ var package_default = {
|
|
|
57
57
|
types: "./dist/index.d.ts",
|
|
58
58
|
scripts: {
|
|
59
59
|
clean: "rm -rf bin/ dist/",
|
|
60
|
-
build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
61
|
-
"build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
60
|
+
build: "bun run clean && bun build --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @hasna/contracts/kernel-lock ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
61
|
+
"build:js": "rm -rf dist && bun build --external @hasna/contracts/kernel-lock ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
62
62
|
test: "bun test --timeout 30000",
|
|
63
63
|
dev: "bun run ./src/cli/index.tsx",
|
|
64
64
|
"dev:watch": "bun --watch run ./src/cli/index.tsx",
|
|
@@ -96,7 +96,6 @@ var package_default = {
|
|
|
96
96
|
author: "Hasna",
|
|
97
97
|
license: "Apache-2.0",
|
|
98
98
|
devDependencies: {
|
|
99
|
-
"@hasna/contracts": "1.1.0",
|
|
100
99
|
"@types/bun": "1.3.14",
|
|
101
100
|
"@types/node": "25.2.3",
|
|
102
101
|
"@types/react": "^18.2.0",
|
|
@@ -118,7 +117,8 @@ var package_default = {
|
|
|
118
117
|
"ink-spinner": "^5.0.0",
|
|
119
118
|
"ink-text-input": "^6.0.0",
|
|
120
119
|
react: "^18.2.0",
|
|
121
|
-
zod: "^4.3.6"
|
|
120
|
+
zod: "^4.3.6",
|
|
121
|
+
"@hasna/contracts": "1.3.5"
|
|
122
122
|
},
|
|
123
123
|
engines: {
|
|
124
124
|
bun: ">=1.0.0"
|