@hasna/skills 0.10.19 → 0.10.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -12
- package/bin/index.js +761 -443
- package/bin/mcp.js +308 -119
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +520 -207
- package/dist/lib/agent-discovery.d.ts +4 -1
- package/dist/lib/agent-integration.d.ts +4 -0
- package/dist/lib/codex-hook-rpc.d.ts +1 -1
- package/dist/lib/codex-native-compatibility.d.ts +3 -0
- package/dist/lib/codex-native-skill-catalog.d.ts +20 -0
- package/dist/lib/codex-plugin-skill-controls.d.ts +2 -1
- package/dist/lib/codex-settings-witness-v1.d.ts +12 -0
- package/dist/lib/codex-settings-witness.d.ts +13 -5
- package/dist/sdk/index.js +594 -284
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7262,7 +7262,7 @@ import {
|
|
|
7262
7262
|
statSync as statSync10,
|
|
7263
7263
|
writeFileSync as writeFileSync8
|
|
7264
7264
|
} from "fs";
|
|
7265
|
-
import { basename as
|
|
7265
|
+
import { basename as basename11, dirname as dirname19, isAbsolute as isAbsolute17, join as join29, normalize as normalize4, relative as relative6, resolve as resolve17, sep as sep8 } from "path";
|
|
7266
7266
|
|
|
7267
7267
|
// src/lib/registry-data/index.ts
|
|
7268
7268
|
var SKILLS = [];
|
|
@@ -8920,7 +8920,7 @@ var NATIVE_SKILL_ROOTS = [
|
|
|
8920
8920
|
// src/lib/authoring-path.ts
|
|
8921
8921
|
import { lstatSync as lstatSync22, realpathSync as realpathSync8 } from "fs";
|
|
8922
8922
|
import { homedir as homedir6 } from "os";
|
|
8923
|
-
import { basename as
|
|
8923
|
+
import { basename as basename10, dirname as dirname18, join as join28, resolve as resolve16, sep as sep7 } from "path";
|
|
8924
8924
|
|
|
8925
8925
|
// src/lib/agent-adapters.ts
|
|
8926
8926
|
var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes"];
|
|
@@ -9038,12 +9038,14 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9038
9038
|
}
|
|
9039
9039
|
if (bridge.codexPluginSkills !== undefined)
|
|
9040
9040
|
for (const control of array(bridge.codexPluginSkills, 4096)) {
|
|
9041
|
-
requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256"].includes(key)));
|
|
9041
|
+
requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "remotePluginId"].includes(key)));
|
|
9042
9042
|
text(control.name, 129);
|
|
9043
9043
|
text(control.pluginId, 1024);
|
|
9044
9044
|
text(control.namespace, 64);
|
|
9045
9045
|
text(control.pluginParent);
|
|
9046
9046
|
requireBound(isAbsolute2(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
|
|
9047
|
+
if (control.remotePluginId !== undefined)
|
|
9048
|
+
requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
|
|
9047
9049
|
if (control.appSha256 !== undefined)
|
|
9048
9050
|
requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
|
|
9049
9051
|
}
|
|
@@ -9066,8 +9068,8 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9066
9068
|
text(source.path);
|
|
9067
9069
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
9068
9070
|
if (source.hashMode !== undefined)
|
|
9069
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9070
|
-
if (source.hashMode === "codex-settings-v1")
|
|
9071
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9072
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2")
|
|
9071
9073
|
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "config.toml");
|
|
9072
9074
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
9073
9075
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "settings.json");
|
|
@@ -9103,10 +9105,10 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9103
9105
|
}
|
|
9104
9106
|
|
|
9105
9107
|
// src/lib/agent-discovery.ts
|
|
9106
|
-
import { createHash as
|
|
9108
|
+
import { createHash as createHash18 } from "crypto";
|
|
9107
9109
|
import { lstatSync as lstatSync21, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync7 } from "fs";
|
|
9108
|
-
import { basename as
|
|
9109
|
-
import { isDeepStrictEqual as
|
|
9110
|
+
import { basename as basename9, dirname as dirname17, isAbsolute as isAbsolute16, join as join27, resolve as resolve15, sep as sep6 } from "path";
|
|
9111
|
+
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
9110
9112
|
|
|
9111
9113
|
// node_modules/yaml/dist/index.js
|
|
9112
9114
|
var composer = require_composer();
|
|
@@ -9616,7 +9618,7 @@ function hashDiscoveryPathFile(path, budget, changes) {
|
|
|
9616
9618
|
|
|
9617
9619
|
// src/lib/plugin-discovery.ts
|
|
9618
9620
|
import { closeSync as closeSync10, constants as constants9, fstatSync as fstatSync10, lstatSync as lstatSync18, openSync as openSync10, opendirSync as opendirSync5, readSync as readSync8 } from "fs";
|
|
9619
|
-
import { basename as
|
|
9621
|
+
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute14, join as join26, resolve as resolve13 } from "path";
|
|
9620
9622
|
|
|
9621
9623
|
// src/lib/plugin-admission.ts
|
|
9622
9624
|
import { homedir as homedir5 } from "os";
|
|
@@ -10877,7 +10879,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
10877
10879
|
// package.json
|
|
10878
10880
|
var package_default = {
|
|
10879
10881
|
name: "@hasna/skills",
|
|
10880
|
-
version: "0.10.
|
|
10882
|
+
version: "0.10.22",
|
|
10881
10883
|
description: "Skills library for AI coding agents",
|
|
10882
10884
|
type: "module",
|
|
10883
10885
|
bin: {
|
|
@@ -12710,7 +12712,7 @@ import {
|
|
|
12710
12712
|
writeFileSync as writeFileSync5
|
|
12711
12713
|
} from "fs";
|
|
12712
12714
|
import { homedir as homedir3 } from "os";
|
|
12713
|
-
import { basename as
|
|
12715
|
+
import { basename as basename6, dirname as dirname12, join as join18 } from "path";
|
|
12714
12716
|
|
|
12715
12717
|
// src/lib/claude-settings-witness.ts
|
|
12716
12718
|
import { createHash as createHash8 } from "crypto";
|
|
@@ -13024,9 +13026,28 @@ function hashNativeJsonControls(text2, excludedMetadata) {
|
|
|
13024
13026
|
}
|
|
13025
13027
|
|
|
13026
13028
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13027
|
-
import { dirname as dirname10, join as join14, resolve as resolve9, sep as sep4 } from "path";
|
|
13029
|
+
import { basename as basename5, dirname as dirname10, join as join14, relative as relative4, resolve as resolve9, sep as sep4 } from "path";
|
|
13028
13030
|
import { lstatSync as lstatSync12, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "fs";
|
|
13029
13031
|
|
|
13032
|
+
// src/lib/codex-native-compatibility.ts
|
|
13033
|
+
var MEASURED_CODEX_CAPABILITIES = {
|
|
13034
|
+
"codex-cli 0.153.0": ["hooks"],
|
|
13035
|
+
"codex-cli 0.154.0": ["hooks"],
|
|
13036
|
+
"codex-cli 0.155.0": ["hooks"],
|
|
13037
|
+
"codex-cli 0.155.1": ["hooks"],
|
|
13038
|
+
"codex-cli 0.156.1": ["hooks"],
|
|
13039
|
+
"codex-cli 0.157.0": ["hooks"],
|
|
13040
|
+
"codex-cli 0.157.1": ["hooks"],
|
|
13041
|
+
"codex-cli 0.158.0": ["hooks"],
|
|
13042
|
+
"codex-cli 0.159.0": ["hooks"],
|
|
13043
|
+
"codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
|
|
13044
|
+
"codex-cli 0.160.0": ["hooks", "qualified-skill-catalog"]
|
|
13045
|
+
};
|
|
13046
|
+
function supportsCodexNativeCapability(version, capability) {
|
|
13047
|
+
return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
|
|
13048
|
+
}
|
|
13049
|
+
var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
|
|
13050
|
+
|
|
13030
13051
|
// src/lib/codex-native-skill-catalog.ts
|
|
13031
13052
|
import { realpathSync as realpathSync4 } from "fs";
|
|
13032
13053
|
import { isAbsolute as isAbsolute10, normalize as normalize2 } from "path";
|
|
@@ -13036,7 +13057,6 @@ import { execFile, spawn } from "child_process";
|
|
|
13036
13057
|
import { realpathSync as realpathSync3, statSync as statSync7 } from "fs";
|
|
13037
13058
|
import { isAbsolute as isAbsolute9 } from "path";
|
|
13038
13059
|
import { promisify } from "util";
|
|
13039
|
-
var SUPPORTED_CODEX_HOOK_VERSIONS = ["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.155.0", "codex-cli 0.155.1", "codex-cli 0.156.1", "codex-cli 0.157.0", "codex-cli 0.157.1", "codex-cli 0.158.0", "codex-cli 0.159.0", "codex-cli 0.159.2"];
|
|
13040
13060
|
async function connectCodexHookRpc(options) {
|
|
13041
13061
|
const timeoutMs = options.timeoutMs ?? 20000;
|
|
13042
13062
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 20000)
|
|
@@ -13054,7 +13074,7 @@ async function connectCodexHookRpc(options) {
|
|
|
13054
13074
|
} catch {
|
|
13055
13075
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE");
|
|
13056
13076
|
}
|
|
13057
|
-
if (!
|
|
13077
|
+
if (!supportsCodexNativeCapability(version, "hooks"))
|
|
13058
13078
|
throw new Error("CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED_VERSION");
|
|
13059
13079
|
const current = statSync7(binary);
|
|
13060
13080
|
if (realpathSync3(found) !== binary || ["dev", "ino", "size", "mtimeMs", "ctimeMs"].some((k) => stat[k] !== current[k]))
|
|
@@ -13178,12 +13198,13 @@ async function connectCodexHookRpc(options) {
|
|
|
13178
13198
|
var MAX_SKILLS = 4096;
|
|
13179
13199
|
var MAX_NAME_BYTES = 1024;
|
|
13180
13200
|
var MAX_PATH_BYTES = 16384;
|
|
13181
|
-
var
|
|
13201
|
+
var MAX_PLUGIN_ID_BYTES = 1024;
|
|
13182
13202
|
function refuse() {
|
|
13183
13203
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_INVALID");
|
|
13184
13204
|
}
|
|
13185
13205
|
var object4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13186
13206
|
var scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value);
|
|
13207
|
+
var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
|
|
13187
13208
|
var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute10(value);
|
|
13188
13209
|
function projectCodexNativeSkillCatalog(response, cwd) {
|
|
13189
13210
|
if (!absolutePath2(cwd) || !object4(response) || !Array.isArray(response.data) || response.data.length !== 1)
|
|
@@ -13201,6 +13222,47 @@ function projectCodexNativeSkillCatalog(response, cwd) {
|
|
|
13201
13222
|
return { name: value.name, path: value.path, enabled: value.enabled, pluginId: value.pluginId ?? null };
|
|
13202
13223
|
});
|
|
13203
13224
|
}
|
|
13225
|
+
function projectCodexInstalledPlugins(response) {
|
|
13226
|
+
if (!object4(response) || !Array.isArray(response.marketplaces) || !Array.isArray(response.marketplaceLoadErrors) || response.marketplaceLoadErrors.length !== 0 || response.marketplaces.length > MAX_SKILLS)
|
|
13227
|
+
refuse();
|
|
13228
|
+
const plugins = [];
|
|
13229
|
+
for (const marketplace of response.marketplaces) {
|
|
13230
|
+
if (!object4(marketplace) || !scalar(marketplace.name, MAX_NAME_BYTES) || !Array.isArray(marketplace.plugins) || marketplace.plugins.length > MAX_SKILLS)
|
|
13231
|
+
refuse();
|
|
13232
|
+
for (const value of marketplace.plugins) {
|
|
13233
|
+
if (!object4(value) || !scalar(value.id, MAX_PLUGIN_ID_BYTES) || !scalar(value.name, MAX_NAME_BYTES) || value.id !== `${value.name}@${marketplace.name}`)
|
|
13234
|
+
refuse();
|
|
13235
|
+
plugins.push({
|
|
13236
|
+
...value,
|
|
13237
|
+
marketplace: marketplace.name,
|
|
13238
|
+
sourceType: object4(value.source) && value.source.type === "remote" ? "remote" : undefined
|
|
13239
|
+
});
|
|
13240
|
+
}
|
|
13241
|
+
}
|
|
13242
|
+
return projectCodexInstalledPluginEntries(plugins);
|
|
13243
|
+
}
|
|
13244
|
+
function projectCodexInstalledPluginEntries(value) {
|
|
13245
|
+
if (!Array.isArray(value) || value.length > MAX_SKILLS)
|
|
13246
|
+
refuse();
|
|
13247
|
+
const ids = new Set;
|
|
13248
|
+
return value.map((item) => {
|
|
13249
|
+
if (!object4(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && item.sourceType !== "remote")
|
|
13250
|
+
refuse();
|
|
13251
|
+
const at = item.id.lastIndexOf("@");
|
|
13252
|
+
if (at < 1 || item.id.slice(0, at) !== item.name || at === item.id.length - 1 || ids.has(item.id))
|
|
13253
|
+
refuse();
|
|
13254
|
+
ids.add(item.id);
|
|
13255
|
+
return {
|
|
13256
|
+
id: item.id,
|
|
13257
|
+
name: item.name,
|
|
13258
|
+
installed: item.installed,
|
|
13259
|
+
enabled: item.enabled,
|
|
13260
|
+
localVersion: item.localVersion ?? null,
|
|
13261
|
+
...item.sourceType === "remote" ? { sourceType: "remote" } : {},
|
|
13262
|
+
...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
|
|
13263
|
+
};
|
|
13264
|
+
});
|
|
13265
|
+
}
|
|
13204
13266
|
async function captureCodexNativeSkillCatalog(options, connect = connectCodexHookRpc) {
|
|
13205
13267
|
if (!absolutePath2(options.cwd) || !absolutePath2(options.home))
|
|
13206
13268
|
refuse();
|
|
@@ -13211,10 +13273,16 @@ async function captureCodexNativeSkillCatalog(options, connect = connectCodexHoo
|
|
|
13211
13273
|
...options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }
|
|
13212
13274
|
});
|
|
13213
13275
|
try {
|
|
13214
|
-
if (!
|
|
13276
|
+
if (!supportsCodexNativeCapability(rpc.version, "qualified-skill-catalog"))
|
|
13215
13277
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_UNSUPPORTED_VERSION");
|
|
13216
13278
|
const response = await rpc.request("skills/list", { cwds: [options.cwd], forceReload: true });
|
|
13217
|
-
|
|
13279
|
+
const installed = await rpc.request("plugin/installed", { cwds: [options.cwd] });
|
|
13280
|
+
return {
|
|
13281
|
+
version: rpc.version,
|
|
13282
|
+
cwd: options.cwd,
|
|
13283
|
+
skills: projectCodexNativeSkillCatalog(response, options.cwd),
|
|
13284
|
+
plugins: projectCodexInstalledPlugins(installed)
|
|
13285
|
+
};
|
|
13218
13286
|
} finally {
|
|
13219
13287
|
await rpc.close();
|
|
13220
13288
|
}
|
|
@@ -13259,9 +13327,25 @@ function isCodexNativeSkillDisabled(skill, rules) {
|
|
|
13259
13327
|
|
|
13260
13328
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13261
13329
|
var identifier2 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v);
|
|
13330
|
+
var CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
|
|
13262
13331
|
function refuse2() {
|
|
13263
13332
|
throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
|
|
13264
13333
|
}
|
|
13334
|
+
function remoteInstallationMatches(parent, remotePluginId, read2) {
|
|
13335
|
+
try {
|
|
13336
|
+
if (!remotePluginIdentifier(remotePluginId) || !lstatSync12(parent).isDirectory() || realpathSync5(parent) !== parent)
|
|
13337
|
+
return false;
|
|
13338
|
+
const path = join14(parent, ".codex-remote-plugin-install.json"), stat = lstatSync12(path, { throwIfNoEntry: false });
|
|
13339
|
+
if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
|
|
13340
|
+
return false;
|
|
13341
|
+
const text2 = read2(path);
|
|
13342
|
+
hashNativeJsonControls(text2);
|
|
13343
|
+
const value = JSON.parse(text2);
|
|
13344
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 2 && value.schema_version === 1 && value.remote_plugin_id === remotePluginId);
|
|
13345
|
+
} catch {
|
|
13346
|
+
return false;
|
|
13347
|
+
}
|
|
13348
|
+
}
|
|
13265
13349
|
function appControls(path, read2) {
|
|
13266
13350
|
const text2 = read2(path);
|
|
13267
13351
|
let value, sha2562;
|
|
@@ -13297,6 +13381,9 @@ function rootControls(root, read2) {
|
|
|
13297
13381
|
}
|
|
13298
13382
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
13299
13383
|
refuse2();
|
|
13384
|
+
const cacheVersion = basename5(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
13385
|
+
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
13386
|
+
refuse2();
|
|
13300
13387
|
if (manifest.hooks !== undefined || manifest.mcpServers !== undefined || manifest.commands !== undefined || manifest.apps !== undefined && manifest.apps !== "./.app.json" || ["hooks", ".mcp.json"].some((path) => lstatSync12(join14(root, path), { throwIfNoEntry: false })))
|
|
13301
13388
|
refuse2();
|
|
13302
13389
|
const app = join14(root, ".app.json"), present = lstatSync12(app, { throwIfNoEntry: false });
|
|
@@ -13348,6 +13435,10 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
|
|
|
13348
13435
|
for (const parent of new Set(controls.map((control) => control.pluginParent))) {
|
|
13349
13436
|
if (!parent.startsWith(cache + sep4) || !lstatSync12(parent, { throwIfNoEntry: false })?.isDirectory())
|
|
13350
13437
|
return false;
|
|
13438
|
+
for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
|
|
13439
|
+
if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read2))
|
|
13440
|
+
return false;
|
|
13441
|
+
}
|
|
13351
13442
|
const names = readdirSync6(parent);
|
|
13352
13443
|
entries += names.length;
|
|
13353
13444
|
if (entries > 4096)
|
|
@@ -13373,22 +13464,56 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
|
|
|
13373
13464
|
return false;
|
|
13374
13465
|
}
|
|
13375
13466
|
}
|
|
13376
|
-
function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2) {
|
|
13377
|
-
if (catalog?.version
|
|
13467
|
+
function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, rules) {
|
|
13468
|
+
if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
|
|
13378
13469
|
refuse2();
|
|
13379
13470
|
const skills = projectCodexNativeSkillCatalog({ data: [{ cwd, errors: [], skills: catalog.skills }] }, cwd);
|
|
13380
13471
|
const allowed = new Set(documents), result = [];
|
|
13381
|
-
|
|
13382
|
-
|
|
13472
|
+
let installedPlugins;
|
|
13473
|
+
try {
|
|
13474
|
+
installedPlugins = projectCodexInstalledPluginEntries(catalog.plugins ?? []);
|
|
13475
|
+
} catch {
|
|
13476
|
+
refuse2();
|
|
13477
|
+
}
|
|
13478
|
+
const candidates = catalog.plugins === undefined ? skills.map((skill) => skill.path).filter((path) => allowed.has(path)) : allowed;
|
|
13479
|
+
for (const document2 of candidates) {
|
|
13480
|
+
if (!document2.startsWith(cache + sep4))
|
|
13383
13481
|
continue;
|
|
13384
|
-
|
|
13482
|
+
const parsed = identity3(document2, cache, read2);
|
|
13483
|
+
if (parsed.name === "skills-cli" || parsed.name.endsWith(":skills-cli"))
|
|
13484
|
+
refuse2();
|
|
13485
|
+
const cacheParts = relative4(cache, parsed.pluginParent).split(sep4);
|
|
13486
|
+
if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier2(part)))
|
|
13385
13487
|
refuse2();
|
|
13386
|
-
|
|
13488
|
+
const [marketplace, pluginName] = cacheParts;
|
|
13489
|
+
if (pluginName !== parsed.namespace)
|
|
13387
13490
|
refuse2();
|
|
13388
|
-
const
|
|
13389
|
-
|
|
13491
|
+
const expectedPluginId = `${pluginName}@${marketplace}`;
|
|
13492
|
+
const listed = skills.filter((skill) => skill.path === document2);
|
|
13493
|
+
if (listed.length > 1)
|
|
13390
13494
|
refuse2();
|
|
13391
|
-
|
|
13495
|
+
let remotePluginId;
|
|
13496
|
+
if (listed.length === 1) {
|
|
13497
|
+
if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
|
|
13498
|
+
refuse2();
|
|
13499
|
+
} else {
|
|
13500
|
+
const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
|
|
13501
|
+
if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
|
|
13502
|
+
refuse2();
|
|
13503
|
+
const plugin = installed[0];
|
|
13504
|
+
if (plugin.localVersion !== null) {
|
|
13505
|
+
if (plugin.localVersion !== basename5(dirname10(dirname10(dirname10(document2)))))
|
|
13506
|
+
continue;
|
|
13507
|
+
} else {
|
|
13508
|
+
if (plugin.sourceType !== "remote" || !plugin.remotePluginId || !remoteInstallationMatches(parsed.pluginParent, plugin.remotePluginId, read2) || !lstatSync12(document2).isFile() || realpathSync5(document2) !== document2)
|
|
13509
|
+
refuse2();
|
|
13510
|
+
remotePluginId = plugin.remotePluginId;
|
|
13511
|
+
}
|
|
13512
|
+
const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
|
|
13513
|
+
if (!exactPathDeny || !isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true }, rules))
|
|
13514
|
+
refuse2();
|
|
13515
|
+
}
|
|
13516
|
+
result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
|
|
13392
13517
|
}
|
|
13393
13518
|
if (!result.length || new Set(result.map((item) => item.name)).size !== result.length)
|
|
13394
13519
|
refuse2();
|
|
@@ -13404,7 +13529,7 @@ function isReviewedCodexPluginSkillDisabled(document2, cache, controls, rules, r
|
|
|
13404
13529
|
return false;
|
|
13405
13530
|
}
|
|
13406
13531
|
const reviewed = controls.find((item) => item.name === parsed.name && item.namespace === parsed.namespace && item.pluginParent === parsed.pluginParent && item.manifestSha256 === parsed.manifestSha256 && item.appSha256 === parsed.appSha256);
|
|
13407
|
-
if (!reviewed)
|
|
13532
|
+
if (!reviewed || reviewed.remotePluginId !== undefined && !remoteInstallationMatches(reviewed.pluginParent, reviewed.remotePluginId, read2))
|
|
13408
13533
|
return false;
|
|
13409
13534
|
return isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: reviewed.pluginId, enabled: true }, rules);
|
|
13410
13535
|
}
|
|
@@ -13423,7 +13548,7 @@ enabled = false
|
|
|
13423
13548
|
return text2;
|
|
13424
13549
|
}
|
|
13425
13550
|
|
|
13426
|
-
// src/lib/codex-settings-witness.ts
|
|
13551
|
+
// src/lib/codex-settings-witness-v1.ts
|
|
13427
13552
|
import { createHash as createHash9 } from "crypto";
|
|
13428
13553
|
var sha = (text2) => createHash9("sha256").update(text2).digest("hex");
|
|
13429
13554
|
var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
@@ -13517,21 +13642,169 @@ function captureCodexSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
|
13517
13642
|
const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
13518
13643
|
return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
|
|
13519
13644
|
}
|
|
13645
|
+
|
|
13646
|
+
// src/lib/codex-settings-witness.ts
|
|
13647
|
+
import { createHash as createHash10 } from "crypto";
|
|
13648
|
+
var sha2 = (text2) => createHash10("sha256").update(text2).digest("hex");
|
|
13649
|
+
var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
13650
|
+
var VERBOSITY2 = new Set(["low", "medium", "high"]);
|
|
13651
|
+
var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
13652
|
+
var CODEX_DISCOVERY_PROJECTION_FIELDS = ["plugins", "marketplaces", "skills"];
|
|
13653
|
+
var INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
|
|
13654
|
+
function need4(value) {
|
|
13655
|
+
if (!value)
|
|
13656
|
+
throw new Error("Invalid Codex settings witness");
|
|
13657
|
+
}
|
|
13658
|
+
function isRecord3(value) {
|
|
13659
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
13660
|
+
}
|
|
13661
|
+
function isDate(value) {
|
|
13662
|
+
return value instanceof Date;
|
|
13663
|
+
}
|
|
13664
|
+
function numericTokens2(text2) {
|
|
13665
|
+
const result = [];
|
|
13666
|
+
for (let at = 0;at < text2.length; ) {
|
|
13667
|
+
const char = text2[at];
|
|
13668
|
+
if (char === "#") {
|
|
13669
|
+
while (at < text2.length && text2[at] !== `
|
|
13670
|
+
`)
|
|
13671
|
+
at++;
|
|
13672
|
+
continue;
|
|
13673
|
+
}
|
|
13674
|
+
if (char === '"' || char === "'") {
|
|
13675
|
+
const quote = char, triple = text2.slice(at, at + 3) === quote.repeat(3);
|
|
13676
|
+
at += triple ? 3 : 1;
|
|
13677
|
+
while (at < text2.length) {
|
|
13678
|
+
if (quote === '"' && text2[at] === "\\") {
|
|
13679
|
+
at += 2;
|
|
13680
|
+
continue;
|
|
13681
|
+
}
|
|
13682
|
+
if (text2.slice(at, at + (triple ? 3 : 1)) === quote.repeat(triple ? 3 : 1)) {
|
|
13683
|
+
at += triple ? 3 : 1;
|
|
13684
|
+
break;
|
|
13685
|
+
}
|
|
13686
|
+
at++;
|
|
13687
|
+
}
|
|
13688
|
+
continue;
|
|
13689
|
+
}
|
|
13690
|
+
if (/[0-9+-]/.test(char)) {
|
|
13691
|
+
const start = at;
|
|
13692
|
+
while (at < text2.length && !/[\s,\]{}#=]/.test(text2[at]))
|
|
13693
|
+
at++;
|
|
13694
|
+
if (at > start) {
|
|
13695
|
+
result.push(text2.slice(start, at));
|
|
13696
|
+
continue;
|
|
13697
|
+
}
|
|
13698
|
+
}
|
|
13699
|
+
at++;
|
|
13700
|
+
}
|
|
13701
|
+
return result.sort();
|
|
13702
|
+
}
|
|
13703
|
+
function canonical4(value, depth = 0, budget = { remaining: 65536 }) {
|
|
13704
|
+
need4(depth <= 32 && --budget.remaining >= 0);
|
|
13705
|
+
if (value === null)
|
|
13706
|
+
return ["null"];
|
|
13707
|
+
if (typeof value === "string")
|
|
13708
|
+
return ["string", value];
|
|
13709
|
+
if (typeof value === "boolean")
|
|
13710
|
+
return ["boolean", value];
|
|
13711
|
+
if (typeof value === "number") {
|
|
13712
|
+
need4(Number.isFinite(value));
|
|
13713
|
+
return ["number", String(value)];
|
|
13714
|
+
}
|
|
13715
|
+
if (isDate(value)) {
|
|
13716
|
+
need4(Number.isFinite(value.getTime()));
|
|
13717
|
+
return ["date", value.toISOString()];
|
|
13718
|
+
}
|
|
13719
|
+
if (Array.isArray(value))
|
|
13720
|
+
return ["array", value.map((child) => canonical4(child, depth + 1, budget))];
|
|
13721
|
+
need4(isRecord3(value));
|
|
13722
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget)])];
|
|
13723
|
+
}
|
|
13724
|
+
function normalize3(config) {
|
|
13725
|
+
if (isRecord3(config.hooks)) {
|
|
13726
|
+
delete config.hooks.state;
|
|
13727
|
+
if (!Object.keys(config.hooks).length)
|
|
13728
|
+
delete config.hooks;
|
|
13729
|
+
}
|
|
13730
|
+
if (isRecord3(config.skills) && Array.isArray(config.skills.config)) {
|
|
13731
|
+
const skills = config.skills;
|
|
13732
|
+
const retained = skills.config.filter((entry) => !disablesOneSkill(entry));
|
|
13733
|
+
if (retained.length !== skills.config.length) {
|
|
13734
|
+
if (retained.length)
|
|
13735
|
+
skills.config = retained;
|
|
13736
|
+
else
|
|
13737
|
+
delete skills.config;
|
|
13738
|
+
}
|
|
13739
|
+
if (!Object.keys(skills).length)
|
|
13740
|
+
delete config.skills;
|
|
13741
|
+
}
|
|
13742
|
+
inference(config);
|
|
13743
|
+
if (isRecord3(config.profiles)) {
|
|
13744
|
+
for (const profile of Object.values(config.profiles))
|
|
13745
|
+
if (isRecord3(profile))
|
|
13746
|
+
inference(profile);
|
|
13747
|
+
}
|
|
13748
|
+
}
|
|
13749
|
+
function disablesOneSkill(entry) {
|
|
13750
|
+
if (!isRecord3(entry) || entry.enabled !== false)
|
|
13751
|
+
return false;
|
|
13752
|
+
const keys = Object.keys(entry);
|
|
13753
|
+
if (!keys.every((key) => key === "path" || key === "name" || key === "enabled"))
|
|
13754
|
+
return false;
|
|
13755
|
+
const selector = ["path", "name"].filter((key) => Object.hasOwn(entry, key));
|
|
13756
|
+
return selector.length === 1 && typeof entry[selector[0]] === "string" && entry[selector[0]].length > 0;
|
|
13757
|
+
}
|
|
13758
|
+
function inference(target) {
|
|
13759
|
+
for (const key of INFERENCE)
|
|
13760
|
+
if (Object.hasOwn(target, key)) {
|
|
13761
|
+
const value = target[key];
|
|
13762
|
+
if (key === "model")
|
|
13763
|
+
need4(typeof value === "string" && MODEL.test(value));
|
|
13764
|
+
else
|
|
13765
|
+
need4(typeof value === "string" && (key === "model_reasoning_effort" ? EFFORT2.has(value) : VERBOSITY2.has(value)));
|
|
13766
|
+
delete target[key];
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13769
|
+
function settingsDigest2(text2) {
|
|
13770
|
+
let config;
|
|
13771
|
+
try {
|
|
13772
|
+
config = Bun.TOML.parse(text2);
|
|
13773
|
+
} catch {
|
|
13774
|
+
throw new Error("Invalid Codex settings witness");
|
|
13775
|
+
}
|
|
13776
|
+
need4(isRecord3(config));
|
|
13777
|
+
normalize3(config);
|
|
13778
|
+
return sha2(`hasna.skills.codex-settings.v2\x00${JSON.stringify([canonical4(config), numericTokens2(text2)])}`);
|
|
13779
|
+
}
|
|
13780
|
+
function hashCodexSettingsReplacementV2(text2, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
13781
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
|
|
13782
|
+
budget.remaining -= Buffer.byteLength(text2);
|
|
13783
|
+
return settingsDigest2(text2);
|
|
13784
|
+
}
|
|
13785
|
+
function captureCodexSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
13786
|
+
const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
13787
|
+
return { path, hashMode: "codex-settings-v2", sha256: settingsDigest2(text2) };
|
|
13788
|
+
}
|
|
13789
|
+
function readCodexSettingsPreimage(path, budget = { remaining: 2 * 1024 * 1024 }) {
|
|
13790
|
+
return readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
13791
|
+
}
|
|
13520
13792
|
function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
|
|
13521
|
-
|
|
13793
|
+
need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "codex-settings-v1") && /^[a-f0-9]{64}$/.test(previous.sha256));
|
|
13522
13794
|
const budget = { remaining: 2 * 1024 * 1024 };
|
|
13523
13795
|
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
|
|
13524
|
-
|
|
13525
|
-
const sha2562 =
|
|
13526
|
-
if (
|
|
13796
|
+
need4((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
|
|
13797
|
+
const sha2562 = settingsDigest2(current);
|
|
13798
|
+
if (settingsDigest2(before) !== sha2562)
|
|
13527
13799
|
throw new Error("Codex non-preference settings changed; explicit discovery review required");
|
|
13528
|
-
return { path: previous.path, hashMode: "codex-settings-
|
|
13800
|
+
return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
|
|
13529
13801
|
}
|
|
13530
13802
|
|
|
13531
13803
|
// src/lib/agent-integration.ts
|
|
13532
|
-
import { createHash as
|
|
13804
|
+
import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
|
|
13805
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
13533
13806
|
import { existsSync as existsSync8, lstatSync as lstatSync15, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync12, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync6, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync8, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, readSync as readSync6, constants as constants7, linkSync } from "fs";
|
|
13534
|
-
import { dirname as dirname11, join as join17, resolve as resolve10, relative as
|
|
13807
|
+
import { dirname as dirname11, join as join17, resolve as resolve10, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
|
|
13535
13808
|
import { homedir as homedir2 } from "os";
|
|
13536
13809
|
|
|
13537
13810
|
// src/lib/managed-policy.ts
|
|
@@ -13615,7 +13888,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
|
|
|
13615
13888
|
}
|
|
13616
13889
|
|
|
13617
13890
|
// src/lib/agent-bridge.ts
|
|
13618
|
-
import { createHash as
|
|
13891
|
+
import { createHash as createHash11 } from "crypto";
|
|
13619
13892
|
import { lstatSync as lstatSync14, readFileSync as readFileSync11, readdirSync as readdirSync7 } from "fs";
|
|
13620
13893
|
import { join as join16 } from "path";
|
|
13621
13894
|
var CLI_BRIDGE_NAME = "skills-cli";
|
|
@@ -13634,7 +13907,7 @@ Use the Skills CLI as the skill authority for this station.
|
|
|
13634
13907
|
|
|
13635
13908
|
This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
|
|
13636
13909
|
`;
|
|
13637
|
-
var CLI_BRIDGE_DIGEST =
|
|
13910
|
+
var CLI_BRIDGE_DIGEST = createHash11("sha256").update(markdown).digest("hex");
|
|
13638
13911
|
var CLI_BRIDGE_FILES = Object.freeze({
|
|
13639
13912
|
"SKILL.md": markdown,
|
|
13640
13913
|
".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
|
|
@@ -13926,7 +14199,7 @@ function disableCodexBundledSkills(text2) {
|
|
|
13926
14199
|
}
|
|
13927
14200
|
|
|
13928
14201
|
// src/lib/agent-integration.ts
|
|
13929
|
-
var
|
|
14202
|
+
var sha3 = (data) => createHash12("sha256").update(data).digest("hex");
|
|
13930
14203
|
var ROOTS = NATIVE_SKILL_ROOTS;
|
|
13931
14204
|
function assertSafePath(path) {
|
|
13932
14205
|
let cursor = resolve10(path);
|
|
@@ -13950,7 +14223,7 @@ function readRootAlias(home, agent) {
|
|
|
13950
14223
|
const targetStat = lstatSync15(targetPath, { throwIfNoEntry: false });
|
|
13951
14224
|
if (!targetStat?.isDirectory())
|
|
13952
14225
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
13953
|
-
const target = realpathSync6(targetPath), inside =
|
|
14226
|
+
const target = realpathSync6(targetPath), inside = relative5(realHome, target);
|
|
13954
14227
|
if (!inside || inside === ".." || inside.startsWith(`..${sep5}`) || isAbsolute11(inside))
|
|
13955
14228
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
13956
14229
|
return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
@@ -13968,7 +14241,7 @@ function rootAliases(home, allowed = false) {
|
|
|
13968
14241
|
}
|
|
13969
14242
|
function canonicalAgentPath(path, aliases) {
|
|
13970
14243
|
const absolute = resolve10(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
|
|
13971
|
-
return binding ? join17(binding.target,
|
|
14244
|
+
return binding ? join17(binding.target, relative5(binding.alias, absolute)) : absolute;
|
|
13972
14245
|
}
|
|
13973
14246
|
function disabledCodexSkillPaths(config, aliases) {
|
|
13974
14247
|
const skills = config.skills;
|
|
@@ -14033,13 +14306,13 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
14033
14306
|
}
|
|
14034
14307
|
}
|
|
14035
14308
|
function treeHash(root) {
|
|
14036
|
-
const hash2 =
|
|
14309
|
+
const hash2 = createHash12("sha256");
|
|
14037
14310
|
let bytes = 0, files = 0;
|
|
14038
14311
|
function visit2(path) {
|
|
14039
14312
|
const stat = lstatSync15(path);
|
|
14040
14313
|
if (stat.isSymbolicLink())
|
|
14041
14314
|
throw new Error(`Refusing symlink in skill: ${path}`);
|
|
14042
|
-
const rel =
|
|
14315
|
+
const rel = relative5(root, path);
|
|
14043
14316
|
if (stat.isDirectory()) {
|
|
14044
14317
|
hash2.update(`d\x00${rel}\x00`);
|
|
14045
14318
|
for (const name of readdirSync8(path).sort())
|
|
@@ -14065,7 +14338,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
14065
14338
|
const identities = new Set;
|
|
14066
14339
|
const selected = [];
|
|
14067
14340
|
for (const target of manifest.targets) {
|
|
14068
|
-
const projectRoot = resolve10(target.projectRoot), normalized =
|
|
14341
|
+
const projectRoot = resolve10(target.projectRoot), normalized = relative5(projectRoot, resolve10(projectRoot, target.path));
|
|
14069
14342
|
if (!isAbsolute11(target.projectRoot) || projectRoot !== target.projectRoot || !existsSync8(projectRoot) || !lstatSync15(projectRoot).isDirectory())
|
|
14070
14343
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
14071
14344
|
assertSafePath(projectRoot);
|
|
@@ -14418,9 +14691,9 @@ function planAgentIntegration(options) {
|
|
|
14418
14691
|
const observedNativeSources = [];
|
|
14419
14692
|
const codexPluginSkills = options.codexNativeCatalog ? reviewCodexPluginSkillControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join17(entry.path, "SKILL.md")), canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), resolve10(options.projectDir ?? home), (path) => {
|
|
14420
14693
|
const bytes = readNativeBytes(path, 1024 * 1024);
|
|
14421
|
-
observedNativeSources.push({ path, sha256:
|
|
14694
|
+
observedNativeSources.push({ path, sha256: sha3(bytes) });
|
|
14422
14695
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
14423
|
-
}) : policy.bridge?.codexPluginSkills ?? [];
|
|
14696
|
+
}, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
|
|
14424
14697
|
const changes = [];
|
|
14425
14698
|
for (const agent of [...new Set(options.agents)]) {
|
|
14426
14699
|
if (!INTEGRATION_AGENTS.includes(agent))
|
|
@@ -14446,7 +14719,7 @@ function planAgentIntegration(options) {
|
|
|
14446
14719
|
throw new Error("Refusing to overwrite an unrecognized Hermes supervisor");
|
|
14447
14720
|
if (supervisorBefore !== supervisorAfter)
|
|
14448
14721
|
changes.push({ path: supervisorPath, before: supervisorBefore, after: supervisorAfter });
|
|
14449
|
-
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256:
|
|
14722
|
+
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256: sha3(supervisorAfter) };
|
|
14450
14723
|
const after2 = configureHermesHooks(before, supervisor, previous);
|
|
14451
14724
|
if (before !== after2)
|
|
14452
14725
|
changes.push({ path, before, after: after2 });
|
|
@@ -14493,12 +14766,12 @@ function planAgentIntegration(options) {
|
|
|
14493
14766
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
14494
14767
|
home: resolve10(home),
|
|
14495
14768
|
includeVendor: true,
|
|
14496
|
-
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join17(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256:
|
|
14769
|
+
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join17(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
|
|
14497
14770
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
14498
14771
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
14499
14772
|
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join17(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
14500
14773
|
rootAliases: aliases,
|
|
14501
|
-
...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256:
|
|
14774
|
+
...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join17(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join17(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview } : {},
|
|
14502
14775
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
14503
14776
|
} };
|
|
14504
14777
|
const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
|
|
@@ -14514,12 +14787,12 @@ function planClaudePreToolUseHookUpdate(options) {
|
|
|
14514
14787
|
return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
|
|
14515
14788
|
}
|
|
14516
14789
|
function claudeSettingsConsumers(home, path, discoveries, aliases) {
|
|
14517
|
-
const
|
|
14790
|
+
const canonical5 = (value) => canonicalAgentPath(value, aliases);
|
|
14518
14791
|
const containsSettings = (root) => {
|
|
14519
|
-
const distance =
|
|
14792
|
+
const distance = relative5(canonical5(root), path);
|
|
14520
14793
|
return distance === "" || distance !== ".." && !distance.startsWith(`..${sep5}`) && !isAbsolute11(distance);
|
|
14521
14794
|
};
|
|
14522
|
-
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" ||
|
|
14795
|
+
return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical5(join17(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical5(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
|
|
14523
14796
|
}
|
|
14524
14797
|
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
14525
14798
|
try {
|
|
@@ -14551,7 +14824,7 @@ function planClaudeHookEventsUpdate(options) {
|
|
|
14551
14824
|
const path = canonicalAgentPath(join17(home, ".claude", "settings.json"), aliases);
|
|
14552
14825
|
assertSafePath(path);
|
|
14553
14826
|
const before = lstatSync15(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
|
|
14554
|
-
if ((before === null ? "absent" :
|
|
14827
|
+
if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
|
|
14555
14828
|
throw new Error("Claude settings preimage changed");
|
|
14556
14829
|
assertClaudeHookEventsReplacement(before, options.replacement, options.events);
|
|
14557
14830
|
const agents = bridge.agents;
|
|
@@ -14617,7 +14890,7 @@ function atomicWrite2(path, content) {
|
|
|
14617
14890
|
function planAgentSettingsWitnessUpgrade(options) {
|
|
14618
14891
|
const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
|
|
14619
14892
|
const snapshot = readManagedSkillPolicySnapshot(dataDir2);
|
|
14620
|
-
if (!snapshot ||
|
|
14893
|
+
if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
|
|
14621
14894
|
throw new Error("Managed policy preimage changed");
|
|
14622
14895
|
const policy = snapshot.value, aliases = policy.bridge?.rootAliases ?? [];
|
|
14623
14896
|
recheckRootAliases(aliases);
|
|
@@ -14626,7 +14899,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
14626
14899
|
throw new Error("Settings witness upgrade requires an existing reviewed native binding");
|
|
14627
14900
|
const configPath = canonicalAgentPath(join17(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
|
|
14628
14901
|
const settings = readOptional(configPath);
|
|
14629
|
-
if (settings === null ||
|
|
14902
|
+
if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
|
|
14630
14903
|
throw new Error("Native settings preimage changed");
|
|
14631
14904
|
const sources = binding.sources.filter((source) => source.path === configPath && source.format === undefined && source.fields === undefined);
|
|
14632
14905
|
if (sources.length !== 1)
|
|
@@ -14634,15 +14907,40 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
14634
14907
|
const previous = sources[0];
|
|
14635
14908
|
if (typeof previous.sha256 !== "string")
|
|
14636
14909
|
throw new Error("Settings witness upgrade requires a preserved settings preimage");
|
|
14910
|
+
const replaced = [previous];
|
|
14911
|
+
if (options.agent === "codex") {
|
|
14912
|
+
if (previous.hashMode !== undefined && previous.hashMode !== "bytes" && previous.hashMode !== "codex-settings-v1")
|
|
14913
|
+
throw new Error("Codex settings witness upgrade requires a preserved raw or codex-settings-v1 configuration witness");
|
|
14914
|
+
const preimage = readCodexSettingsPreimage(options.reviewedPreimage);
|
|
14915
|
+
for (const source of binding.sources) {
|
|
14916
|
+
if (source === previous || source.path !== configPath)
|
|
14917
|
+
continue;
|
|
14918
|
+
const fields = source.fields;
|
|
14919
|
+
if (source.hashMode !== undefined || source.format !== "toml" || !Array.isArray(fields) || !isDeepStrictEqual3(fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]) || typeof source.sha256 !== "string")
|
|
14920
|
+
throw new Error("Codex settings witness upgrade found an unrecognized configuration witness");
|
|
14921
|
+
if (projectNativeDiscoveryFields(preimage, "toml", fields, configPath) !== source.sha256)
|
|
14922
|
+
throw new Error("Reviewed preimage does not explain the Codex discovery projection witness");
|
|
14923
|
+
replaced.push(source);
|
|
14924
|
+
}
|
|
14925
|
+
}
|
|
14637
14926
|
const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
14638
|
-
const
|
|
14927
|
+
const rebound = [];
|
|
14928
|
+
for (const source of binding.sources) {
|
|
14929
|
+
if (!replaced.includes(source)) {
|
|
14930
|
+
rebound.push(source);
|
|
14931
|
+
continue;
|
|
14932
|
+
}
|
|
14933
|
+
if (source === replaced[0])
|
|
14934
|
+
rebound.push(next);
|
|
14935
|
+
}
|
|
14936
|
+
const replacement = { ...binding, sources: rebound };
|
|
14639
14937
|
verifyAgentDiscovery(replacement);
|
|
14640
14938
|
const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
|
|
14641
|
-
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join17(dataDir2, "agent-policy.json"), before: snapshot.text, after }], nativeSkills: [], observedPolicy: { path: join17(dataDir2, "agent-policy.json"), before: snapshot.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256 } };
|
|
14939
|
+
return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join17(dataDir2, "agent-policy.json"), before: snapshot.text, after }], nativeSkills: [], observedPolicy: { path: join17(dataDir2, "agent-policy.json"), before: snapshot.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
|
|
14642
14940
|
}
|
|
14643
14941
|
function applyAgentIntegration(plan) {
|
|
14644
14942
|
for (const source of plan.observedNativeSources ?? [])
|
|
14645
|
-
if (
|
|
14943
|
+
if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
|
|
14646
14944
|
throw new Error("Native identity source changed after planning");
|
|
14647
14945
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
14648
14946
|
throw new Error("Native settings changed after witness planning");
|
|
@@ -14662,7 +14960,7 @@ function applyAgentIntegration(plan) {
|
|
|
14662
14960
|
const upgrade = plan.settingsWitnessUpgrade;
|
|
14663
14961
|
if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
|
|
14664
14962
|
throw new Error("Invalid settings witness upgrade plan");
|
|
14665
|
-
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256:
|
|
14963
|
+
const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
|
|
14666
14964
|
if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
|
|
14667
14965
|
throw new Error("Settings witness upgrade plan changed");
|
|
14668
14966
|
provenDiscovery = verified.discoveryBefore[0];
|
|
@@ -14690,7 +14988,7 @@ function applyAgentIntegration(plan) {
|
|
|
14690
14988
|
chmodSync(backupRoot, 448);
|
|
14691
14989
|
for (const [index, change] of plan.changes.entries())
|
|
14692
14990
|
if (change.before !== null) {
|
|
14693
|
-
const backup = join17(backupRoot, `${index}-${
|
|
14991
|
+
const backup = join17(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
|
|
14694
14992
|
writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
|
|
14695
14993
|
if (readFileSync12(backup, "utf8") !== change.before)
|
|
14696
14994
|
throw new Error("Native configuration preservation readback failed");
|
|
@@ -14709,7 +15007,7 @@ function applyAgentIntegration(plan) {
|
|
|
14709
15007
|
}
|
|
14710
15008
|
recheckRootAliases(aliases);
|
|
14711
15009
|
for (const source of plan.observedNativeSources ?? [])
|
|
14712
|
-
if (
|
|
15010
|
+
if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
|
|
14713
15011
|
throw new Error("Native identity source changed during application");
|
|
14714
15012
|
if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
|
|
14715
15013
|
throw new Error("Native settings changed during witness application");
|
|
@@ -14721,7 +15019,7 @@ function applyAgentIntegration(plan) {
|
|
|
14721
15019
|
}
|
|
14722
15020
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
14723
15021
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
14724
|
-
atomicWrite2(join17(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null :
|
|
15022
|
+
atomicWrite2(join17(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha3(change.before), afterHash: sha3(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
|
14725
15023
|
`);
|
|
14726
15024
|
} catch (error) {
|
|
14727
15025
|
for (const change of written.reverse()) {
|
|
@@ -14800,8 +15098,8 @@ function archiveNativeSkills(inventory, options) {
|
|
|
14800
15098
|
const operationId = randomUUID2(), archiveRoot = join17(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join17(archiveRoot, "receipt.json");
|
|
14801
15099
|
const entries = selected.map((entry, index) => ({
|
|
14802
15100
|
source: entry.vendor ? join17(entry.path, "SKILL.md") : entry.path,
|
|
14803
|
-
archive: join17(archiveRoot, `${index}-${
|
|
14804
|
-
hash: entry.vendor ?
|
|
15101
|
+
archive: join17(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
|
|
15102
|
+
hash: entry.vendor ? sha3(readNativeBytes(join17(entry.path, "SKILL.md"))) : entry.hash,
|
|
14805
15103
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
14806
15104
|
status: "planned"
|
|
14807
15105
|
}));
|
|
@@ -14812,7 +15110,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
14812
15110
|
const stat = lstatSync15(entry.archive);
|
|
14813
15111
|
if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
|
|
14814
15112
|
throw new Error("Archive type changed");
|
|
14815
|
-
if ((entry.discoveryOnly ?
|
|
15113
|
+
if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
14816
15114
|
throw new Error("Archive bytes changed");
|
|
14817
15115
|
};
|
|
14818
15116
|
assertSafePath(archiveRoot);
|
|
@@ -14932,7 +15230,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
14932
15230
|
throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
|
|
14933
15231
|
if (agent === "hermes") {
|
|
14934
15232
|
const supervisor = binding.supervisors?.hermes;
|
|
14935
|
-
if (supervisor?.path !== join17(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !==
|
|
15233
|
+
if (supervisor?.path !== join17(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
|
|
14936
15234
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
14937
15235
|
assertHermesProtection(home, config, command, profile, supervisor);
|
|
14938
15236
|
} else if (agent === "opencode") {
|
|
@@ -15261,7 +15559,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
15261
15559
|
return { action, path: skillMdPath };
|
|
15262
15560
|
const parentDir = dirname12(dir);
|
|
15263
15561
|
mkdirSync6(parentDir, { recursive: true });
|
|
15264
|
-
const transactionDir = mkdtempSync(join18(parentDir, `.hasna-skills-write-${
|
|
15562
|
+
const transactionDir = mkdtempSync(join18(parentDir, `.hasna-skills-write-${basename6(dir)}-`));
|
|
15265
15563
|
const candidateDir = join18(transactionDir, "candidate");
|
|
15266
15564
|
const backupDir = join18(transactionDir, "backup");
|
|
15267
15565
|
const candidateSkillMdPath = join18(candidateDir, "SKILL.md");
|
|
@@ -16230,7 +16528,7 @@ function exactProfileSelection(spec, profile) {
|
|
|
16230
16528
|
}
|
|
16231
16529
|
|
|
16232
16530
|
// src/lib/plugin-projection.ts
|
|
16233
|
-
import { createHash as
|
|
16531
|
+
import { createHash as createHash13 } from "crypto";
|
|
16234
16532
|
import { posix } from "path";
|
|
16235
16533
|
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
|
|
16236
16534
|
var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -16241,7 +16539,7 @@ function pluginNeed(value, message) {
|
|
|
16241
16539
|
if (!value)
|
|
16242
16540
|
pluginRefusal(message);
|
|
16243
16541
|
}
|
|
16244
|
-
var pluginHash = (bytes) =>
|
|
16542
|
+
var pluginHash = (bytes) => createHash13("sha256").update(bytes).digest("hex");
|
|
16245
16543
|
function pluginObject(value) {
|
|
16246
16544
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
16247
16545
|
}
|
|
@@ -16429,7 +16727,7 @@ function buildPluginProjection(entries) {
|
|
|
16429
16727
|
}
|
|
16430
16728
|
|
|
16431
16729
|
// src/lib/plugin-projection-store.ts
|
|
16432
|
-
import { createHash as
|
|
16730
|
+
import { createHash as createHash14, randomUUID as randomUUID3 } from "crypto";
|
|
16433
16731
|
import { constants as constants8, closeSync as closeSync9, fchmodSync, fstatSync as fstatSync9, fsyncSync as fsyncSync3, linkSync as linkSync2, lstatSync as lstatSync17, mkdirSync as mkdirSync8, openSync as openSync9, opendirSync as opendirSync4, readSync as readSync7, renameSync as renameSync4, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync4, writeSync as writeSync2 } from "fs";
|
|
16434
16732
|
import { dirname as dirname13, isAbsolute as isAbsolute12, join as join23, parse as parse5, resolve as resolve11 } from "path";
|
|
16435
16733
|
var READ_FLAGS = constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
|
|
@@ -16445,7 +16743,7 @@ function stat(path) {
|
|
|
16445
16743
|
return pluginRefusal("Cannot inspect plugin filesystem entry");
|
|
16446
16744
|
}
|
|
16447
16745
|
}
|
|
16448
|
-
function
|
|
16746
|
+
function canonical5(path) {
|
|
16449
16747
|
pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute12(path) && resolve11(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
|
|
16450
16748
|
}
|
|
16451
16749
|
function owner(stats, system = false) {
|
|
@@ -16456,7 +16754,7 @@ function same3(left, right, identityOnly = false) {
|
|
|
16456
16754
|
pluginNeed(right && left.dev === right.dev && left.ino === right.ino && left.mode === right.mode && left.uid === right.uid && left.gid === right.gid && (identityOnly || left.size === right.size && left.nlink === right.nlink && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs), "Plugin filesystem entry changed during access");
|
|
16457
16755
|
}
|
|
16458
16756
|
function ancestors(path, create = false) {
|
|
16459
|
-
|
|
16757
|
+
canonical5(path);
|
|
16460
16758
|
const result = [], parent = dirname13(path), filesystemRoot = parse5(parent).root;
|
|
16461
16759
|
let current = filesystemRoot;
|
|
16462
16760
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
@@ -16566,7 +16864,7 @@ function directoryNames(path) {
|
|
|
16566
16864
|
function snapshotPluginTree(root, options = {}) {
|
|
16567
16865
|
const chain = ancestors(root), entries = [], files = [], directories = [];
|
|
16568
16866
|
let bytes = 0, visited = 0;
|
|
16569
|
-
const visit2 = (directory,
|
|
16867
|
+
const visit2 = (directory, relative6) => {
|
|
16570
16868
|
const before = stat(directory);
|
|
16571
16869
|
pluginNeed(before && before.isDirectory() && !before.isSymbolicLink(), "Plugin tree must contain real directories");
|
|
16572
16870
|
const names = directoryNames(directory);
|
|
@@ -16574,18 +16872,18 @@ function snapshotPluginTree(root, options = {}) {
|
|
|
16574
16872
|
directories.push({ path: directory, stat: before, names });
|
|
16575
16873
|
for (const name of names) {
|
|
16576
16874
|
pluginNeed(++visited <= PLUGIN_PROJECTION_LIMITS.files * 50, "Plugin tree membership exceeds its limit");
|
|
16577
|
-
const path =
|
|
16875
|
+
const path = relative6 ? `${relative6}/${name}` : name;
|
|
16578
16876
|
pluginNeed(new TextEncoder().encode(path).byteLength <= 100 && !/[\\:\x00-\x1f\x7f]/u.test(path), "Plugin entry path exceeds its limit or is unsafe");
|
|
16579
16877
|
const absolute = join23(directory, name), entryStat = stat(absolute);
|
|
16580
16878
|
pluginNeed(entryStat, "Plugin tree member disappeared");
|
|
16581
|
-
if (
|
|
16879
|
+
if (relative6 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
|
|
16582
16880
|
const bytes2 = readBytes(absolute, 32, "native-pin");
|
|
16583
16881
|
pluginNeed(/^[1-9][0-9]{12}$/.test(new TextDecoder().decode(bytes2)), "Malformed native orphan timestamp");
|
|
16584
16882
|
same3(entryStat, stat(absolute));
|
|
16585
16883
|
files.push({ path: absolute, stat: entryStat });
|
|
16586
16884
|
continue;
|
|
16587
16885
|
}
|
|
16588
|
-
if (
|
|
16886
|
+
if (relative6 === "" && name === ".in_use" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
|
|
16589
16887
|
pluginNeed(entryStat.isDirectory() && !entryStat.isSymbolicLink(), "Native plugin pins require a real directory");
|
|
16590
16888
|
owner(entryStat);
|
|
16591
16889
|
const pins = directoryNames(absolute);
|
|
@@ -16771,7 +17069,7 @@ function pluginExecutableDigest(path) {
|
|
|
16771
17069
|
const opened = fstatSync9(fd, { bigint: true });
|
|
16772
17070
|
same3(before, opened);
|
|
16773
17071
|
fileShape(opened, maximum, "executable");
|
|
16774
|
-
const hash2 =
|
|
17072
|
+
const hash2 = createHash14("sha256"), buffer = new Uint8Array(64 * 1024);
|
|
16775
17073
|
let remaining = Number(opened.size);
|
|
16776
17074
|
while (remaining > 0) {
|
|
16777
17075
|
const count = readSync7(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
|
|
@@ -17268,7 +17566,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
17268
17566
|
pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join26(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
|
|
17269
17567
|
const approval = (producer) => {
|
|
17270
17568
|
pluginText(producer);
|
|
17271
|
-
const digest = `sha256:${
|
|
17569
|
+
const digest = `sha256:${basename7(producer)}`;
|
|
17272
17570
|
const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest);
|
|
17273
17571
|
pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
|
|
17274
17572
|
if (!verified.has(producer)) {
|
|
@@ -17330,23 +17628,23 @@ function captureManagedPluginRegistry(path, managedPlugins) {
|
|
|
17330
17628
|
}
|
|
17331
17629
|
|
|
17332
17630
|
// src/lib/claude-marketplace-registry.ts
|
|
17333
|
-
import { createHash as
|
|
17631
|
+
import { createHash as createHash15 } from "crypto";
|
|
17334
17632
|
import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, lstatSync as lstatSync19, openSync as openSync11, readSync as readSync9 } from "fs";
|
|
17335
|
-
import { basename as
|
|
17633
|
+
import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute15, resolve as resolve14 } from "path";
|
|
17336
17634
|
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
17337
|
-
function
|
|
17635
|
+
function need5(value, reason) {
|
|
17338
17636
|
if (!value)
|
|
17339
17637
|
throw new Error(`Claude marketplace registry ${reason}`);
|
|
17340
17638
|
}
|
|
17341
17639
|
function absolutePath3(value) {
|
|
17342
|
-
|
|
17640
|
+
need5(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve14(value) === value, "requires a normalized absolute path");
|
|
17343
17641
|
}
|
|
17344
17642
|
var sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
|
|
17345
17643
|
function parents2(path) {
|
|
17346
17644
|
const result = [];
|
|
17347
17645
|
for (let at = dirname15(path);; at = dirname15(at)) {
|
|
17348
17646
|
const stat2 = lstatSync19(at, { bigint: true });
|
|
17349
|
-
|
|
17647
|
+
need5(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
17350
17648
|
result.push([at, stat2]);
|
|
17351
17649
|
if (at === dirname15(at))
|
|
17352
17650
|
return result;
|
|
@@ -17354,15 +17652,15 @@ function parents2(path) {
|
|
|
17354
17652
|
}
|
|
17355
17653
|
function readRegistry2(path, budget) {
|
|
17356
17654
|
absolutePath3(path);
|
|
17357
|
-
|
|
17358
|
-
|
|
17655
|
+
need5(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
|
|
17656
|
+
need5(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
|
|
17359
17657
|
const ancestors2 = parents2(path), initial = lstatSync19(path, { bigint: true });
|
|
17360
|
-
|
|
17361
|
-
|
|
17658
|
+
need5(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
|
|
17659
|
+
need5(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
|
|
17362
17660
|
const fd = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
|
|
17363
17661
|
try {
|
|
17364
17662
|
const opened = fstatSync11(fd, { bigint: true });
|
|
17365
|
-
|
|
17663
|
+
need5(opened.isFile() && sameFile2(initial, opened), "changed during open");
|
|
17366
17664
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
17367
17665
|
let length = 0;
|
|
17368
17666
|
while (length < bytes.length) {
|
|
@@ -17372,10 +17670,10 @@ function readRegistry2(path, budget) {
|
|
|
17372
17670
|
length += count;
|
|
17373
17671
|
}
|
|
17374
17672
|
const after = fstatSync11(fd, { bigint: true }), current = lstatSync19(path, { bigint: true });
|
|
17375
|
-
|
|
17673
|
+
need5(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
17376
17674
|
for (const [ancestor, before] of ancestors2) {
|
|
17377
17675
|
const now = lstatSync19(ancestor, { bigint: true });
|
|
17378
|
-
|
|
17676
|
+
need5(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
17379
17677
|
}
|
|
17380
17678
|
budget.remaining -= length;
|
|
17381
17679
|
try {
|
|
@@ -17395,7 +17693,7 @@ function parse6(text2) {
|
|
|
17395
17693
|
at++;
|
|
17396
17694
|
};
|
|
17397
17695
|
function string() {
|
|
17398
|
-
|
|
17696
|
+
need5(text2[at] === '"', "contains invalid JSON");
|
|
17399
17697
|
const start = at++;
|
|
17400
17698
|
while (at < text2.length) {
|
|
17401
17699
|
const character = text2[at++];
|
|
@@ -17406,7 +17704,7 @@ function parse6(text2) {
|
|
|
17406
17704
|
} catch {
|
|
17407
17705
|
throw new Error("Claude marketplace registry contains invalid JSON string");
|
|
17408
17706
|
}
|
|
17409
|
-
|
|
17707
|
+
need5(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
|
|
17410
17708
|
return value2;
|
|
17411
17709
|
}
|
|
17412
17710
|
if (character === "\\")
|
|
@@ -17415,7 +17713,7 @@ function parse6(text2) {
|
|
|
17415
17713
|
throw new Error("Claude marketplace registry contains an unterminated JSON string");
|
|
17416
17714
|
}
|
|
17417
17715
|
function value(depth) {
|
|
17418
|
-
|
|
17716
|
+
need5(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
17419
17717
|
space();
|
|
17420
17718
|
const character = text2[at];
|
|
17421
17719
|
if (character === '"')
|
|
@@ -17429,17 +17727,17 @@ function parse6(text2) {
|
|
|
17429
17727
|
if (object5) {
|
|
17430
17728
|
space();
|
|
17431
17729
|
const key = string();
|
|
17432
|
-
|
|
17730
|
+
need5(!keys.has(key), "contains a duplicate JSON key");
|
|
17433
17731
|
keys.add(key);
|
|
17434
17732
|
space();
|
|
17435
|
-
|
|
17733
|
+
need5(text2[at++] === ":", "contains invalid JSON");
|
|
17436
17734
|
entries.push([key, value(depth + 1)]);
|
|
17437
17735
|
} else
|
|
17438
17736
|
items.push(value(depth + 1));
|
|
17439
17737
|
space();
|
|
17440
17738
|
if (text2[at] === end)
|
|
17441
17739
|
break;
|
|
17442
|
-
|
|
17740
|
+
need5(text2[at++] === ",", "contains invalid JSON");
|
|
17443
17741
|
}
|
|
17444
17742
|
at++;
|
|
17445
17743
|
return object5 ? { kind: "object", entries } : { kind: "array", items };
|
|
@@ -17451,14 +17749,14 @@ function parse6(text2) {
|
|
|
17451
17749
|
}
|
|
17452
17750
|
numberPattern.lastIndex = at;
|
|
17453
17751
|
const number = numberPattern.exec(text2);
|
|
17454
|
-
|
|
17752
|
+
need5(number, "contains invalid JSON");
|
|
17455
17753
|
at += number[0].length;
|
|
17456
17754
|
return { kind: "number", value: number[0] };
|
|
17457
17755
|
}
|
|
17458
17756
|
const root2 = value(0);
|
|
17459
17757
|
space();
|
|
17460
|
-
|
|
17461
|
-
|
|
17758
|
+
need5(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
|
|
17759
|
+
need5(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
|
|
17462
17760
|
return root2;
|
|
17463
17761
|
}
|
|
17464
17762
|
function exactKeys(value, keys) {
|
|
@@ -17466,7 +17764,7 @@ function exactKeys(value, keys) {
|
|
|
17466
17764
|
}
|
|
17467
17765
|
var field = (object5, key) => object5.entries.find(([name]) => name === key)?.[1];
|
|
17468
17766
|
function textField(value) {
|
|
17469
|
-
|
|
17767
|
+
need5(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
|
|
17470
17768
|
return value.value;
|
|
17471
17769
|
}
|
|
17472
17770
|
function normalizeRow(row, version = 1) {
|
|
@@ -17474,7 +17772,7 @@ function normalizeRow(row, version = 1) {
|
|
|
17474
17772
|
if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
17475
17773
|
return;
|
|
17476
17774
|
const autoUpdate = field(row, "autoUpdate");
|
|
17477
|
-
|
|
17775
|
+
need5(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
17478
17776
|
}
|
|
17479
17777
|
const source = field(row, "source");
|
|
17480
17778
|
if (source?.kind !== "object")
|
|
@@ -17490,34 +17788,34 @@ function normalizeRow(row, version = 1) {
|
|
|
17490
17788
|
absolutePath3(sourceLocation);
|
|
17491
17789
|
absolutePath3(textField(field(row, "installLocation")));
|
|
17492
17790
|
const updated = textField(field(row, "lastUpdated"));
|
|
17493
|
-
|
|
17791
|
+
need5(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
|
|
17494
17792
|
const millis = Date.parse(updated);
|
|
17495
|
-
|
|
17793
|
+
need5(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
17496
17794
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
17497
17795
|
}
|
|
17498
17796
|
function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
17499
17797
|
const value = parse6(readRegistry2(path, budget));
|
|
17500
17798
|
for (const [, row] of value.entries)
|
|
17501
17799
|
normalizeRow(row);
|
|
17502
|
-
const sha2562 =
|
|
17800
|
+
const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
17503
17801
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
17504
17802
|
}
|
|
17505
17803
|
function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
17506
17804
|
const value = parse6(readRegistry2(path, budget));
|
|
17507
17805
|
for (const [, row] of value.entries)
|
|
17508
17806
|
normalizeRow(row, 2);
|
|
17509
|
-
const sha2562 =
|
|
17807
|
+
const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
17510
17808
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
17511
17809
|
}
|
|
17512
17810
|
|
|
17513
17811
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
17514
|
-
import { createHash as
|
|
17812
|
+
import { createHash as createHash17 } from "crypto";
|
|
17515
17813
|
|
|
17516
17814
|
// src/lib/codex-hook-trust-files.ts
|
|
17517
17815
|
import { closeSync as closeSync12, constants as constants11, fstatSync as fstatSync12, fsyncSync as fsyncSync4, lstatSync as lstatSync20, openSync as openSync12, readSync as readSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
17518
|
-
import { createHash as
|
|
17816
|
+
import { createHash as createHash16 } from "crypto";
|
|
17519
17817
|
import { dirname as dirname16 } from "path";
|
|
17520
|
-
function
|
|
17818
|
+
function need6(value, code) {
|
|
17521
17819
|
if (!value)
|
|
17522
17820
|
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
17523
17821
|
}
|
|
@@ -17525,8 +17823,8 @@ var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs
|
|
|
17525
17823
|
function safeParents(file) {
|
|
17526
17824
|
for (let p = dirname16(file);; p = dirname16(p)) {
|
|
17527
17825
|
const s = lstatSync20(p);
|
|
17528
|
-
|
|
17529
|
-
|
|
17826
|
+
need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
|
|
17827
|
+
need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
17530
17828
|
if (dirname16(p) === p)
|
|
17531
17829
|
break;
|
|
17532
17830
|
}
|
|
@@ -17535,12 +17833,12 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
17535
17833
|
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
17536
17834
|
safeParents(file);
|
|
17537
17835
|
const initial = lstatSync20(file, { bigint: true });
|
|
17538
|
-
|
|
17539
|
-
|
|
17836
|
+
need6(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
|
|
17837
|
+
need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
|
|
17540
17838
|
const fd = openSync12(file, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
|
|
17541
17839
|
try {
|
|
17542
17840
|
const before = fstatSync12(fd, { bigint: true });
|
|
17543
|
-
|
|
17841
|
+
need6(sameStat(initial, before), "INPUT_CHANGED");
|
|
17544
17842
|
const bytes = Buffer.alloc(Number(before.size) + 1);
|
|
17545
17843
|
let length = 0;
|
|
17546
17844
|
while (length < bytes.length) {
|
|
@@ -17549,16 +17847,16 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
17549
17847
|
break;
|
|
17550
17848
|
length += n2;
|
|
17551
17849
|
}
|
|
17552
|
-
|
|
17850
|
+
need6(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync20(file, { bigint: true })), "INPUT_CHANGED");
|
|
17553
17851
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
17554
|
-
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256:
|
|
17852
|
+
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
|
|
17555
17853
|
} finally {
|
|
17556
17854
|
closeSync12(fd);
|
|
17557
17855
|
}
|
|
17558
17856
|
}
|
|
17559
17857
|
function unchanged2(before) {
|
|
17560
17858
|
const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
17561
|
-
|
|
17859
|
+
need6(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
17562
17860
|
}
|
|
17563
17861
|
|
|
17564
17862
|
// src/lib/codex-hook-additions-review.ts
|
|
@@ -17567,11 +17865,12 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
|
17567
17865
|
var proofs = new WeakMap;
|
|
17568
17866
|
|
|
17569
17867
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
17570
|
-
var
|
|
17868
|
+
var sha4 = (text2) => createHash17("sha256").update(text2).digest("hex");
|
|
17869
|
+
var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
|
|
17571
17870
|
var proofs2 = new WeakMap;
|
|
17572
17871
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
17573
17872
|
const proof = proofs2.get(witness);
|
|
17574
|
-
|
|
17873
|
+
need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
17575
17874
|
unchanged2(proof.before);
|
|
17576
17875
|
unchanged2(proof.current);
|
|
17577
17876
|
proof.recheckAdditions();
|
|
@@ -17580,12 +17879,12 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
17580
17879
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
17581
17880
|
return false;
|
|
17582
17881
|
assertCodexHookDiscoveryRecovery(binding, witness);
|
|
17583
|
-
|
|
17882
|
+
need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
17584
17883
|
return true;
|
|
17585
17884
|
}
|
|
17586
17885
|
|
|
17587
17886
|
// src/lib/agent-discovery.ts
|
|
17588
|
-
var digest = (text2) =>
|
|
17887
|
+
var digest = (text2) => createHash18("sha256").update(text2).digest("hex");
|
|
17589
17888
|
function parseConfig(text2, path, toml = false) {
|
|
17590
17889
|
try {
|
|
17591
17890
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -17617,14 +17916,26 @@ function read2(path, changes) {
|
|
|
17617
17916
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
17618
17917
|
return readFileSync17(path, "utf8");
|
|
17619
17918
|
}
|
|
17919
|
+
function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
17920
|
+
const object5 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
|
|
17921
|
+
if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
|
|
17922
|
+
throw new Error("Expected native discovery configuration object");
|
|
17923
|
+
return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object5[field2] ?? null]))));
|
|
17924
|
+
}
|
|
17925
|
+
function isSupersededCodexProjection(agent, configPath, source) {
|
|
17926
|
+
return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual4(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
|
|
17927
|
+
}
|
|
17928
|
+
function supersedesCodexProjection(sources, configPath) {
|
|
17929
|
+
return sources.some((source) => source.path === configPath && source.hashMode === "codex-settings-v2");
|
|
17930
|
+
}
|
|
17620
17931
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
17621
|
-
if (source.hashMode === "codex-settings-v1") {
|
|
17932
|
+
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") {
|
|
17622
17933
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
17623
17934
|
throw new Error("Codex settings witnesses require exact typed metadata");
|
|
17624
|
-
const current = captureCodexSettings(source.path, budget).sha256;
|
|
17935
|
+
const current = (source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
|
|
17625
17936
|
if (current !== source.sha256)
|
|
17626
17937
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
17627
|
-
return changes?.has(source.path) ? hashCodexSettingsReplacement(changes.get(source.path), budget) : current;
|
|
17938
|
+
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
|
|
17628
17939
|
}
|
|
17629
17940
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
17630
17941
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
@@ -17663,10 +17974,7 @@ function projected(source, changes, budget = discoveryByteBudget()) {
|
|
|
17663
17974
|
return null;
|
|
17664
17975
|
if (!source.format)
|
|
17665
17976
|
return digest(text2);
|
|
17666
|
-
|
|
17667
|
-
if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
|
|
17668
|
-
throw new Error("Expected native discovery configuration object");
|
|
17669
|
-
return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field2) => [field2, object5[field2] ?? null]))));
|
|
17977
|
+
return projectNativeDiscoveryFields(text2, source.format, source.fields ?? [], source.path);
|
|
17670
17978
|
}
|
|
17671
17979
|
function verifyAgentDiscovery(binding, codexRecovery) {
|
|
17672
17980
|
if (codexRecovery)
|
|
@@ -17694,9 +18002,9 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
17694
18002
|
safe4(root2);
|
|
17695
18003
|
}
|
|
17696
18004
|
function assertMarketplaceBinding(binding, source) {
|
|
17697
|
-
if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" ||
|
|
18005
|
+
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
|
|
17698
18006
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
17699
|
-
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" ||
|
|
18007
|
+
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename9(source.path) !== "settings.json"))
|
|
17700
18008
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
17701
18009
|
if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
17702
18010
|
throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
|
|
@@ -17723,8 +18031,8 @@ function captureDiscoveryPathSources(paths) {
|
|
|
17723
18031
|
const budget = discoveryByteBudget();
|
|
17724
18032
|
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
|
|
17725
18033
|
}
|
|
17726
|
-
function assertProjectDiscovery(agent, directories, home,
|
|
17727
|
-
const homeConfigPath =
|
|
18034
|
+
function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15, reviewed) {
|
|
18035
|
+
const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
|
|
17728
18036
|
for (const directory of directories) {
|
|
17729
18037
|
const isHome = resolve15(directory) === resolve15(home);
|
|
17730
18038
|
if (agent === "hermes") {
|
|
@@ -17735,7 +18043,7 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15
|
|
|
17735
18043
|
continue;
|
|
17736
18044
|
const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : [".cursor/hooks.json"];
|
|
17737
18045
|
for (const suffix of paths) {
|
|
17738
|
-
const path =
|
|
18046
|
+
const path = canonical6(join27(directory, suffix)), raw = read2(path);
|
|
17739
18047
|
if (raw === null)
|
|
17740
18048
|
continue;
|
|
17741
18049
|
const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
|
|
@@ -17751,14 +18059,14 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15
|
|
|
17751
18059
|
const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
|
|
17752
18060
|
if (exact && projected(exact) === exact.sha256 && managed.length) {
|
|
17753
18061
|
const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
|
|
17754
|
-
admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath ===
|
|
18062
|
+
admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath === canonical6(directory))));
|
|
17755
18063
|
}
|
|
17756
18064
|
}
|
|
17757
18065
|
if (keys.some((key) => config[key] !== undefined && !(key === "enabledPlugins" && admittedPlugins)) || config.disableAllHooks === true || config.disableBundledSkills === false || config.hooksConfig?.enabled === false || config.permission?.skill !== undefined || config.permissions?.deny?.some((rule) => typeof rule === "string" && /^Skill(?:\(|$)/.test(rule)))
|
|
17758
18066
|
throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
|
|
17759
18067
|
}
|
|
17760
18068
|
if (agent === "claude") {
|
|
17761
|
-
const commands =
|
|
18069
|
+
const commands = canonical6(join27(directory, ".claude/commands"));
|
|
17762
18070
|
safe4(commands);
|
|
17763
18071
|
if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
17764
18072
|
throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
|
|
@@ -17769,10 +18077,10 @@ function agentDiscoveryConfigPath(home, agent) {
|
|
|
17769
18077
|
return join27(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
17770
18078
|
}
|
|
17771
18079
|
function resolveAgentDiscovery(options) {
|
|
17772
|
-
const
|
|
18080
|
+
const canonical6 = options.canonical ?? resolve15, home = resolve15(options.home), agent = options.agent;
|
|
17773
18081
|
const sources = [], roots = new Set, builtinNames = [];
|
|
17774
|
-
const witness = (path, format,
|
|
17775
|
-
const source = { path:
|
|
18082
|
+
const witness = (path, format, fields2) => {
|
|
18083
|
+
const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
|
|
17776
18084
|
source.sha256 = projected(source);
|
|
17777
18085
|
sources.push(source);
|
|
17778
18086
|
return read2(source.path);
|
|
@@ -17780,7 +18088,7 @@ function resolveAgentDiscovery(options) {
|
|
|
17780
18088
|
if (agent === "hermes")
|
|
17781
18089
|
assertHermesEnvironment(home);
|
|
17782
18090
|
const configPath = agentDiscoveryConfigPath(home, agent);
|
|
17783
|
-
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [
|
|
18091
|
+
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : ["version"]);
|
|
17784
18092
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
17785
18093
|
const unresolved = (detail) => {
|
|
17786
18094
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
@@ -17839,11 +18147,12 @@ function resolveAgentDiscovery(options) {
|
|
|
17839
18147
|
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
17840
18148
|
unresolved("invalid retained review");
|
|
17841
18149
|
verifyAgentDiscovery(retained);
|
|
17842
|
-
if (!retained.sources.some((source) => source.path ===
|
|
18150
|
+
if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
|
|
17843
18151
|
unresolved("retained review is missing the full agent configuration source");
|
|
17844
|
-
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "codex-settings-v1")) && source.path !==
|
|
18152
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2"))) && source.path !== canonical6(configPath)))
|
|
17845
18153
|
unresolved("retained settings witness names another configuration source");
|
|
17846
|
-
|
|
18154
|
+
const covered = supersedesCodexProjection(retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
|
|
18155
|
+
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual4(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
17847
18156
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
17848
18157
|
return retained;
|
|
17849
18158
|
}
|
|
@@ -17857,14 +18166,15 @@ function resolveAgentDiscovery(options) {
|
|
|
17857
18166
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
17858
18167
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
17859
18168
|
verifyAgentDiscovery(supplied);
|
|
17860
|
-
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "codex-settings-v1")) && source.path !==
|
|
18169
|
+
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2"))) && source.path !== canonical6(configPath)))
|
|
17861
18170
|
throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
|
|
17862
|
-
if (!review.sources.some((source) => source.path ===
|
|
18171
|
+
if (!review.sources.some((source) => source.path === canonical6(configPath)))
|
|
17863
18172
|
throw new Error("Discovery review must include the agent configuration source");
|
|
17864
|
-
|
|
18173
|
+
const reviewedSources = supersedesCodexProjection(review.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
|
|
18174
|
+
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...reviewedSources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
|
|
17865
18175
|
}
|
|
17866
18176
|
function plugin(root2) {
|
|
17867
|
-
root2 =
|
|
18177
|
+
root2 = canonical6(root2);
|
|
17868
18178
|
safe4(root2);
|
|
17869
18179
|
const manifestPath = join27(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
17870
18180
|
const raw = witness(manifestPath);
|
|
@@ -17896,7 +18206,7 @@ function resolveAgentDiscovery(options) {
|
|
|
17896
18206
|
if (agent === "claude") {
|
|
17897
18207
|
const registrations = witness(join27(home, ".claude/plugins/installed_plugins.json"));
|
|
17898
18208
|
const installed = registrations === null ? {} : parseConfig(registrations, join27(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
17899
|
-
const commands =
|
|
18209
|
+
const commands = canonical6(join27(home, ".claude/commands"));
|
|
17900
18210
|
safe4(commands);
|
|
17901
18211
|
if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
|
|
17902
18212
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
@@ -17970,7 +18280,7 @@ function resolveAgentDiscovery(options) {
|
|
|
17970
18280
|
roots.add(join27(root2, "skills"));
|
|
17971
18281
|
}
|
|
17972
18282
|
}
|
|
17973
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join27(home, ".hermes/plugins"), join27(home, ".hermes/hermes-agent")].map((path) =>
|
|
18283
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join27(home, ".hermes/plugins"), join27(home, ".hermes/hermes-agent")].map((path) => canonical6(path))) } : {} };
|
|
17974
18284
|
}
|
|
17975
18285
|
|
|
17976
18286
|
// src/lib/authoring-path.ts
|
|
@@ -17993,7 +18303,7 @@ function physicalPath(path) {
|
|
|
17993
18303
|
let cursor = resolve16(path);
|
|
17994
18304
|
const suffix = [];
|
|
17995
18305
|
while (!lstatSync22(cursor, { throwIfNoEntry: false })) {
|
|
17996
|
-
suffix.unshift(
|
|
18306
|
+
suffix.unshift(basename10(cursor));
|
|
17997
18307
|
const parent = dirname18(cursor);
|
|
17998
18308
|
if (parent === cursor)
|
|
17999
18309
|
return resolve16(path);
|
|
@@ -18162,7 +18472,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
18162
18472
|
return { name: skillName, path: skillPath, manifest, created: true };
|
|
18163
18473
|
}
|
|
18164
18474
|
function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
18165
|
-
const absoluteSource =
|
|
18475
|
+
const absoluteSource = normalize4(sourceDir);
|
|
18166
18476
|
if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
|
|
18167
18477
|
throw new Error(`Import directory not found: ${sourceDir}`);
|
|
18168
18478
|
}
|
|
@@ -18212,7 +18522,7 @@ function physicalImportPath(path) {
|
|
|
18212
18522
|
let ancestor = resolve17(path);
|
|
18213
18523
|
const suffix = [];
|
|
18214
18524
|
while (!existsSync13(ancestor)) {
|
|
18215
|
-
suffix.unshift(
|
|
18525
|
+
suffix.unshift(basename11(ancestor));
|
|
18216
18526
|
ancestor = dirname19(ancestor);
|
|
18217
18527
|
}
|
|
18218
18528
|
return join29(realpathSync9(ancestor), ...suffix);
|
|
@@ -18221,7 +18531,7 @@ function assertDisjointImportPaths(source, destination) {
|
|
|
18221
18531
|
const sourcePath = realpathSync9(source);
|
|
18222
18532
|
const destinationPath = physicalImportPath(destination);
|
|
18223
18533
|
const contains2 = (parent, child) => {
|
|
18224
|
-
const path =
|
|
18534
|
+
const path = relative6(parent, child);
|
|
18225
18535
|
return path === "" || !isAbsolute17(path) && path !== ".." && !path.startsWith(`..${sep8}`);
|
|
18226
18536
|
};
|
|
18227
18537
|
if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
|
|
@@ -18229,7 +18539,7 @@ function assertDisjointImportPaths(source, destination) {
|
|
|
18229
18539
|
}
|
|
18230
18540
|
}
|
|
18231
18541
|
function portPortableSkill(sourcePath, options = {}) {
|
|
18232
|
-
const absoluteSource =
|
|
18542
|
+
const absoluteSource = normalize4(sourcePath);
|
|
18233
18543
|
if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
|
|
18234
18544
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
18235
18545
|
}
|
|
@@ -18237,8 +18547,8 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
18237
18547
|
const explicitName = options.name != null;
|
|
18238
18548
|
const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
|
|
18239
18549
|
if (isOfficialSkillName(skillName) && !options.allowShadow) {
|
|
18240
|
-
const sourceSlug = safeNormalizeName(
|
|
18241
|
-
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${
|
|
18550
|
+
const sourceSlug = safeNormalizeName(basename11(absoluteSource));
|
|
18551
|
+
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename11(absoluteSource)}')` : ""} matches a bundled official skill.`;
|
|
18242
18552
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
18243
18553
|
}
|
|
18244
18554
|
const readOnlyDestination = join29(resolvePortableSkillsRoot(options, false), skillName);
|
|
@@ -18505,7 +18815,7 @@ function safeIsDirectory(path) {
|
|
|
18505
18815
|
function isSafeRelativePath2(value) {
|
|
18506
18816
|
if (!value.trim() || isAbsolute17(value))
|
|
18507
18817
|
return false;
|
|
18508
|
-
const normalized =
|
|
18818
|
+
const normalized = normalize4(value).replace(/\\/g, "/");
|
|
18509
18819
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
18510
18820
|
}
|
|
18511
18821
|
function add3(target, code, message) {
|
|
@@ -18819,9 +19129,9 @@ function getAllTags() {
|
|
|
18819
19129
|
return Array.from(tagSet).sort();
|
|
18820
19130
|
}
|
|
18821
19131
|
// src/lib/run-state.ts
|
|
18822
|
-
import { createHash as
|
|
19132
|
+
import { createHash as createHash19, randomBytes } from "crypto";
|
|
18823
19133
|
import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync19, readdirSync as readdirSync13, statSync as statSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
18824
|
-
import { extname, join as join31, relative as
|
|
19134
|
+
import { extname, join as join31, relative as relative7 } from "path";
|
|
18825
19135
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
18826
19136
|
const now = new Date;
|
|
18827
19137
|
const id = createRunId(now);
|
|
@@ -18946,7 +19256,7 @@ function collectRunArtifacts(context) {
|
|
|
18946
19256
|
artifacts.push({
|
|
18947
19257
|
path: toProjectRelative(context.targetDir, path),
|
|
18948
19258
|
mime: mimeForPath(path),
|
|
18949
|
-
sha256:
|
|
19259
|
+
sha256: createHash19("sha256").update(bytes).digest("hex"),
|
|
18950
19260
|
sizeBytes: stat2.size
|
|
18951
19261
|
});
|
|
18952
19262
|
}
|
|
@@ -18977,7 +19287,7 @@ function createRunId(now) {
|
|
|
18977
19287
|
return `run_${now.getTime().toString(36)}_${randomBytes(4).toString("hex")}`;
|
|
18978
19288
|
}
|
|
18979
19289
|
function toProjectRelative(targetDir, path) {
|
|
18980
|
-
const rel =
|
|
19290
|
+
const rel = relative7(targetDir, path).split(/[\\/]/).join("/");
|
|
18981
19291
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
18982
19292
|
}
|
|
18983
19293
|
function mimeForPath(path) {
|
|
@@ -22304,15 +22614,15 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
|
22304
22614
|
} catch (_err) {}
|
|
22305
22615
|
}
|
|
22306
22616
|
const input = payload.value;
|
|
22307
|
-
const
|
|
22308
|
-
const isValidDate =
|
|
22617
|
+
const isDate2 = input instanceof Date;
|
|
22618
|
+
const isValidDate = isDate2 && !Number.isNaN(input.getTime());
|
|
22309
22619
|
if (isValidDate)
|
|
22310
22620
|
return payload;
|
|
22311
22621
|
payload.issues.push({
|
|
22312
22622
|
expected: "date",
|
|
22313
22623
|
code: "invalid_type",
|
|
22314
22624
|
input,
|
|
22315
|
-
...
|
|
22625
|
+
...isDate2 ? { received: "Invalid Date" } : {},
|
|
22316
22626
|
inst
|
|
22317
22627
|
});
|
|
22318
22628
|
return payload;
|
|
@@ -38365,7 +38675,7 @@ function clone2(value) {
|
|
|
38365
38675
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
38366
38676
|
}
|
|
38367
38677
|
// src/lib/canonical-json.ts
|
|
38368
|
-
import { createHash as
|
|
38678
|
+
import { createHash as createHash20 } from "crypto";
|
|
38369
38679
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
38370
38680
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
38371
38681
|
}
|
|
@@ -38409,7 +38719,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
38409
38719
|
return visit3(value, 0);
|
|
38410
38720
|
}
|
|
38411
38721
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
38412
|
-
return
|
|
38722
|
+
return createHash20("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
38413
38723
|
}
|
|
38414
38724
|
|
|
38415
38725
|
// src/lib/remote-recurring-contract.ts
|
|
@@ -39165,7 +39475,7 @@ function getApiUrl(action, env = process.env, options = {}) {
|
|
|
39165
39475
|
// src/lib/remote-run-contract.ts
|
|
39166
39476
|
var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
|
|
39167
39477
|
function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
39168
|
-
const record6 =
|
|
39478
|
+
const record6 = isRecord4(payload) ? payload : {};
|
|
39169
39479
|
return {
|
|
39170
39480
|
contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
|
|
39171
39481
|
...pickString(record6, "id"),
|
|
@@ -39187,7 +39497,7 @@ function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
|
39187
39497
|
...hasOwn(record6, "details") ? { details: record6.details } : {}
|
|
39188
39498
|
};
|
|
39189
39499
|
}
|
|
39190
|
-
function
|
|
39500
|
+
function isRecord4(value) {
|
|
39191
39501
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
39192
39502
|
}
|
|
39193
39503
|
function hasOwn(record6, key) {
|
|
@@ -39319,7 +39629,7 @@ function creditCheckoutFailure(value, status, key) {
|
|
|
39319
39629
|
|
|
39320
39630
|
// src/lib/remote-profile.ts
|
|
39321
39631
|
function customerNamePatch(input2, field2) {
|
|
39322
|
-
if (!
|
|
39632
|
+
if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
|
|
39323
39633
|
throw new Error("Provide only the requested name field.");
|
|
39324
39634
|
const value = input2[field2];
|
|
39325
39635
|
if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
|
|
@@ -39327,22 +39637,22 @@ function customerNamePatch(input2, field2) {
|
|
|
39327
39637
|
}
|
|
39328
39638
|
return { [field2]: value.trim() };
|
|
39329
39639
|
}
|
|
39330
|
-
function
|
|
39640
|
+
function isRecord5(value) {
|
|
39331
39641
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
39332
39642
|
}
|
|
39333
39643
|
function string4(value) {
|
|
39334
39644
|
return typeof value === "string" && value.length > 0;
|
|
39335
39645
|
}
|
|
39336
39646
|
function parseUpdatedProfile(value) {
|
|
39337
|
-
const user =
|
|
39338
|
-
if (!
|
|
39647
|
+
const user = isRecord5(value) && value.user;
|
|
39648
|
+
if (!isRecord5(user) || !string4(user.id) || !string4(user.email) || !(user.displayName === null || typeof user.displayName === "string") || typeof user.role !== "string" || !["owner", "admin", "member", "viewer"].includes(user.role)) {
|
|
39339
39649
|
throw new Error("The server returned an invalid account profile.");
|
|
39340
39650
|
}
|
|
39341
39651
|
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
39342
39652
|
}
|
|
39343
39653
|
function parseUpdatedWorkspace(value) {
|
|
39344
|
-
const organization2 =
|
|
39345
|
-
if (!
|
|
39654
|
+
const organization2 = isRecord5(value) && value.organization;
|
|
39655
|
+
if (!isRecord5(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
|
|
39346
39656
|
throw new Error("The server returned an invalid workspace.");
|
|
39347
39657
|
}
|
|
39348
39658
|
return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
|
|
@@ -40531,11 +40841,11 @@ function validateCronField(expr, min, max, label) {
|
|
|
40531
40841
|
return { valid: true };
|
|
40532
40842
|
}
|
|
40533
40843
|
function validateCron(expr) {
|
|
40534
|
-
const
|
|
40535
|
-
if (
|
|
40536
|
-
return { valid: false, error: `Expected 5 fields, got ${
|
|
40844
|
+
const fields2 = expr.trim().split(/\s+/);
|
|
40845
|
+
if (fields2.length !== 5) {
|
|
40846
|
+
return { valid: false, error: `Expected 5 fields, got ${fields2.length}. Format: "minute hour day-of-month month day-of-week"` };
|
|
40537
40847
|
}
|
|
40538
|
-
const [minuteF, hourF, domF, monthF, dowF] =
|
|
40848
|
+
const [minuteF, hourF, domF, monthF, dowF] = fields2;
|
|
40539
40849
|
const checks3 = [
|
|
40540
40850
|
{ expr: minuteF, min: 0, max: 59, label: "minute" },
|
|
40541
40851
|
{ expr: hourF, min: 0, max: 23, label: "hour" },
|
|
@@ -40676,7 +40986,7 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
40676
40986
|
saveSchedules(data, targetDir);
|
|
40677
40987
|
}
|
|
40678
40988
|
// src/lib/revision.ts
|
|
40679
|
-
import { createHash as
|
|
40989
|
+
import { createHash as createHash21 } from "crypto";
|
|
40680
40990
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
40681
40991
|
function revisionIdOf(content) {
|
|
40682
40992
|
const canonicalContent = {
|
|
@@ -40697,8 +41007,8 @@ function revisionIdOf(content) {
|
|
|
40697
41007
|
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
40698
41008
|
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
40699
41009
|
}
|
|
40700
|
-
const
|
|
40701
|
-
return
|
|
41010
|
+
const canonical6 = JSON.stringify(canonicalContent);
|
|
41011
|
+
return createHash21("sha256").update(canonical6).digest("hex");
|
|
40702
41012
|
}
|
|
40703
41013
|
function revisionIdOfRecord(record7) {
|
|
40704
41014
|
return revisionIdOf(record7);
|
|
@@ -41239,7 +41549,7 @@ async function requestInvitationEmail(origin, action, input2) {
|
|
|
41239
41549
|
}
|
|
41240
41550
|
|
|
41241
41551
|
// src/lib/remote-private-publications.ts
|
|
41242
|
-
import { createHash as
|
|
41552
|
+
import { createHash as createHash22 } from "crypto";
|
|
41243
41553
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
41244
41554
|
var failures2 = {
|
|
41245
41555
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -41286,7 +41596,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
|
41286
41596
|
var record8 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
41287
41597
|
var exact = (v, keys) => record8(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
|
|
41288
41598
|
var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
|
|
41289
|
-
var publicationSha256 = (bytes) =>
|
|
41599
|
+
var publicationSha256 = (bytes) => createHash22("sha256").update(bytes).digest("hex");
|
|
41290
41600
|
function checkedPublicationDeclaration(value) {
|
|
41291
41601
|
if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash3(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
|
|
41292
41602
|
return bad();
|
|
@@ -41585,7 +41895,7 @@ async function requestAuthApi(instance, path, options) {
|
|
|
41585
41895
|
const text3 = await res.text();
|
|
41586
41896
|
const body = text3 ? parseJsonBody(text3) : {};
|
|
41587
41897
|
if (!res.ok) {
|
|
41588
|
-
const record9 =
|
|
41898
|
+
const record9 = isRecord6(body) ? body : {};
|
|
41589
41899
|
const detail = typeof record9.detail === "string" ? record9.detail : undefined;
|
|
41590
41900
|
const error61 = typeof record9.error === "string" ? record9.error : undefined;
|
|
41591
41901
|
const code = typeof record9.code === "string" ? record9.code : undefined;
|
|
@@ -41613,7 +41923,7 @@ function condenseErrorBody(text3) {
|
|
|
41613
41923
|
return collapsed;
|
|
41614
41924
|
return `${collapsed.slice(0, MAX_ERROR_DETAIL_LENGTH - 1).trimEnd()}\u2026`;
|
|
41615
41925
|
}
|
|
41616
|
-
function
|
|
41926
|
+
function isRecord6(value) {
|
|
41617
41927
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
41618
41928
|
}
|
|
41619
41929
|
|
|
@@ -41770,7 +42080,7 @@ class RemoteSkillsAuthClient {
|
|
|
41770
42080
|
|
|
41771
42081
|
// src/lib/recurring-recovery.ts
|
|
41772
42082
|
var uuid12 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
41773
|
-
var
|
|
42083
|
+
var sha5 = exports_external.string().regex(/^[a-f0-9]{64}$/);
|
|
41774
42084
|
var common = exports_external.object({
|
|
41775
42085
|
contractVersion: exports_external.literal(1),
|
|
41776
42086
|
apiOrigin: exports_external.string().min(1).max(4096),
|
|
@@ -41783,7 +42093,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
|
|
|
41783
42093
|
common.extend({
|
|
41784
42094
|
operation: exports_external.literal("activate"),
|
|
41785
42095
|
draftId: uuid12,
|
|
41786
|
-
termsSha256:
|
|
42096
|
+
termsSha256: sha5,
|
|
41787
42097
|
approvalDeadline: exports_external.string().datetime(),
|
|
41788
42098
|
approval: recurringActivationSchema
|
|
41789
42099
|
}).strict(),
|
|
@@ -41791,7 +42101,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
|
|
|
41791
42101
|
]);
|
|
41792
42102
|
var stateSchema = exports_external.object({
|
|
41793
42103
|
contractVersion: exports_external.literal(1),
|
|
41794
|
-
intentSha256:
|
|
42104
|
+
intentSha256: sha5,
|
|
41795
42105
|
phase: exports_external.enum(["prepared", "pending", "observed"]),
|
|
41796
42106
|
consentId: uuid12.nullable()
|
|
41797
42107
|
}).strict();
|
|
@@ -41951,7 +42261,7 @@ function findSkillsParityForMcpTool(tool) {
|
|
|
41951
42261
|
}
|
|
41952
42262
|
// src/lib/registry-sync.ts
|
|
41953
42263
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync12 } from "fs";
|
|
41954
|
-
import { dirname as dirname21, relative as
|
|
42264
|
+
import { dirname as dirname21, relative as relative8 } from "path";
|
|
41955
42265
|
function createRegistrySyncArtifact(options = {}) {
|
|
41956
42266
|
const profile = options.profile ?? "all";
|
|
41957
42267
|
const includeDocs = options.includeDocs ?? true;
|
|
@@ -41963,7 +42273,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
41963
42273
|
const registry2 = [...loadRegistryProfile(profile)].sort((a, b) => a.name.localeCompare(b.name));
|
|
41964
42274
|
const skills = registry2.map((skill) => {
|
|
41965
42275
|
const skillPath = getSkillPath(skill.name);
|
|
41966
|
-
const directory2 =
|
|
42276
|
+
const directory2 = relative8(process.cwd(), skillPath) || skillPath;
|
|
41967
42277
|
const validation = includeValidation ? validateSkillDirectory(skill.name, skillPath, skill) : undefined;
|
|
41968
42278
|
const docs = includeDocs ? buildDocs(skill.name) : undefined;
|
|
41969
42279
|
return {
|
|
@@ -43152,7 +43462,7 @@ function isApiMode(env = process.env) {
|
|
|
43152
43462
|
}
|
|
43153
43463
|
}
|
|
43154
43464
|
// src/lib/native-storage.ts
|
|
43155
|
-
import { createHash as
|
|
43465
|
+
import { createHash as createHash23, createHmac as createHmac2 } from "crypto";
|
|
43156
43466
|
import {
|
|
43157
43467
|
existsSync as existsSync19,
|
|
43158
43468
|
mkdirSync as mkdirSync15,
|
|
@@ -43161,7 +43471,7 @@ import {
|
|
|
43161
43471
|
statSync as statSync12,
|
|
43162
43472
|
writeFileSync as writeFileSync13
|
|
43163
43473
|
} from "fs";
|
|
43164
|
-
import { dirname as dirname23, join as join35, normalize as
|
|
43474
|
+
import { dirname as dirname23, join as join35, normalize as normalize5, relative as relative9, sep as sep9 } from "path";
|
|
43165
43475
|
var SKILLS_STORAGE_TABLES = [
|
|
43166
43476
|
"skills_sync_records",
|
|
43167
43477
|
"skills_sync_cursors"
|
|
@@ -43324,11 +43634,11 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
43324
43634
|
if (existsSync19(projectStateDir)) {
|
|
43325
43635
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
43326
43636
|
const bytes = readFileSync23(filePath);
|
|
43327
|
-
const relativePath = toPosix(
|
|
43637
|
+
const relativePath = toPosix(relative9(targetDir, filePath));
|
|
43328
43638
|
files.push({
|
|
43329
43639
|
path: relativePath,
|
|
43330
43640
|
sizeBytes: bytes.byteLength,
|
|
43331
|
-
sha256:
|
|
43641
|
+
sha256: createHash23("sha256").update(bytes).digest("hex"),
|
|
43332
43642
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
43333
43643
|
});
|
|
43334
43644
|
}
|
|
@@ -43353,7 +43663,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
|
|
|
43353
43663
|
continue;
|
|
43354
43664
|
}
|
|
43355
43665
|
const bytes = Buffer.from(file2.contentBase64, "base64");
|
|
43356
|
-
const hash4 =
|
|
43666
|
+
const hash4 = createHash23("sha256").update(bytes).digest("hex");
|
|
43357
43667
|
if (hash4 !== file2.sha256) {
|
|
43358
43668
|
throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
|
|
43359
43669
|
}
|
|
@@ -43665,7 +43975,7 @@ function walkFiles2(dir) {
|
|
|
43665
43975
|
return files;
|
|
43666
43976
|
}
|
|
43667
43977
|
function resolveSnapshotPath(targetDir, snapshotPath) {
|
|
43668
|
-
const normalizedPath =
|
|
43978
|
+
const normalizedPath = normalize5(snapshotPath);
|
|
43669
43979
|
if (normalizedPath.startsWith("..") || normalizedPath.includes(`${sep9}..${sep9}`) || normalizedPath.startsWith(sep9)) {
|
|
43670
43980
|
throw new Error(`Unsafe snapshot path: ${snapshotPath}`);
|
|
43671
43981
|
}
|
|
@@ -43685,7 +43995,7 @@ function toIsoString(value) {
|
|
|
43685
43995
|
return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
|
|
43686
43996
|
}
|
|
43687
43997
|
function sha256Hex2(value) {
|
|
43688
|
-
return
|
|
43998
|
+
return createHash23("sha256").update(value).digest("hex");
|
|
43689
43999
|
}
|
|
43690
44000
|
function normalizeHeaders(headers) {
|
|
43691
44001
|
const result = {};
|
|
@@ -43737,7 +44047,7 @@ function toArrayBuffer(bytes) {
|
|
|
43737
44047
|
return buffer;
|
|
43738
44048
|
}
|
|
43739
44049
|
// src/lib/station-snapshot.ts
|
|
43740
|
-
import { createHash as
|
|
44050
|
+
import { createHash as createHash24 } from "crypto";
|
|
43741
44051
|
import {
|
|
43742
44052
|
copyFileSync,
|
|
43743
44053
|
mkdirSync as mkdirSync16,
|
|
@@ -43745,7 +44055,7 @@ import {
|
|
|
43745
44055
|
statSync as statSync14,
|
|
43746
44056
|
writeFileSync as writeFileSync14
|
|
43747
44057
|
} from "fs";
|
|
43748
|
-
import { dirname as dirname24, isAbsolute as isAbsolute19, relative as
|
|
44058
|
+
import { dirname as dirname24, isAbsolute as isAbsolute19, relative as relative10, resolve as resolve18, sep as sep11 } from "path";
|
|
43749
44059
|
|
|
43750
44060
|
// src/lib/portable-snapshot-filter.ts
|
|
43751
44061
|
import { readdirSync as readdirSync15, statSync as statSync13 } from "fs";
|
|
@@ -43912,7 +44222,7 @@ function validateStationId(stationId) {
|
|
|
43912
44222
|
}
|
|
43913
44223
|
}
|
|
43914
44224
|
function sha256File(filePath) {
|
|
43915
|
-
return
|
|
44225
|
+
return createHash24("sha256").update(readFileSync25(filePath)).digest("hex");
|
|
43916
44226
|
}
|
|
43917
44227
|
function scanHome(definition, homesRoot) {
|
|
43918
44228
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -44010,7 +44320,7 @@ function writeStationSnapshot(options) {
|
|
|
44010
44320
|
const untouched = [];
|
|
44011
44321
|
for (const plan of plans) {
|
|
44012
44322
|
const destination = resolve18(repoRoot, plan.destination);
|
|
44013
|
-
const destinationRelative =
|
|
44323
|
+
const destinationRelative = relative10(repoRoot, destination);
|
|
44014
44324
|
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep11) || isAbsolute19(destinationRelative)) {
|
|
44015
44325
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
44016
44326
|
}
|
|
@@ -44063,7 +44373,7 @@ function writeStationSnapshot(options) {
|
|
|
44063
44373
|
};
|
|
44064
44374
|
}
|
|
44065
44375
|
// src/lib/station-hydrate.ts
|
|
44066
|
-
import { createHash as
|
|
44376
|
+
import { createHash as createHash25 } from "crypto";
|
|
44067
44377
|
import {
|
|
44068
44378
|
copyFileSync as copyFileSync2,
|
|
44069
44379
|
mkdirSync as mkdirSync17,
|
|
@@ -44269,7 +44579,7 @@ function skillSha256(skill) {
|
|
|
44269
44579
|
return sha256File(skill.files[0].winner.fullPath);
|
|
44270
44580
|
}
|
|
44271
44581
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
44272
|
-
return
|
|
44582
|
+
return createHash25("sha256").update(joined.sort().join(`
|
|
44273
44583
|
`)).digest("hex");
|
|
44274
44584
|
}
|
|
44275
44585
|
function writeStationHydration(options) {
|
|
@@ -44597,7 +44907,7 @@ async function inspectLocked(client2, directory2, cancel) {
|
|
|
44597
44907
|
return privatePublicationResult(directory2, receipt);
|
|
44598
44908
|
}
|
|
44599
44909
|
// src/lib/skill-context.ts
|
|
44600
|
-
import { createHash as
|
|
44910
|
+
import { createHash as createHash26 } from "crypto";
|
|
44601
44911
|
import { resolve as resolve21 } from "path";
|
|
44602
44912
|
function words(value) {
|
|
44603
44913
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
@@ -44613,9 +44923,9 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
44613
44923
|
if (prompt.length > 128 * 1024 || (input2.paths?.length ?? 0) > 100 || (input2.skills?.length ?? 0) > 100) {
|
|
44614
44924
|
throw new SkillSelectionError("CONTEXT_INPUT_TOO_LARGE", "The Skills context input exceeds its size limit.");
|
|
44615
44925
|
}
|
|
44616
|
-
const maxChars = options.maxChars
|
|
44926
|
+
const maxChars = options.maxChars;
|
|
44617
44927
|
const maxSkills = options.maxSkills ?? 3;
|
|
44618
|
-
if (!Number.isInteger(maxChars) || maxChars < 512 || maxChars > 64000 || !Number.isInteger(maxSkills) || maxSkills < 1 || maxSkills > 20) {
|
|
44928
|
+
if (maxChars !== undefined && (!Number.isInteger(maxChars) || maxChars < 512 || maxChars > 64000) || !Number.isInteger(maxSkills) || maxSkills < 1 || maxSkills > 20) {
|
|
44619
44929
|
throw new SkillSelectionError("INVALID_CONTEXT_BUDGET", "Context limits must be 512\u201364000 characters and 1\u201320 skills.");
|
|
44620
44930
|
}
|
|
44621
44931
|
const profileId = input2.profileId ?? "default";
|
|
@@ -44672,7 +44982,7 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
44672
44982
|
const { content } = readSelectedDocument(entries);
|
|
44673
44983
|
const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${reason})
|
|
44674
44984
|
${content}`;
|
|
44675
|
-
if (chars + section.length + (sections.length ? 2 : 0) > maxChars) {
|
|
44985
|
+
if (maxChars !== undefined && chars + section.length + (sections.length ? 2 : 0) > maxChars) {
|
|
44676
44986
|
omitted.push({ slug: selection.slug, version: selection.version, reason: "context-budget", loadCommand });
|
|
44677
44987
|
continue;
|
|
44678
44988
|
}
|
|
@@ -44686,7 +44996,7 @@ ${content}`;
|
|
|
44686
44996
|
`);
|
|
44687
44997
|
const receipt = {
|
|
44688
44998
|
schemaVersion: 1,
|
|
44689
|
-
id:
|
|
44999
|
+
id: createHash26("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
44690
45000
|
profileId: profile.profileId,
|
|
44691
45001
|
profileRevision: profile.profileRevision,
|
|
44692
45002
|
authority: profile.authority,
|
|
@@ -44702,9 +45012,9 @@ ${content}`;
|
|
|
44702
45012
|
return { context: context2, selections, omitted, receipt };
|
|
44703
45013
|
}
|
|
44704
45014
|
// src/lib/session-reconciliation.ts
|
|
44705
|
-
import { createHash as
|
|
45015
|
+
import { createHash as createHash27 } from "crypto";
|
|
44706
45016
|
var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
|
|
44707
|
-
var digest3 = (value) =>
|
|
45017
|
+
var digest3 = (value) => createHash27("sha256").update(JSON.stringify(value)).digest("hex");
|
|
44708
45018
|
function exactTimestamp(value) {
|
|
44709
45019
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
44710
45020
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -44871,6 +45181,8 @@ export {
|
|
|
44871
45181
|
publicDiscoveryDocumentation,
|
|
44872
45182
|
publicDiscoveryDependencies,
|
|
44873
45183
|
projectCodexNativeSkillCatalog,
|
|
45184
|
+
projectCodexInstalledPlugins,
|
|
45185
|
+
projectCodexInstalledPluginEntries,
|
|
44874
45186
|
preparePrivatePublication,
|
|
44875
45187
|
portPortableSkillDirectory,
|
|
44876
45188
|
portPortableSkill,
|
|
@@ -45001,6 +45313,7 @@ export {
|
|
|
45001
45313
|
captureDiscoveryPathSources,
|
|
45002
45314
|
captureDiscoveryDirectories,
|
|
45003
45315
|
captureDiscoveryByteSources,
|
|
45316
|
+
captureCodexSettingsV2,
|
|
45004
45317
|
captureCodexSettings,
|
|
45005
45318
|
captureCodexNativeSkillCatalog,
|
|
45006
45319
|
captureClaudeSettingsV3,
|