@hasna/skills 0.10.62 → 0.10.63
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 +41 -0
- package/bin/index.js +1958 -1690
- package/bin/mcp.js +698 -551
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +1055 -816
- package/dist/lib/agent-discovery.d.ts +3 -0
- package/dist/lib/agent-integration.d.ts +19 -0
- package/dist/lib/retained-review-artifacts.d.ts +35 -0
- package/dist/runtime-prerequisites.js +10 -1
- package/dist/sdk/index.js +1050 -811
- package/package.json +1 -1
package/bin/mcp.js
CHANGED
|
@@ -23,7 +23,7 @@ var package_default;
|
|
|
23
23
|
var init_package = __esm(() => {
|
|
24
24
|
package_default = {
|
|
25
25
|
name: "@hasna/skills",
|
|
26
|
-
version: "0.10.
|
|
26
|
+
version: "0.10.63",
|
|
27
27
|
description: "Skills library for AI coding agents",
|
|
28
28
|
type: "module",
|
|
29
29
|
bin: {
|
|
@@ -23119,6 +23119,15 @@ function assertAgentPolicyCollections(policy) {
|
|
|
23119
23119
|
}
|
|
23120
23120
|
} else
|
|
23121
23121
|
requireBound(source.managedPlugins === undefined);
|
|
23122
|
+
if (source.reviewArtifact !== undefined) {
|
|
23123
|
+
const artifact = source.reviewArtifact;
|
|
23124
|
+
requireBound(agent === "codex" && value.method === "reviewed" && source.hashMode === "bytes" && object2(artifact) && Object.keys(artifact).length === 6 && Object.keys(artifact).every((key) => ["version", "kind", "storeRoot", "originalPath", "sha256", "catalogSha256"].includes(key)) && artifact.version === 1 && artifact.kind === "codex-native-catalog" && artifact.sha256 === source.sha256 && /^[a-f0-9]{64}$/.test(artifact.catalogSha256));
|
|
23125
|
+
for (const path of [artifact.storeRoot, artifact.originalPath]) {
|
|
23126
|
+
text(path);
|
|
23127
|
+
requireBound(isAbsolute5(path) && resolve4(path) === path);
|
|
23128
|
+
}
|
|
23129
|
+
requireBound(source.path === join10(artifact.storeRoot, "agent-discovery", "artifacts", artifact.sha256, "catalog.json"));
|
|
23130
|
+
}
|
|
23122
23131
|
if (source.format !== undefined)
|
|
23123
23132
|
requireBound(["json", "toml", "yaml"].includes(source.format));
|
|
23124
23133
|
if (source.fields !== undefined)
|
|
@@ -32722,11 +32731,55 @@ var init_codex_hook_rpc = __esm(() => {
|
|
|
32722
32731
|
});
|
|
32723
32732
|
|
|
32724
32733
|
// src/lib/codex-native-skill-catalog.ts
|
|
32734
|
+
import { isAbsolute as isAbsolute10, normalize as normalize2 } from "path";
|
|
32725
32735
|
function codexPluginSourceIsConfigControlled(plugin) {
|
|
32726
32736
|
const remoteRealms = ["openai-curated-remote", "created-by-me-remote", "workspace-directory", "workspace-shared-with-me", "workspace-shared-with-me-private", "workspace-shared-with-me-unlisted"];
|
|
32727
32737
|
return Boolean(plugin.sourceType && ["local", "git", "npm"].includes(plugin.sourceType) && plugin.sourceSha256 && /^[a-f0-9]{64}$/.test(plugin.sourceSha256) && !plugin.remotePluginId && !remoteRealms.includes(plugin.id.slice(plugin.id.lastIndexOf("@") + 1)));
|
|
32728
32738
|
}
|
|
32729
|
-
|
|
32739
|
+
function refuse() {
|
|
32740
|
+
throw new Error("CODEX_NATIVE_SKILL_CATALOG_INVALID");
|
|
32741
|
+
}
|
|
32742
|
+
function projectCodexNativeSkillCatalog(response, cwd) {
|
|
32743
|
+
if (!absolutePath2(cwd) || !object5(response) || !Array.isArray(response.data) || response.data.length !== 1)
|
|
32744
|
+
refuse();
|
|
32745
|
+
const entry = response.data[0];
|
|
32746
|
+
if (!object5(entry) || entry.cwd !== cwd || !Array.isArray(entry.errors) || entry.errors.length !== 0 || !Array.isArray(entry.skills) || entry.skills.length > MAX_SKILLS)
|
|
32747
|
+
refuse();
|
|
32748
|
+
const paths = new Set;
|
|
32749
|
+
return entry.skills.map((value) => {
|
|
32750
|
+
if (!object5(value) || !scalar(value.name, MAX_NAME_BYTES) || !absolutePath2(value.path) || typeof value.enabled !== "boolean" || value.pluginId !== undefined && value.pluginId !== null && !scalar(value.pluginId, MAX_NAME_BYTES))
|
|
32751
|
+
refuse();
|
|
32752
|
+
if (paths.has(value.path))
|
|
32753
|
+
refuse();
|
|
32754
|
+
paths.add(value.path);
|
|
32755
|
+
return { name: value.name, path: value.path, enabled: value.enabled, pluginId: value.pluginId ?? null };
|
|
32756
|
+
});
|
|
32757
|
+
}
|
|
32758
|
+
function projectCodexInstalledPluginEntries(value) {
|
|
32759
|
+
if (!Array.isArray(value) || value.length > MAX_SKILLS)
|
|
32760
|
+
refuse();
|
|
32761
|
+
const ids = new Set;
|
|
32762
|
+
return value.map((item) => {
|
|
32763
|
+
if (!object5(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && !["remote", "local", "git", "npm"].includes(item.sourceType) || (["local", "git", "npm"].includes(item.sourceType) ? typeof item.sourceSha256 !== "string" || !/^[a-f0-9]{64}$/.test(item.sourceSha256) : item.sourceSha256 !== undefined) || (item.sourceType === "local" ? !absolutePath2(item.sourcePath) || normalize2(item.sourcePath) !== item.sourcePath || item.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: item.sourcePath })) : item.sourcePath !== undefined))
|
|
32764
|
+
refuse();
|
|
32765
|
+
const at = item.id.lastIndexOf("@");
|
|
32766
|
+
if (at < 1 || item.id.slice(0, at) !== item.name || at === item.id.length - 1 || ids.has(item.id))
|
|
32767
|
+
refuse();
|
|
32768
|
+
ids.add(item.id);
|
|
32769
|
+
return {
|
|
32770
|
+
id: item.id,
|
|
32771
|
+
name: item.name,
|
|
32772
|
+
installed: item.installed,
|
|
32773
|
+
enabled: item.enabled,
|
|
32774
|
+
localVersion: item.localVersion ?? null,
|
|
32775
|
+
...item.sourceType === undefined ? {} : { sourceType: item.sourceType },
|
|
32776
|
+
...item.sourceSha256 === undefined ? {} : { sourceSha256: item.sourceSha256 },
|
|
32777
|
+
...item.sourcePath === undefined ? {} : { sourcePath: item.sourcePath },
|
|
32778
|
+
...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
|
|
32779
|
+
};
|
|
32780
|
+
});
|
|
32781
|
+
}
|
|
32782
|
+
var MAX_SKILLS = 4096, MAX_NAME_BYTES = 1024, MAX_PATH_BYTES = 16384, MAX_PLUGIN_ID_BYTES = 1024, object5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value), remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value), absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute10(value);
|
|
32730
32783
|
var init_codex_native_skill_catalog = __esm(() => {
|
|
32731
32784
|
init_codex_hook_rpc();
|
|
32732
32785
|
init_codex_native_compatibility();
|
|
@@ -32736,7 +32789,7 @@ var init_codex_native_skill_catalog = __esm(() => {
|
|
|
32736
32789
|
// src/lib/codex-plugin-skill-controls.ts
|
|
32737
32790
|
import { basename as basename6, dirname as dirname12, join as join18, relative as relative5, resolve as resolve11, sep as sep6 } from "path";
|
|
32738
32791
|
import { lstatSync as lstatSync12, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync4 } from "fs";
|
|
32739
|
-
function
|
|
32792
|
+
function refuse2() {
|
|
32740
32793
|
throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
|
|
32741
32794
|
}
|
|
32742
32795
|
function remoteInstallationMatches(parent, remotePluginId, read) {
|
|
@@ -32761,17 +32814,17 @@ function appControls(path, read) {
|
|
|
32761
32814
|
sha2562 = hashNativeJsonControls(text2);
|
|
32762
32815
|
value = JSON.parse(text2);
|
|
32763
32816
|
} catch {
|
|
32764
|
-
|
|
32817
|
+
refuse2();
|
|
32765
32818
|
}
|
|
32766
|
-
const
|
|
32767
|
-
if (!
|
|
32768
|
-
|
|
32819
|
+
const object6 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
32820
|
+
if (!object6(value) || Object.keys(value).length !== 1 || !object6(value.apps))
|
|
32821
|
+
refuse2();
|
|
32769
32822
|
const apps = Object.entries(value.apps);
|
|
32770
32823
|
if (!apps.length || apps.length > 256)
|
|
32771
|
-
|
|
32824
|
+
refuse2();
|
|
32772
32825
|
for (const [name, entry] of apps) {
|
|
32773
|
-
if (!identifier2(name) || !
|
|
32774
|
-
|
|
32826
|
+
if (!identifier2(name) || !object6(entry) || !Object.keys(entry).every((key) => ["id", "category", "required"].includes(key)) || typeof entry.id !== "string" || !/^[A-Za-z0-9_-]{1,256}$/.test(entry.id) || entry.category !== undefined && (typeof entry.category !== "string" || !entry.category.trim() || entry.category.length > 256 || /[\x00-\x1f\x7f]/.test(entry.category)) || entry.required !== undefined && typeof entry.required !== "boolean")
|
|
32827
|
+
refuse2();
|
|
32775
32828
|
}
|
|
32776
32829
|
return sha2562;
|
|
32777
32830
|
}
|
|
@@ -32782,26 +32835,26 @@ function mcpControls(path, read) {
|
|
|
32782
32835
|
sha2562 = hashNativeJsonControls(text2);
|
|
32783
32836
|
value = JSON.parse(text2);
|
|
32784
32837
|
} catch {
|
|
32785
|
-
|
|
32838
|
+
refuse2();
|
|
32786
32839
|
}
|
|
32787
|
-
const
|
|
32788
|
-
if (!
|
|
32789
|
-
|
|
32840
|
+
const object6 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
32841
|
+
if (!object6(value) || Object.keys(value).length !== 1 || !object6(value.mcpServers))
|
|
32842
|
+
refuse2();
|
|
32790
32843
|
const servers = Object.entries(value.mcpServers);
|
|
32791
32844
|
if (!servers.length || servers.length > 256)
|
|
32792
|
-
|
|
32845
|
+
refuse2();
|
|
32793
32846
|
for (const [name, server] of servers)
|
|
32794
32847
|
if (!isCodexLocalStdioMcpServer(name, server))
|
|
32795
|
-
|
|
32848
|
+
refuse2();
|
|
32796
32849
|
return sha2562;
|
|
32797
32850
|
}
|
|
32798
32851
|
function isBundledCleanupHooks(root, namespace, value) {
|
|
32799
|
-
const
|
|
32852
|
+
const object6 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
32800
32853
|
const exactKeys = (v, keys) => Object.keys(v).length === keys.length && keys.every((key) => Object.hasOwn(v, key));
|
|
32801
32854
|
if (basename6(dirname12(root)) !== namespace || basename6(dirname12(dirname12(root))) !== "openai-bundled")
|
|
32802
32855
|
return false;
|
|
32803
32856
|
const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
|
|
32804
|
-
if (!server || !
|
|
32857
|
+
if (!server || !object6(value) || !exactKeys(value, ["hooks"]) || !object6(value.hooks))
|
|
32805
32858
|
return false;
|
|
32806
32859
|
const events = Object.entries(value.hooks);
|
|
32807
32860
|
if (!events.length || events.length > 3)
|
|
@@ -32810,10 +32863,10 @@ function isBundledCleanupHooks(root, namespace, value) {
|
|
|
32810
32863
|
if (!["Interrupt", "SubagentStop", "Stop"].includes(event) || !Array.isArray(groups) || groups.length !== 1)
|
|
32811
32864
|
return false;
|
|
32812
32865
|
const group = groups[0];
|
|
32813
|
-
if (!
|
|
32866
|
+
if (!object6(group) || !exactKeys(group, ["hooks"]) || !Array.isArray(group.hooks) || group.hooks.length !== 1)
|
|
32814
32867
|
return false;
|
|
32815
32868
|
const handler = group.hooks[0];
|
|
32816
|
-
if (!
|
|
32869
|
+
if (!object6(handler) || !exactKeys(handler, ["type", "server", "tool", "input"]) || handler.type !== "mcp_tool" || handler.server !== server || handler.tool !== "turn_ended" || !object6(handler.input) || !exactKeys(handler.input, ["hook_event_name", "session_id", "turn_id"]) || handler.input.hook_event_name !== "${hook_event_name}" || handler.input.turn_id !== "${turn_id}" || handler.input.session_id !== (event === "SubagentStop" ? "${agent_id}" : "${session_id}"))
|
|
32817
32870
|
return false;
|
|
32818
32871
|
}
|
|
32819
32872
|
return true;
|
|
@@ -32821,35 +32874,35 @@ function isBundledCleanupHooks(root, namespace, value) {
|
|
|
32821
32874
|
function rootControls(root, read) {
|
|
32822
32875
|
const manifestPath = join18(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync12(manifestPath, { throwIfNoEntry: false });
|
|
32823
32876
|
if (!lstatSync12(join18(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
|
|
32824
|
-
|
|
32877
|
+
refuse2();
|
|
32825
32878
|
let manifest, manifestSha256;
|
|
32826
32879
|
try {
|
|
32827
32880
|
const text2 = read(manifestPath);
|
|
32828
32881
|
manifestSha256 = hashNativeJsonControls(text2, "version");
|
|
32829
32882
|
manifest = JSON.parse(text2);
|
|
32830
32883
|
} catch {
|
|
32831
|
-
|
|
32884
|
+
refuse2();
|
|
32832
32885
|
}
|
|
32833
32886
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
|
|
32834
|
-
|
|
32887
|
+
refuse2();
|
|
32835
32888
|
const cacheVersion = basename6(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
32836
32889
|
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
32837
|
-
|
|
32890
|
+
refuse2();
|
|
32838
32891
|
if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync12(join18(root, "hooks"), { throwIfNoEntry: false }))
|
|
32839
|
-
|
|
32892
|
+
refuse2();
|
|
32840
32893
|
const mcp = join18(root, ".mcp.json"), mcpPresent = lstatSync12(mcp, { throwIfNoEntry: false });
|
|
32841
32894
|
if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
|
|
32842
|
-
|
|
32895
|
+
refuse2();
|
|
32843
32896
|
if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync4(mcp) !== mcp))
|
|
32844
|
-
|
|
32897
|
+
refuse2();
|
|
32845
32898
|
if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
|
|
32846
|
-
|
|
32899
|
+
refuse2();
|
|
32847
32900
|
const mcpSha256 = mcpPresent ? mcpControls(mcp, read) : undefined;
|
|
32848
32901
|
const app = join18(root, ".app.json"), present = lstatSync12(app, { throwIfNoEntry: false });
|
|
32849
32902
|
if (present && (!present.isFile() || present.isSymbolicLink() || present.size > 1024 * 1024))
|
|
32850
|
-
|
|
32903
|
+
refuse2();
|
|
32851
32904
|
if (manifest.apps !== undefined && !present)
|
|
32852
|
-
|
|
32905
|
+
refuse2();
|
|
32853
32906
|
const appSha256 = present ? appControls(app, read) : undefined;
|
|
32854
32907
|
return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
|
|
32855
32908
|
}
|
|
@@ -32965,10 +33018,10 @@ function sourceInputUnchanged(input2, cache, read) {
|
|
|
32965
33018
|
}
|
|
32966
33019
|
function reviewedCodexPluginSourceRoots(cache, inputs, read) {
|
|
32967
33020
|
if (!Array.isArray(inputs) || inputs.length > 4096 || new Set(inputs.map((input2) => input2.pluginId)).size !== inputs.length || new Set(inputs.map((input2) => input2.sourceRoot)).size !== inputs.length || inputs.some((input2) => !sourceInputUnchanged(input2, cache, read)))
|
|
32968
|
-
|
|
33021
|
+
refuse2();
|
|
32969
33022
|
const roots = inputs.map((input2) => input2.sourceRoot);
|
|
32970
33023
|
if (roots.some((root) => roots.some((other) => root !== other && root.startsWith(other + sep6))))
|
|
32971
|
-
|
|
33024
|
+
refuse2();
|
|
32972
33025
|
return roots;
|
|
32973
33026
|
}
|
|
32974
33027
|
var identifier2 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v), CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
|
|
@@ -33223,7 +33276,7 @@ function canonical6(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
|
33223
33276
|
need5(isRecord3(value));
|
|
33224
33277
|
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child2]) => [key, canonical6(child2, depth + 1, budget2)])];
|
|
33225
33278
|
}
|
|
33226
|
-
function
|
|
33279
|
+
function normalize3(config2, version2 = 2) {
|
|
33227
33280
|
if (isRecord3(config2.hooks)) {
|
|
33228
33281
|
delete config2.hooks.state;
|
|
33229
33282
|
if (!Object.keys(config2.hooks).length)
|
|
@@ -33394,7 +33447,7 @@ function settingsDigest2(text2, version2 = 2) {
|
|
|
33394
33447
|
need5(isRecord3(config2));
|
|
33395
33448
|
const mcp = version2 >= 3 ? projectRegularCodexMcp(config2, text2) : { config: config2, numericSource: text2 };
|
|
33396
33449
|
const projected = version2 === 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
|
|
33397
|
-
|
|
33450
|
+
normalize3(projected.config, version2);
|
|
33398
33451
|
return sha2(`hasna.skills.codex-settings.v${version2}\x00${JSON.stringify([canonical6(projected.config), numericTokens2(projected.numericSource)])}`);
|
|
33399
33452
|
}
|
|
33400
33453
|
function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
@@ -33545,15 +33598,15 @@ var init_agent_codex = () => {};
|
|
|
33545
33598
|
// src/lib/agent-sumi.ts
|
|
33546
33599
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
33547
33600
|
import { accessSync, constants as constants8, existsSync as existsSync8, lstatSync as lstatSync14, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
|
|
33548
|
-
import { delimiter, isAbsolute as
|
|
33601
|
+
import { delimiter, isAbsolute as isAbsolute11, join as join20, resolve as resolve12 } from "path";
|
|
33549
33602
|
function recordWithKeys(value, keys) {
|
|
33550
33603
|
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
33551
33604
|
}
|
|
33552
|
-
function
|
|
33553
|
-
return typeof value === "string" && value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) &&
|
|
33605
|
+
function absolutePath3(value) {
|
|
33606
|
+
return typeof value === "string" && value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute11(value) && resolve12(value) === value;
|
|
33554
33607
|
}
|
|
33555
33608
|
function absoluteInput(value) {
|
|
33556
|
-
return value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) &&
|
|
33609
|
+
return value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute11(value);
|
|
33557
33610
|
}
|
|
33558
33611
|
function resolveSumiPathsCommand(pathValue) {
|
|
33559
33612
|
if (!pathValue)
|
|
@@ -33578,18 +33631,18 @@ function validPathPlan(value, home, cwd) {
|
|
|
33578
33631
|
if (!recordWithKeys(value.roots, SUMI_PATH_KEYS) || !recordWithKeys(value.legacyRoots, SUMI_PATH_KEYS))
|
|
33579
33632
|
return false;
|
|
33580
33633
|
for (const key of SUMI_PATH_KEYS) {
|
|
33581
|
-
if (!
|
|
33634
|
+
if (!absolutePath3(value.roots[key]))
|
|
33582
33635
|
return false;
|
|
33583
|
-
if (value.legacyRoots[key] !== null && !
|
|
33636
|
+
if (value.legacyRoots[key] !== null && !absolutePath3(value.legacyRoots[key]))
|
|
33584
33637
|
return false;
|
|
33585
33638
|
}
|
|
33586
33639
|
if (!recordWithKeys(value.configFiles, ["canonical", "legacy"]) || !recordWithKeys(value.skillRoots, ["canonical", "legacy"]))
|
|
33587
33640
|
return false;
|
|
33588
|
-
if (!
|
|
33641
|
+
if (!absolutePath3(value.configFiles.canonical) || !absolutePath3(value.skillRoots.canonical))
|
|
33589
33642
|
return false;
|
|
33590
|
-
if (value.configFiles.legacy !== null && !
|
|
33643
|
+
if (value.configFiles.legacy !== null && !absolutePath3(value.configFiles.legacy))
|
|
33591
33644
|
return false;
|
|
33592
|
-
if (value.skillRoots.legacy !== null && !
|
|
33645
|
+
if (value.skillRoots.legacy !== null && !absolutePath3(value.skillRoots.legacy))
|
|
33593
33646
|
return false;
|
|
33594
33647
|
const configRoot = value.roots.config;
|
|
33595
33648
|
const legacyConfigRoot = value.legacyRoots.config;
|
|
@@ -33609,7 +33662,7 @@ function sumiPathPlan(home, cwd = process.cwd(), env = process.env) {
|
|
|
33609
33662
|
throw new SumiPathResolverError("SUMI_PATH_INPUT_REFUSED");
|
|
33610
33663
|
const runtimePath = process.env.PATH ?? "";
|
|
33611
33664
|
const executable = resolveSumiPathsCommand(runtimePath);
|
|
33612
|
-
if (!executable || !
|
|
33665
|
+
if (!executable || !isAbsolute11(executable))
|
|
33613
33666
|
throw new SumiPathResolverError("SUMI_PATH_RESOLVER_UNAVAILABLE");
|
|
33614
33667
|
const childEnv = { PATH: runtimePath };
|
|
33615
33668
|
for (const key of SUMI_PATH_SELECTORS)
|
|
@@ -33685,7 +33738,7 @@ var init_agent_sumi = __esm(() => {
|
|
|
33685
33738
|
// src/lib/codex-hook-trust-files.ts
|
|
33686
33739
|
import { closeSync as closeSync11, constants as constants9, fstatSync as fstatSync11, fsyncSync as fsyncSync2, lstatSync as lstatSync15, openSync as openSync11, readlinkSync as readlinkSync4, readSync as readSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
33687
33740
|
import { createHash as createHash15 } from "crypto";
|
|
33688
|
-
import { dirname as dirname13, isAbsolute as
|
|
33741
|
+
import { dirname as dirname13, isAbsolute as isAbsolute12, resolve as resolve13 } from "path";
|
|
33689
33742
|
function need6(value, code) {
|
|
33690
33743
|
if (!value)
|
|
33691
33744
|
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
@@ -33785,6 +33838,96 @@ var init_runtime_launcher = __esm(() => {
|
|
|
33785
33838
|
PINNED_LAUNCHER_MAX_BYTES = 16 * 1024;
|
|
33786
33839
|
});
|
|
33787
33840
|
|
|
33841
|
+
// src/lib/retained-review-artifacts.ts
|
|
33842
|
+
import { createHash as createHash16 } from "crypto";
|
|
33843
|
+
import { constants as constants10, closeSync as closeSync12, fstatSync as fstatSync12, fsyncSync as fsyncSync3, lstatSync as lstatSync16, mkdirSync as mkdirSync5, openSync as openSync12, readSync as readSync7, writeSync as writeSync2 } from "fs";
|
|
33844
|
+
import { dirname as dirname14, isAbsolute as isAbsolute13, join as join21, resolve as resolve14, sep as sep7 } from "path";
|
|
33845
|
+
function keys(value, names) {
|
|
33846
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === names.length && names.every((name) => Object.hasOwn(value, name)));
|
|
33847
|
+
}
|
|
33848
|
+
function safe3(path) {
|
|
33849
|
+
need7(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute13(path) && resolve14(path) === path && !/[\x00-\x1f\x7f]/.test(path));
|
|
33850
|
+
for (let cursor = path;; cursor = dirname14(cursor)) {
|
|
33851
|
+
const stat = lstatSync16(cursor, { throwIfNoEntry: false });
|
|
33852
|
+
need7(!stat?.isSymbolicLink());
|
|
33853
|
+
if (cursor !== path && stat)
|
|
33854
|
+
need7(stat.isDirectory());
|
|
33855
|
+
if (dirname14(cursor) === cursor)
|
|
33856
|
+
break;
|
|
33857
|
+
}
|
|
33858
|
+
}
|
|
33859
|
+
function readExact(path) {
|
|
33860
|
+
safe3(path);
|
|
33861
|
+
const before = lstatSync16(path, { throwIfNoEntry: false });
|
|
33862
|
+
need7(before?.isFile() && before.nlink === 1 && before.size <= AGENT_POLICY_LIMITS.discoveryRawSourceBytes && (before.mode & 63) === 0 && (process.getuid === undefined || before.uid === process.getuid()));
|
|
33863
|
+
const fd = openSync12(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
|
|
33864
|
+
try {
|
|
33865
|
+
const opened = fstatSync12(fd);
|
|
33866
|
+
need7(opened.dev === before.dev && opened.ino === before.ino && opened.size === before.size && opened.mode === before.mode && opened.uid === before.uid && opened.nlink === 1);
|
|
33867
|
+
const bytes = Buffer.alloc(opened.size);
|
|
33868
|
+
let offset = 0;
|
|
33869
|
+
while (offset < bytes.length) {
|
|
33870
|
+
const count = readSync7(fd, bytes, offset, bytes.length - offset, null);
|
|
33871
|
+
need7(count > 0);
|
|
33872
|
+
offset += count;
|
|
33873
|
+
}
|
|
33874
|
+
need7(readSync7(fd, Buffer.alloc(1), 0, 1, null) === 0);
|
|
33875
|
+
const after = fstatSync12(fd), current = lstatSync16(path, { throwIfNoEntry: false });
|
|
33876
|
+
safe3(path);
|
|
33877
|
+
need7(current && opened.dev === current.dev && opened.ino === current.ino && opened.mode === current.mode && opened.uid === current.uid && opened.size === current.size && opened.mtimeMs === current.mtimeMs && opened.ctimeMs === current.ctimeMs && current.nlink === 1 && opened.mode === after.mode && opened.uid === after.uid && after.nlink === 1 && opened.size === after.size && opened.mtimeMs === after.mtimeMs && opened.ctimeMs === after.ctimeMs);
|
|
33878
|
+
const text2 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
33879
|
+
need7(Buffer.from(text2).equals(bytes));
|
|
33880
|
+
return text2;
|
|
33881
|
+
} finally {
|
|
33882
|
+
closeSync12(fd);
|
|
33883
|
+
}
|
|
33884
|
+
}
|
|
33885
|
+
function catalogDigest(text2) {
|
|
33886
|
+
let catalog;
|
|
33887
|
+
try {
|
|
33888
|
+
catalog = JSON.parse(text2);
|
|
33889
|
+
} catch {
|
|
33890
|
+
return refuse3();
|
|
33891
|
+
}
|
|
33892
|
+
need7(catalog && typeof catalog === "object" && !Array.isArray(catalog) && Object.keys(catalog).every((key) => ["version", "cwd", "skills", "plugins"].includes(key)) && typeof catalog.version === "string" && supportsCodexNativeCapability(catalog.version, "installed-plugin-review"));
|
|
33893
|
+
const skills = projectCodexNativeSkillCatalog({ data: [{ cwd: catalog.cwd, errors: [], skills: catalog.skills }] }, catalog.cwd);
|
|
33894
|
+
const plugins = catalog.plugins === undefined ? undefined : projectCodexInstalledPluginEntries(catalog.plugins);
|
|
33895
|
+
return sha3(JSON.stringify({ version: catalog.version, cwd: catalog.cwd, skills, ...plugins === undefined ? {} : { plugins } }));
|
|
33896
|
+
}
|
|
33897
|
+
function retainedArtifactPath(metadata) {
|
|
33898
|
+
return join21(metadata.storeRoot, "agent-discovery", "artifacts", metadata.sha256, "catalog.json");
|
|
33899
|
+
}
|
|
33900
|
+
function validRetainedArtifact(value) {
|
|
33901
|
+
return keys(value, ["version", "kind", "storeRoot", "originalPath", "sha256", "catalogSha256"]) && value.version === 1 && value.kind === "codex-native-catalog" && hex3(value.sha256) && hex3(value.catalogSha256) && [value.storeRoot, value.originalPath].every((path) => typeof path === "string" && isAbsolute13(path) && resolve14(path) === path && path.length <= AGENT_POLICY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(path));
|
|
33902
|
+
}
|
|
33903
|
+
function privateStore(storeRoot) {
|
|
33904
|
+
safe3(storeRoot);
|
|
33905
|
+
const stat = lstatSync16(storeRoot, { throwIfNoEntry: false });
|
|
33906
|
+
need7(!stat || stat.isDirectory() && (stat.mode & 18) === 0 && (process.getuid === undefined || stat.uid === process.getuid()));
|
|
33907
|
+
}
|
|
33908
|
+
function verifyRetainedReviewArtifact(source, storeRoot) {
|
|
33909
|
+
const artifact = source.reviewArtifact;
|
|
33910
|
+
need7(validRetainedArtifact(artifact) && source.hashMode === "bytes" && source.sha256 === artifact.sha256 && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.path === retainedArtifactPath(artifact) && (storeRoot === undefined || artifact.storeRoot === resolve14(storeRoot)));
|
|
33911
|
+
privateStore(artifact.storeRoot);
|
|
33912
|
+
const text2 = readExact(source.path);
|
|
33913
|
+
need7(sha3(text2) === artifact.sha256 && catalogDigest(text2) === artifact.catalogSha256);
|
|
33914
|
+
for (let parent = dirname14(source.path);parent !== artifact.storeRoot; parent = dirname14(parent)) {
|
|
33915
|
+
const stat = lstatSync16(parent);
|
|
33916
|
+
need7(stat.isDirectory() && (stat.mode & 63) === 0 && (process.getuid === undefined || stat.uid === process.getuid()));
|
|
33917
|
+
}
|
|
33918
|
+
}
|
|
33919
|
+
var sha3 = (bytes) => createHash16("sha256").update(bytes).digest("hex"), hex3 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), refuse3 = () => {
|
|
33920
|
+
throw new Error("REVIEW_ARTIFACT_INVALID");
|
|
33921
|
+
}, need7 = (condition) => {
|
|
33922
|
+
if (!condition)
|
|
33923
|
+
refuse3();
|
|
33924
|
+
};
|
|
33925
|
+
var init_retained_review_artifacts = __esm(() => {
|
|
33926
|
+
init_agent_policy_limits();
|
|
33927
|
+
init_codex_native_skill_catalog();
|
|
33928
|
+
init_codex_native_compatibility();
|
|
33929
|
+
});
|
|
33930
|
+
|
|
33788
33931
|
// src/lib/agent-integration.ts
|
|
33789
33932
|
var init_agent_integration = __esm(() => {
|
|
33790
33933
|
init_sumi_settings_witness();
|
|
@@ -33806,6 +33949,7 @@ var init_agent_integration = __esm(() => {
|
|
|
33806
33949
|
init_claude_settings_witness();
|
|
33807
33950
|
init_codex_hook_trust_files();
|
|
33808
33951
|
init_runtime_launcher();
|
|
33952
|
+
init_retained_review_artifacts();
|
|
33809
33953
|
});
|
|
33810
33954
|
|
|
33811
33955
|
// src/lib/home-migration.ts
|
|
@@ -33842,13 +33986,13 @@ var init_skill_aliases = __esm(() => {
|
|
|
33842
33986
|
});
|
|
33843
33987
|
|
|
33844
33988
|
// src/lib/project-state.ts
|
|
33845
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
33846
|
-
import { join as
|
|
33989
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync5 } from "fs";
|
|
33990
|
+
import { join as join22 } from "path";
|
|
33847
33991
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
33848
|
-
return
|
|
33992
|
+
return join22(targetDir, SKILLS_PROJECT_DIR);
|
|
33849
33993
|
}
|
|
33850
33994
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
33851
|
-
return
|
|
33995
|
+
return join22(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
33852
33996
|
}
|
|
33853
33997
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
33854
33998
|
const path = getProjectConfigPath(targetDir);
|
|
@@ -33876,7 +34020,7 @@ function ensureProjectConfig(targetDir = process.cwd()) {
|
|
|
33876
34020
|
}
|
|
33877
34021
|
function saveProjectConfig(config2, targetDir = process.cwd()) {
|
|
33878
34022
|
const dir = getProjectStateDir(targetDir);
|
|
33879
|
-
|
|
34023
|
+
mkdirSync6(dir, { recursive: true });
|
|
33880
34024
|
const normalized = normalizeProjectConfig({ ...config2, updatedAt: new Date().toISOString() });
|
|
33881
34025
|
writeFileSync5(getProjectConfigPath(targetDir), JSON.stringify(normalized, null, 2) + `
|
|
33882
34026
|
`);
|
|
@@ -33964,7 +34108,7 @@ var init_project_state = __esm(() => {
|
|
|
33964
34108
|
|
|
33965
34109
|
// src/lib/installer.ts
|
|
33966
34110
|
import { existsSync as existsSync10, readFileSync as readFileSync14, rmSync } from "fs";
|
|
33967
|
-
import { join as
|
|
34111
|
+
import { join as join23 } from "path";
|
|
33968
34112
|
import { homedir as homedir2 } from "os";
|
|
33969
34113
|
function getSkillPath(name) {
|
|
33970
34114
|
const skillName = normalizeSkillName(getCanonicalSkillName(name));
|
|
@@ -33974,7 +34118,7 @@ function getSkillPath(name) {
|
|
|
33974
34118
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
33975
34119
|
if (extensionPath)
|
|
33976
34120
|
return extensionPath;
|
|
33977
|
-
return
|
|
34121
|
+
return join23(getPortableSkillsRoot(), skillName);
|
|
33978
34122
|
}
|
|
33979
34123
|
function getCanonicalSkillName(name) {
|
|
33980
34124
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
@@ -34028,11 +34172,11 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
34028
34172
|
const base = projectDir || process.cwd();
|
|
34029
34173
|
switch (agent) {
|
|
34030
34174
|
case "pi":
|
|
34031
|
-
return scope === "project" ?
|
|
34175
|
+
return scope === "project" ? join23(base, ".pi", "skills") : join23(homedir2(), ".pi", "agent", "skills");
|
|
34032
34176
|
case "opencode":
|
|
34033
|
-
return scope === "project" ?
|
|
34177
|
+
return scope === "project" ? join23(base, ".opencode", "skills") : join23(homedir2(), ".config", "opencode", "skills");
|
|
34034
34178
|
default:
|
|
34035
|
-
return scope === "project" ?
|
|
34179
|
+
return scope === "project" ? join23(base, `.${agent}`, "skills") : join23(homedir2(), `.${agent}`, "skills");
|
|
34036
34180
|
}
|
|
34037
34181
|
}
|
|
34038
34182
|
function warnMissingDependencies(name, targetDir) {
|
|
@@ -34047,7 +34191,7 @@ function warnMissingDependencies(name, targetDir) {
|
|
|
34047
34191
|
}
|
|
34048
34192
|
}
|
|
34049
34193
|
function readBundledSkillVersion(name) {
|
|
34050
|
-
const pkgPath =
|
|
34194
|
+
const pkgPath = join23(getSkillPath(name), "package.json");
|
|
34051
34195
|
if (!existsSync10(pkgPath))
|
|
34052
34196
|
return "unknown";
|
|
34053
34197
|
try {
|
|
@@ -34117,11 +34261,11 @@ var init_skill_doc_file = __esm(() => {
|
|
|
34117
34261
|
|
|
34118
34262
|
// src/lib/skillinfo.ts
|
|
34119
34263
|
import { existsSync as existsSync11, readFileSync as readFileSync15 } from "fs";
|
|
34120
|
-
import { join as
|
|
34264
|
+
import { join as join24 } from "path";
|
|
34121
34265
|
function isInstructionSkillDir(skillPath, meta3) {
|
|
34122
34266
|
if (meta3?.kind === "instruction")
|
|
34123
34267
|
return true;
|
|
34124
|
-
const skillMdPath =
|
|
34268
|
+
const skillMdPath = join24(skillPath, "SKILL.md");
|
|
34125
34269
|
if (!existsSync11(skillMdPath))
|
|
34126
34270
|
return false;
|
|
34127
34271
|
try {
|
|
@@ -34135,9 +34279,9 @@ function getSkillDocs(name) {
|
|
|
34135
34279
|
if (!existsSync11(skillPath))
|
|
34136
34280
|
return null;
|
|
34137
34281
|
return {
|
|
34138
|
-
skillMd: readIfExists(
|
|
34139
|
-
readme: readIfExists(
|
|
34140
|
-
claudeMd: readIfExists(
|
|
34282
|
+
skillMd: readIfExists(join24(skillPath, "SKILL.md")),
|
|
34283
|
+
readme: readIfExists(join24(skillPath, "README.md")),
|
|
34284
|
+
claudeMd: readIfExists(join24(skillPath, "CLAUDE.md"))
|
|
34141
34285
|
};
|
|
34142
34286
|
}
|
|
34143
34287
|
function getSkillBestDoc(name, file2) {
|
|
@@ -34153,14 +34297,14 @@ function getSkillRequirements(name) {
|
|
|
34153
34297
|
return null;
|
|
34154
34298
|
const texts = [];
|
|
34155
34299
|
for (const file2 of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
34156
|
-
const content = readIfExists(
|
|
34300
|
+
const content = readIfExists(join24(skillPath, file2));
|
|
34157
34301
|
if (content)
|
|
34158
34302
|
texts.push(content);
|
|
34159
34303
|
}
|
|
34160
34304
|
const meta3 = getSkill(name);
|
|
34161
34305
|
let dependencies = {};
|
|
34162
34306
|
try {
|
|
34163
|
-
dependencies = JSON.parse(readFileSync15(
|
|
34307
|
+
dependencies = JSON.parse(readFileSync15(join24(skillPath, "package.json"), "utf8")).dependencies || {};
|
|
34164
34308
|
} catch {}
|
|
34165
34309
|
return getSkillRequirementsFromContent(name, texts, dependencies, meta3);
|
|
34166
34310
|
}
|
|
@@ -34222,7 +34366,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
34222
34366
|
error: `Skill '${name}' is an instruction skill (kind: instruction) and is not runnable. Instruction skills are consumed by coding agents via SKILL.md, not executed with 'skills run'.`
|
|
34223
34367
|
};
|
|
34224
34368
|
}
|
|
34225
|
-
const pkgPath =
|
|
34369
|
+
const pkgPath = join24(skillPath, "package.json");
|
|
34226
34370
|
if (!existsSync11(pkgPath)) {
|
|
34227
34371
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
34228
34372
|
}
|
|
@@ -34242,12 +34386,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
34242
34386
|
} catch {
|
|
34243
34387
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
34244
34388
|
}
|
|
34245
|
-
const entryPath =
|
|
34389
|
+
const entryPath = join24(skillPath, entryPoint2);
|
|
34246
34390
|
if (!existsSync11(entryPath)) {
|
|
34247
34391
|
return { exitCode: 1, error: `Entry point '${entryPoint2}' not found in skill '${name}'` };
|
|
34248
34392
|
}
|
|
34249
34393
|
const env = { ...process.env, ...options.env };
|
|
34250
|
-
if (!existsSync11(
|
|
34394
|
+
if (!existsSync11(join24(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
|
|
34251
34395
|
const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
|
|
34252
34396
|
if (failure)
|
|
34253
34397
|
return failure;
|
|
@@ -34273,7 +34417,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
34273
34417
|
function detectProjectSkills(cwd = process.cwd()) {
|
|
34274
34418
|
let pkg;
|
|
34275
34419
|
try {
|
|
34276
|
-
pkg = JSON.parse(readFileSync15(
|
|
34420
|
+
pkg = JSON.parse(readFileSync15(join24(cwd, "package.json"), "utf8"));
|
|
34277
34421
|
} catch {
|
|
34278
34422
|
return { detected: [], recommended: [] };
|
|
34279
34423
|
}
|
|
@@ -34478,7 +34622,7 @@ var init_selection_resolver = __esm(() => {
|
|
|
34478
34622
|
});
|
|
34479
34623
|
|
|
34480
34624
|
// src/lib/plugin-projection.ts
|
|
34481
|
-
import { createHash as
|
|
34625
|
+
import { createHash as createHash17 } from "crypto";
|
|
34482
34626
|
function pluginRefusal(message) {
|
|
34483
34627
|
throw new SkillSelectionError("PLUGIN_ADMISSION_REFUSED", message);
|
|
34484
34628
|
}
|
|
@@ -34489,8 +34633,8 @@ function pluginNeed(value, message) {
|
|
|
34489
34633
|
function pluginObject(value) {
|
|
34490
34634
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
34491
34635
|
}
|
|
34492
|
-
function pluginKeys(value,
|
|
34493
|
-
pluginNeed(pluginObject(value) && Object.keys(value).every((key) =>
|
|
34636
|
+
function pluginKeys(value, keys2) {
|
|
34637
|
+
pluginNeed(pluginObject(value) && Object.keys(value).every((key) => keys2.includes(key)), "Unsupported plugin contract fields");
|
|
34494
34638
|
}
|
|
34495
34639
|
function pluginText(value, limit = 4096) {
|
|
34496
34640
|
pluginNeed(typeof value === "string" && value.length > 0 && value.length <= limit && !/[\x00-\x1f\x7f]/.test(value), "Invalid plugin contract text");
|
|
@@ -34556,18 +34700,18 @@ function pluginFileWitnesses(entries) {
|
|
|
34556
34700
|
return { path: item.path, mode: item.mode, size: item.bytes.byteLength, sha256: pluginHash(item.bytes) };
|
|
34557
34701
|
});
|
|
34558
34702
|
}
|
|
34559
|
-
var PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) =>
|
|
34703
|
+
var PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) => createHash17("sha256").update(bytes).digest("hex");
|
|
34560
34704
|
var init_plugin_projection = __esm(() => {
|
|
34561
34705
|
init_selection_cache();
|
|
34562
34706
|
PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
34563
34707
|
});
|
|
34564
34708
|
|
|
34565
34709
|
// src/lib/plugin-projection-store.ts
|
|
34566
|
-
import { constants as
|
|
34567
|
-
import { dirname as
|
|
34710
|
+
import { constants as constants11, closeSync as closeSync13, fchmodSync, fstatSync as fstatSync13, fsyncSync as fsyncSync4, linkSync as linkSync2, lstatSync as lstatSync17, mkdirSync as mkdirSync7, openSync as openSync13, opendirSync as opendirSync2, readSync as readSync8, renameSync as renameSync2, rmdirSync as rmdirSync2, rmSync as rmSync2, unlinkSync as unlinkSync3, writeSync as writeSync3 } from "fs";
|
|
34711
|
+
import { dirname as dirname15, isAbsolute as isAbsolute14, join as join25, parse as parse10, resolve as resolve15 } from "path";
|
|
34568
34712
|
function stat(path) {
|
|
34569
34713
|
try {
|
|
34570
|
-
return
|
|
34714
|
+
return lstatSync17(path, { bigint: true });
|
|
34571
34715
|
} catch (error61) {
|
|
34572
34716
|
if (missing(error61))
|
|
34573
34717
|
return null;
|
|
@@ -34575,7 +34719,7 @@ function stat(path) {
|
|
|
34575
34719
|
}
|
|
34576
34720
|
}
|
|
34577
34721
|
function canonical7(path) {
|
|
34578
|
-
pluginNeed(typeof path === "string" && path.length <= 8192 &&
|
|
34722
|
+
pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute14(path) && resolve15(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
|
|
34579
34723
|
}
|
|
34580
34724
|
function owner(stats, system = false) {
|
|
34581
34725
|
const current = uid();
|
|
@@ -34586,15 +34730,15 @@ function same3(left, right, identityOnly = false) {
|
|
|
34586
34730
|
}
|
|
34587
34731
|
function ancestors(path, create = false) {
|
|
34588
34732
|
canonical7(path);
|
|
34589
|
-
const result = [], parent =
|
|
34733
|
+
const result = [], parent = dirname15(path), filesystemRoot = parse10(parent).root;
|
|
34590
34734
|
let current = filesystemRoot;
|
|
34591
34735
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
34592
34736
|
if (segment)
|
|
34593
|
-
current =
|
|
34737
|
+
current = join25(current, segment);
|
|
34594
34738
|
let currentStat = stat(current);
|
|
34595
34739
|
if (!currentStat && create) {
|
|
34596
34740
|
try {
|
|
34597
|
-
|
|
34741
|
+
mkdirSync7(current, { mode: 448 });
|
|
34598
34742
|
} catch (error61) {
|
|
34599
34743
|
if (!(error61 && typeof error61 === "object" && ("code" in error61) && error61.code === "EEXIST"))
|
|
34600
34744
|
pluginRefusal("Cannot create private plugin storage directory");
|
|
@@ -34636,19 +34780,19 @@ function readBytes(path, maximum, kind) {
|
|
|
34636
34780
|
fileShape(before, maximum, kind);
|
|
34637
34781
|
let fd;
|
|
34638
34782
|
try {
|
|
34639
|
-
fd =
|
|
34640
|
-
const opened =
|
|
34783
|
+
fd = openSync13(path, READ_FLAGS);
|
|
34784
|
+
const opened = fstatSync13(fd, { bigint: true });
|
|
34641
34785
|
same3(before, opened);
|
|
34642
34786
|
fileShape(opened, maximum, kind);
|
|
34643
34787
|
const bytes = new Uint8Array(Number(opened.size));
|
|
34644
34788
|
let offset = 0;
|
|
34645
34789
|
while (offset < bytes.byteLength) {
|
|
34646
|
-
const count =
|
|
34790
|
+
const count = readSync8(fd, bytes, offset, bytes.byteLength - offset, null);
|
|
34647
34791
|
pluginNeed(count > 0, "Plugin file changed while being read");
|
|
34648
34792
|
offset += count;
|
|
34649
34793
|
}
|
|
34650
|
-
pluginNeed(
|
|
34651
|
-
same3(opened,
|
|
34794
|
+
pluginNeed(readSync8(fd, new Uint8Array(1), 0, 1, null) === 0, "Plugin file grew while being read");
|
|
34795
|
+
same3(opened, fstatSync13(fd, { bigint: true }));
|
|
34652
34796
|
same3(opened, stat(path));
|
|
34653
34797
|
verifyAncestors(chain);
|
|
34654
34798
|
return bytes;
|
|
@@ -34658,7 +34802,7 @@ function readBytes(path, maximum, kind) {
|
|
|
34658
34802
|
return pluginRefusal("Cannot safely read plugin file");
|
|
34659
34803
|
} finally {
|
|
34660
34804
|
if (fd !== undefined)
|
|
34661
|
-
|
|
34805
|
+
closeSync13(fd);
|
|
34662
34806
|
}
|
|
34663
34807
|
}
|
|
34664
34808
|
function exactPaths(entries) {
|
|
@@ -34698,7 +34842,7 @@ function snapshotPluginTree(root, options = {}) {
|
|
|
34698
34842
|
pluginNeed(++visited <= PLUGIN_PROJECTION_LIMITS.files * 50, "Plugin tree membership exceeds its limit");
|
|
34699
34843
|
const path = relative6 ? `${relative6}/${name}` : name;
|
|
34700
34844
|
pluginNeed(new TextEncoder().encode(path).byteLength <= 100 && !/[\\:\x00-\x1f\x7f]/u.test(path), "Plugin entry path exceeds its limit or is unsafe");
|
|
34701
|
-
const absolute =
|
|
34845
|
+
const absolute = join25(directory, name), entryStat = stat(absolute);
|
|
34702
34846
|
pluginNeed(entryStat, "Plugin tree member disappeared");
|
|
34703
34847
|
if (relative6 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
|
|
34704
34848
|
const bytes2 = readBytes(absolute, 32, "native-pin");
|
|
@@ -34715,7 +34859,7 @@ function snapshotPluginTree(root, options = {}) {
|
|
|
34715
34859
|
directories.push({ path: absolute, stat: entryStat, names: pins });
|
|
34716
34860
|
for (const pin of pins) {
|
|
34717
34861
|
pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin) && Number(pin) <= 2147483647, "Unrecognized native plugin pin name");
|
|
34718
|
-
const pinPath =
|
|
34862
|
+
const pinPath = join25(absolute, pin), pinStat = stat(pinPath);
|
|
34719
34863
|
pluginNeed(pinStat, "Native plugin pin disappeared");
|
|
34720
34864
|
const bytes2 = readBytes(pinPath, 4096, "native-pin");
|
|
34721
34865
|
let value;
|
|
@@ -34781,8 +34925,8 @@ function readPluginJson(path) {
|
|
|
34781
34925
|
var READ_FLAGS, WRITE_FLAGS, uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined, missing = (error61) => Boolean(error61 && typeof error61 === "object" && "code" in error61 && error61.code === "ENOENT");
|
|
34782
34926
|
var init_plugin_projection_store = __esm(() => {
|
|
34783
34927
|
init_plugin_projection();
|
|
34784
|
-
READ_FLAGS =
|
|
34785
|
-
WRITE_FLAGS =
|
|
34928
|
+
READ_FLAGS = constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK;
|
|
34929
|
+
WRITE_FLAGS = constants11.O_WRONLY | constants11.O_CREAT | constants11.O_EXCL | constants11.O_NOFOLLOW | constants11.O_NONBLOCK;
|
|
34786
34930
|
});
|
|
34787
34931
|
|
|
34788
34932
|
// src/lib/selected-manifest.ts
|
|
@@ -34812,7 +34956,7 @@ var init_selected_manifest = __esm(() => {
|
|
|
34812
34956
|
});
|
|
34813
34957
|
|
|
34814
34958
|
// src/lib/plugin-admission.ts
|
|
34815
|
-
import { isAbsolute as
|
|
34959
|
+
import { isAbsolute as isAbsolute15, join as join26, resolve as resolve16 } from "path";
|
|
34816
34960
|
function validatePluginTarget(value) {
|
|
34817
34961
|
pluginKeys(value, ["schemaVersion", "pluginId", "registrations", "native", "resolver"]);
|
|
34818
34962
|
pluginNeed(value.schemaVersion === 1, "Invalid plugin admission target");
|
|
@@ -34822,7 +34966,7 @@ function validatePluginTarget(value) {
|
|
|
34822
34966
|
for (const registration of value.registrations) {
|
|
34823
34967
|
pluginKeys(registration, ["scope", "projectPath"]);
|
|
34824
34968
|
pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
|
|
34825
|
-
pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" &&
|
|
34969
|
+
pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute15(registration.projectPath) && resolve16(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
|
|
34826
34970
|
const key = JSON.stringify([registration.scope, registration.projectPath]);
|
|
34827
34971
|
pluginNeed(!scopes.has(key), "Duplicate plugin registration scope");
|
|
34828
34972
|
scopes.add(key);
|
|
@@ -34830,7 +34974,7 @@ function validatePluginTarget(value) {
|
|
|
34830
34974
|
for (const key of ["native", "resolver"]) {
|
|
34831
34975
|
pluginKeys(value[key], key === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
|
|
34832
34976
|
pluginText(value[key].executable);
|
|
34833
|
-
pluginNeed(
|
|
34977
|
+
pluginNeed(isAbsolute15(value[key].executable) && resolve16(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
|
|
34834
34978
|
pluginDigest(value[key].digest);
|
|
34835
34979
|
}
|
|
34836
34980
|
pluginNeed(["2.1.274", "2.1.276"].includes(value.native.version), "Plugin admission requires a certified Claude command-source runtime (2.1.274 or 2.1.276)");
|
|
@@ -34973,12 +35117,12 @@ function pluginResolverCommand(binding) {
|
|
|
34973
35117
|
}
|
|
34974
35118
|
function pluginBindingPath(storeRoot, id) {
|
|
34975
35119
|
pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
|
|
34976
|
-
return
|
|
35120
|
+
return join26(storeRoot, "bindings", `${id}.json`);
|
|
34977
35121
|
}
|
|
34978
35122
|
function pluginReceiptPath(storeRoot, id, digest) {
|
|
34979
35123
|
pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
|
|
34980
35124
|
pluginDigest(digest);
|
|
34981
|
-
return
|
|
35125
|
+
return join26(storeRoot, "receipts", id, `${digest.slice(7)}.json`);
|
|
34982
35126
|
}
|
|
34983
35127
|
function readPluginBinding(storeRoot, id) {
|
|
34984
35128
|
const value = readPluginJson(pluginBindingPath(storeRoot, id));
|
|
@@ -34998,7 +35142,7 @@ function readPluginAdmissionReceipt(storeRoot, id, digest) {
|
|
|
34998
35142
|
pluginNeed(receipt.plan.bindingId === id && receipt.plan.planDigest === digest, "Plugin receipt identity differs from its filename");
|
|
34999
35143
|
const binding = readPluginBinding(storeRoot, id);
|
|
35000
35144
|
pluginNeed(canonicalJson(binding) === canonicalJson(receipt.plan.binding) && receipt.plan.sourceCommand === pluginResolverCommand(binding), "Plugin receipt binding changed");
|
|
35001
|
-
pluginNeed(receipt.materializedPath ===
|
|
35145
|
+
pluginNeed(receipt.materializedPath === join26(storeRoot, "objects", id, digest.slice(7)), "Plugin receipt points outside its immutable materialization");
|
|
35002
35146
|
pluginText(receipt.admittedAt, 64);
|
|
35003
35147
|
pluginNeed(Number.isFinite(Date.parse(receipt.admittedAt)), "Invalid plugin admission time");
|
|
35004
35148
|
return receipt;
|
|
@@ -35015,8 +35159,8 @@ var init_plugin_admission = __esm(() => {
|
|
|
35015
35159
|
});
|
|
35016
35160
|
|
|
35017
35161
|
// src/lib/plugin-discovery.ts
|
|
35018
|
-
import { closeSync as
|
|
35019
|
-
import { basename as basename7, dirname as
|
|
35162
|
+
import { closeSync as closeSync14, constants as constants12, fstatSync as fstatSync14, lstatSync as lstatSync18, openSync as openSync14, opendirSync as opendirSync3, readSync as readSync9 } from "fs";
|
|
35163
|
+
import { basename as basename7, dirname as dirname16, isAbsolute as isAbsolute16, join as join27, resolve as resolve17 } from "path";
|
|
35020
35164
|
function validateManagedPluginWitnesses(value) {
|
|
35021
35165
|
pluginNeed(Array.isArray(value) && value.length > 0 && value.length <= PLUGIN_PROJECTION_LIMITS.registrations, "Invalid managed plugin witness collection");
|
|
35022
35166
|
const seen = new Set;
|
|
@@ -35025,36 +35169,36 @@ function validateManagedPluginWitnesses(value) {
|
|
|
35025
35169
|
pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
|
|
35026
35170
|
seen.add(item.bindingId);
|
|
35027
35171
|
pluginText(item.storeRoot);
|
|
35028
|
-
pluginNeed(
|
|
35172
|
+
pluginNeed(isAbsolute16(item.storeRoot) && resolve17(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
|
|
35029
35173
|
}
|
|
35030
35174
|
}
|
|
35031
|
-
function
|
|
35175
|
+
function safe4(path) {
|
|
35032
35176
|
pluginText(path);
|
|
35033
|
-
pluginNeed(
|
|
35034
|
-
for (let at = path;; at =
|
|
35035
|
-
pluginNeed(!
|
|
35036
|
-
if (at ===
|
|
35177
|
+
pluginNeed(isAbsolute16(path) && resolve17(path) === path, "Invalid plugin registry path");
|
|
35178
|
+
for (let at = path;; at = dirname16(at)) {
|
|
35179
|
+
pluginNeed(!lstatSync18(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
|
|
35180
|
+
if (at === dirname16(at))
|
|
35037
35181
|
break;
|
|
35038
35182
|
}
|
|
35039
35183
|
}
|
|
35040
35184
|
function readRegistry(path) {
|
|
35041
|
-
|
|
35042
|
-
const initial =
|
|
35185
|
+
safe4(path);
|
|
35186
|
+
const initial = lstatSync18(path);
|
|
35043
35187
|
pluginNeed(initial.isFile() && initial.size <= 1024 * 1024, "Plugin registry is not a bounded regular file");
|
|
35044
|
-
const fd =
|
|
35188
|
+
const fd = openSync14(path, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
|
|
35045
35189
|
try {
|
|
35046
|
-
const opened =
|
|
35190
|
+
const opened = fstatSync14(fd);
|
|
35047
35191
|
pluginNeed(opened.isFile() && opened.ino === initial.ino && opened.dev === initial.dev, "Plugin registry changed during open");
|
|
35048
35192
|
const bytes = Buffer.alloc(1024 * 1024 + 1);
|
|
35049
35193
|
let length = 0;
|
|
35050
35194
|
while (length < bytes.length) {
|
|
35051
|
-
const count =
|
|
35195
|
+
const count = readSync9(fd, bytes, length, bytes.length - length, null);
|
|
35052
35196
|
if (!count)
|
|
35053
35197
|
break;
|
|
35054
35198
|
length += count;
|
|
35055
35199
|
}
|
|
35056
|
-
const after =
|
|
35057
|
-
|
|
35200
|
+
const after = fstatSync14(fd), current = lstatSync18(path);
|
|
35201
|
+
safe4(path);
|
|
35058
35202
|
pluginNeed(length <= 1024 * 1024 && length === opened.size && after.size === opened.size && after.mtimeMs === opened.mtimeMs && after.ctimeMs === opened.ctimeMs && current.dev === opened.dev && current.ino === opened.ino && current.mtimeMs === opened.mtimeMs && current.ctimeMs === opened.ctimeMs, "Plugin registry changed during read");
|
|
35059
35203
|
let value;
|
|
35060
35204
|
try {
|
|
@@ -35065,7 +35209,7 @@ function readRegistry(path) {
|
|
|
35065
35209
|
pluginNeed(pluginObject(value) && value.version === 2 && pluginObject(value.plugins), "Unsupported native plugin registry schema");
|
|
35066
35210
|
return { value, rawDigest: pluginHash(bytes.subarray(0, length)) };
|
|
35067
35211
|
} finally {
|
|
35068
|
-
|
|
35212
|
+
closeSync14(fd);
|
|
35069
35213
|
}
|
|
35070
35214
|
}
|
|
35071
35215
|
function nativeVersionMatches(version2, receipt) {
|
|
@@ -35083,7 +35227,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
35083
35227
|
seen.add(target.pluginId);
|
|
35084
35228
|
const rows = plugins[target.pluginId];
|
|
35085
35229
|
pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
|
|
35086
|
-
const [name, market] = target.pluginId.split("@"), cacheRoot =
|
|
35230
|
+
const [name, market] = target.pluginId.split("@"), cacheRoot = join27(dirname16(path), "cache", market, name);
|
|
35087
35231
|
const scopes = new Set, approvals = new Map, currentFiles = new Map;
|
|
35088
35232
|
plugins[target.pluginId] = rows.map((row) => {
|
|
35089
35233
|
pluginNeed(pluginObject(row), "Invalid managed plugin registration");
|
|
@@ -35096,7 +35240,7 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
35096
35240
|
pluginText(row.sourceProducerPath);
|
|
35097
35241
|
pluginText(row.lastUpdated, 64);
|
|
35098
35242
|
pluginNeed(Number.isFinite(Date.parse(row.lastUpdated)), "Invalid managed plugin update timestamp");
|
|
35099
|
-
pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath ===
|
|
35243
|
+
pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join27(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
|
|
35100
35244
|
const approval = (producer) => {
|
|
35101
35245
|
pluginText(producer);
|
|
35102
35246
|
const digest = `sha256:${basename7(producer)}`;
|
|
@@ -35125,8 +35269,8 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
35125
35269
|
}
|
|
35126
35270
|
return Object.fromEntries(Object.entries(row).filter(([key]) => !["version", "installPath", "sourceProducerPath", "previousProducerPaths", "lastUpdated"].includes(key)));
|
|
35127
35271
|
});
|
|
35128
|
-
|
|
35129
|
-
const before =
|
|
35272
|
+
safe4(cacheRoot);
|
|
35273
|
+
const before = lstatSync18(cacheRoot);
|
|
35130
35274
|
pluginNeed(before.isDirectory(), "Invalid managed plugin cache root");
|
|
35131
35275
|
const directory = opendirSync3(cacheRoot), names = [];
|
|
35132
35276
|
try {
|
|
@@ -35139,18 +35283,18 @@ function hashManagedPluginRegistry(path, managed) {
|
|
|
35139
35283
|
directory.closeSync();
|
|
35140
35284
|
}
|
|
35141
35285
|
for (const version2 of names.sort()) {
|
|
35142
|
-
const files = pluginFileWitnesses(snapshotPluginTree(
|
|
35286
|
+
const files = pluginFileWitnesses(snapshotPluginTree(join27(cacheRoot, version2), { nativeRuntime: `claude-${target.native.version}` }));
|
|
35143
35287
|
verifiedBytes += files.reduce((total, file2) => total + file2.size, 0);
|
|
35144
35288
|
pluginNeed(verifiedBytes <= 256 * 1024 * 1024, "Managed plugin verification exceeds its aggregate byte budget");
|
|
35145
35289
|
const serialized = JSON.stringify(files);
|
|
35146
|
-
const expected = currentFiles.get(
|
|
35290
|
+
const expected = currentFiles.get(join27(cacheRoot, version2));
|
|
35147
35291
|
pluginNeed(expected === undefined || serialized === expected, "Current native cache differs from its exact registered producer receipt");
|
|
35148
35292
|
pluginNeed([...approvals.values()].some((receipt) => nativeVersionMatches(version2, receipt) && JSON.stringify(receipt.plan.files) === serialized), "A retained native plugin cache version differs from every approved projection");
|
|
35149
35293
|
}
|
|
35150
35294
|
for (const row of rows)
|
|
35151
35295
|
pluginNeed(names.includes(row.version), "Managed native plugin installation is missing");
|
|
35152
|
-
const after =
|
|
35153
|
-
|
|
35296
|
+
const after = lstatSync18(cacheRoot);
|
|
35297
|
+
safe4(cacheRoot);
|
|
35154
35298
|
pluginNeed(before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs, "Managed cache version membership changed during verification");
|
|
35155
35299
|
}
|
|
35156
35300
|
pluginNeed(readRegistry(path).rawDigest === snapshot2.rawDigest, "Native plugin registry changed during content verification");
|
|
@@ -35163,50 +35307,50 @@ var init_plugin_discovery = __esm(() => {
|
|
|
35163
35307
|
});
|
|
35164
35308
|
|
|
35165
35309
|
// src/lib/claude-marketplace-registry.ts
|
|
35166
|
-
import { createHash as
|
|
35167
|
-
import { closeSync as
|
|
35168
|
-
import { basename as basename8, dirname as
|
|
35169
|
-
function
|
|
35310
|
+
import { createHash as createHash18 } from "crypto";
|
|
35311
|
+
import { closeSync as closeSync15, constants as constants13, fstatSync as fstatSync15, lstatSync as lstatSync19, openSync as openSync15, readSync as readSync10 } from "fs";
|
|
35312
|
+
import { basename as basename8, dirname as dirname17, isAbsolute as isAbsolute17, resolve as resolve18 } from "path";
|
|
35313
|
+
function need8(value, reason2) {
|
|
35170
35314
|
if (!value)
|
|
35171
35315
|
throw new Error(`Claude marketplace registry ${reason2}`);
|
|
35172
35316
|
}
|
|
35173
|
-
function
|
|
35174
|
-
|
|
35317
|
+
function absolutePath4(value) {
|
|
35318
|
+
need8(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute17(value) && resolve18(value) === value, "requires a normalized absolute path");
|
|
35175
35319
|
}
|
|
35176
35320
|
function parents2(path) {
|
|
35177
35321
|
const result = [];
|
|
35178
|
-
for (let at =
|
|
35179
|
-
const stat2 =
|
|
35180
|
-
|
|
35322
|
+
for (let at = dirname17(path);; at = dirname17(at)) {
|
|
35323
|
+
const stat2 = lstatSync19(at, { bigint: true });
|
|
35324
|
+
need8(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
35181
35325
|
result.push([at, stat2]);
|
|
35182
|
-
if (at ===
|
|
35326
|
+
if (at === dirname17(at))
|
|
35183
35327
|
return result;
|
|
35184
35328
|
}
|
|
35185
35329
|
}
|
|
35186
35330
|
function readRegistry2(path, budget2) {
|
|
35187
|
-
|
|
35188
|
-
|
|
35189
|
-
|
|
35190
|
-
const ancestors2 = parents2(path), initial =
|
|
35191
|
-
|
|
35192
|
-
|
|
35193
|
-
const fd =
|
|
35331
|
+
absolutePath4(path);
|
|
35332
|
+
need8(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
|
|
35333
|
+
need8(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
|
|
35334
|
+
const ancestors2 = parents2(path), initial = lstatSync19(path, { bigint: true });
|
|
35335
|
+
need8(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
|
|
35336
|
+
need8(initial.size <= BigInt(budget2.remaining), "exceeds its aggregate byte limit");
|
|
35337
|
+
const fd = openSync15(path, constants13.O_RDONLY | constants13.O_NOFOLLOW | constants13.O_NONBLOCK);
|
|
35194
35338
|
try {
|
|
35195
|
-
const opened =
|
|
35196
|
-
|
|
35339
|
+
const opened = fstatSync15(fd, { bigint: true });
|
|
35340
|
+
need8(opened.isFile() && sameFile2(initial, opened), "changed during open");
|
|
35197
35341
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
35198
35342
|
let length = 0;
|
|
35199
35343
|
while (length < bytes.length) {
|
|
35200
|
-
const count =
|
|
35344
|
+
const count = readSync10(fd, bytes, length, bytes.length - length, null);
|
|
35201
35345
|
if (!count)
|
|
35202
35346
|
break;
|
|
35203
35347
|
length += count;
|
|
35204
35348
|
}
|
|
35205
|
-
const after =
|
|
35206
|
-
|
|
35349
|
+
const after = fstatSync15(fd, { bigint: true }), current = lstatSync19(path, { bigint: true });
|
|
35350
|
+
need8(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
35207
35351
|
for (const [ancestor, before] of ancestors2) {
|
|
35208
|
-
const now =
|
|
35209
|
-
|
|
35352
|
+
const now = lstatSync19(ancestor, { bigint: true });
|
|
35353
|
+
need8(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
35210
35354
|
}
|
|
35211
35355
|
budget2.remaining -= length;
|
|
35212
35356
|
try {
|
|
@@ -35215,7 +35359,7 @@ function readRegistry2(path, budget2) {
|
|
|
35215
35359
|
throw new Error("Claude marketplace registry is not strict UTF-8");
|
|
35216
35360
|
}
|
|
35217
35361
|
} finally {
|
|
35218
|
-
|
|
35362
|
+
closeSync15(fd);
|
|
35219
35363
|
}
|
|
35220
35364
|
}
|
|
35221
35365
|
function parse11(text2) {
|
|
@@ -35226,7 +35370,7 @@ function parse11(text2) {
|
|
|
35226
35370
|
at++;
|
|
35227
35371
|
};
|
|
35228
35372
|
function string4() {
|
|
35229
|
-
|
|
35373
|
+
need8(text2[at] === '"', "contains invalid JSON");
|
|
35230
35374
|
const start = at++;
|
|
35231
35375
|
while (at < text2.length) {
|
|
35232
35376
|
const character = text2[at++];
|
|
@@ -35237,7 +35381,7 @@ function parse11(text2) {
|
|
|
35237
35381
|
} catch {
|
|
35238
35382
|
throw new Error("Claude marketplace registry contains invalid JSON string");
|
|
35239
35383
|
}
|
|
35240
|
-
|
|
35384
|
+
need8(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
|
|
35241
35385
|
return value2;
|
|
35242
35386
|
}
|
|
35243
35387
|
if (character === "\\")
|
|
@@ -35246,7 +35390,7 @@ function parse11(text2) {
|
|
|
35246
35390
|
throw new Error("Claude marketplace registry contains an unterminated JSON string");
|
|
35247
35391
|
}
|
|
35248
35392
|
function value(depth) {
|
|
35249
|
-
|
|
35393
|
+
need8(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
35250
35394
|
space();
|
|
35251
35395
|
const character = text2[at];
|
|
35252
35396
|
if (character === '"')
|
|
@@ -35254,26 +35398,26 @@ function parse11(text2) {
|
|
|
35254
35398
|
if (character === "{" || character === "[") {
|
|
35255
35399
|
at++;
|
|
35256
35400
|
space();
|
|
35257
|
-
const
|
|
35401
|
+
const object6 = character === "{", end = object6 ? "}" : "]", entries = [], items = [], keys2 = new Set;
|
|
35258
35402
|
if (text2[at] !== end)
|
|
35259
35403
|
while (true) {
|
|
35260
|
-
if (
|
|
35404
|
+
if (object6) {
|
|
35261
35405
|
space();
|
|
35262
35406
|
const key = string4();
|
|
35263
|
-
|
|
35264
|
-
|
|
35407
|
+
need8(!keys2.has(key), "contains a duplicate JSON key");
|
|
35408
|
+
keys2.add(key);
|
|
35265
35409
|
space();
|
|
35266
|
-
|
|
35410
|
+
need8(text2[at++] === ":", "contains invalid JSON");
|
|
35267
35411
|
entries.push([key, value(depth + 1)]);
|
|
35268
35412
|
} else
|
|
35269
35413
|
items.push(value(depth + 1));
|
|
35270
35414
|
space();
|
|
35271
35415
|
if (text2[at] === end)
|
|
35272
35416
|
break;
|
|
35273
|
-
|
|
35417
|
+
need8(text2[at++] === ",", "contains invalid JSON");
|
|
35274
35418
|
}
|
|
35275
35419
|
at++;
|
|
35276
|
-
return
|
|
35420
|
+
return object6 ? { kind: "object", entries } : { kind: "array", items };
|
|
35277
35421
|
}
|
|
35278
35422
|
for (const literal2 of ["true", "false", "null"])
|
|
35279
35423
|
if (text2.startsWith(literal2, at)) {
|
|
@@ -35282,21 +35426,21 @@ function parse11(text2) {
|
|
|
35282
35426
|
}
|
|
35283
35427
|
numberPattern.lastIndex = at;
|
|
35284
35428
|
const number4 = numberPattern.exec(text2);
|
|
35285
|
-
|
|
35429
|
+
need8(number4, "contains invalid JSON");
|
|
35286
35430
|
at += number4[0].length;
|
|
35287
35431
|
return { kind: "number", value: number4[0] };
|
|
35288
35432
|
}
|
|
35289
35433
|
const root = value(0);
|
|
35290
35434
|
space();
|
|
35291
|
-
|
|
35292
|
-
|
|
35435
|
+
need8(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
|
|
35436
|
+
need8(root.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
|
|
35293
35437
|
return root;
|
|
35294
35438
|
}
|
|
35295
|
-
function exactKeys(value,
|
|
35296
|
-
return value?.kind === "object" && value.entries.length ===
|
|
35439
|
+
function exactKeys(value, keys2) {
|
|
35440
|
+
return value?.kind === "object" && value.entries.length === keys2.length && value.entries.every(([key]) => keys2.includes(key));
|
|
35297
35441
|
}
|
|
35298
35442
|
function textField(value) {
|
|
35299
|
-
|
|
35443
|
+
need8(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
|
|
35300
35444
|
return value.value;
|
|
35301
35445
|
}
|
|
35302
35446
|
function normalizeRow(row, version2 = 1) {
|
|
@@ -35304,7 +35448,7 @@ function normalizeRow(row, version2 = 1) {
|
|
|
35304
35448
|
if (version2 !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
35305
35449
|
return;
|
|
35306
35450
|
const autoUpdate = field2(row, "autoUpdate");
|
|
35307
|
-
|
|
35451
|
+
need8(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
35308
35452
|
}
|
|
35309
35453
|
const source = field2(row, "source");
|
|
35310
35454
|
if (source?.kind !== "object")
|
|
@@ -35317,29 +35461,29 @@ function normalizeRow(row, version2 = 1) {
|
|
|
35317
35461
|
return;
|
|
35318
35462
|
const sourceLocation = textField(field2(source, location));
|
|
35319
35463
|
if (type.value === "directory")
|
|
35320
|
-
|
|
35321
|
-
|
|
35464
|
+
absolutePath4(sourceLocation);
|
|
35465
|
+
absolutePath4(textField(field2(row, "installLocation")));
|
|
35322
35466
|
const updated = textField(field2(row, "lastUpdated"));
|
|
35323
|
-
|
|
35467
|
+
need8(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
|
|
35324
35468
|
const millis = Date.parse(updated);
|
|
35325
|
-
|
|
35469
|
+
need8(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
35326
35470
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
35327
35471
|
}
|
|
35328
35472
|
function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
35329
35473
|
const value = parse11(readRegistry2(path, budget2));
|
|
35330
35474
|
for (const [, row] of value.entries)
|
|
35331
35475
|
normalizeRow(row);
|
|
35332
|
-
const sha2562 =
|
|
35476
|
+
const sha2562 = createHash18("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
35333
35477
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
35334
35478
|
}
|
|
35335
35479
|
function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
35336
35480
|
const value = parse11(readRegistry2(path, budget2));
|
|
35337
35481
|
for (const [, row] of value.entries)
|
|
35338
35482
|
normalizeRow(row, 2);
|
|
35339
|
-
const sha2562 =
|
|
35483
|
+
const sha2562 = createHash18("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
35340
35484
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
35341
35485
|
}
|
|
35342
|
-
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS, 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, field2 = (
|
|
35486
|
+
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS, 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, field2 = (object6, key) => object6.entries.find(([name]) => name === key)?.[1];
|
|
35343
35487
|
var init_claude_marketplace_registry = __esm(() => {
|
|
35344
35488
|
CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
35345
35489
|
});
|
|
@@ -35358,7 +35502,7 @@ var init_codex_hook_additions_review = __esm(() => {
|
|
|
35358
35502
|
});
|
|
35359
35503
|
|
|
35360
35504
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
35361
|
-
import { createHash as
|
|
35505
|
+
import { createHash as createHash19 } from "crypto";
|
|
35362
35506
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
35363
35507
|
const proof = proofs2.get(witness);
|
|
35364
35508
|
need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === discoveryDigest(binding), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
@@ -35373,13 +35517,13 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
35373
35517
|
need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
35374
35518
|
return true;
|
|
35375
35519
|
}
|
|
35376
|
-
var
|
|
35520
|
+
var sha4 = (text2) => createHash19("sha256").update(text2).digest("hex"), fields, discoveryDigest = (binding) => {
|
|
35377
35521
|
const controls = binding.codexDisabledPluginSkills;
|
|
35378
35522
|
if (!Object.hasOwn(binding, "codexDisabledPluginSkills") || Array.isArray(controls) && controls.length === 0) {
|
|
35379
35523
|
const { codexDisabledPluginSkills: _controls, ...withoutControls } = binding;
|
|
35380
|
-
return
|
|
35524
|
+
return sha4(JSON.stringify({ ...withoutControls, codexDisabledPluginSkills: [] }));
|
|
35381
35525
|
}
|
|
35382
|
-
return
|
|
35526
|
+
return sha4(JSON.stringify(binding));
|
|
35383
35527
|
}, proofs2;
|
|
35384
35528
|
var init_codex_hook_discovery_recovery = __esm(() => {
|
|
35385
35529
|
init_codex_hook_trust_files();
|
|
@@ -35391,16 +35535,16 @@ var init_codex_hook_discovery_recovery = __esm(() => {
|
|
|
35391
35535
|
});
|
|
35392
35536
|
|
|
35393
35537
|
// src/lib/agent-discovery.ts
|
|
35394
|
-
import { createHash as
|
|
35395
|
-
import { lstatSync as
|
|
35396
|
-
import { basename as basename9, dirname as
|
|
35538
|
+
import { createHash as createHash20 } from "crypto";
|
|
35539
|
+
import { lstatSync as lstatSync20, readFileSync as readFileSync16, readdirSync as readdirSync7, realpathSync as realpathSync6 } from "fs";
|
|
35540
|
+
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute18, join as join28, resolve as resolve19, sep as sep8 } from "path";
|
|
35397
35541
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
35398
35542
|
function discoveryExecutable(agent) {
|
|
35399
35543
|
const command = DISCOVERY_RUNTIME_COMMANDS[agent];
|
|
35400
35544
|
const found = command ? Bun.which(command) : null;
|
|
35401
35545
|
if (!command || !found)
|
|
35402
35546
|
return null;
|
|
35403
|
-
const path =
|
|
35547
|
+
const path = resolve19(found), target = realpathOrNull(path);
|
|
35404
35548
|
return target === null ? null : { command, path, target };
|
|
35405
35549
|
}
|
|
35406
35550
|
function parseConfig(text2, path, toml = false) {
|
|
@@ -35413,21 +35557,21 @@ function parseConfig(text2, path, toml = false) {
|
|
|
35413
35557
|
throw new Error(`Invalid native discovery configuration: ${path}`);
|
|
35414
35558
|
}
|
|
35415
35559
|
}
|
|
35416
|
-
function
|
|
35417
|
-
if (!
|
|
35560
|
+
function safe5(path) {
|
|
35561
|
+
if (!isAbsolute18(path) || path.includes("\x00"))
|
|
35418
35562
|
throw new Error("Expected an absolute native discovery path");
|
|
35419
|
-
for (let cursor = path;; cursor =
|
|
35420
|
-
if (
|
|
35563
|
+
for (let cursor = path;; cursor = dirname18(cursor)) {
|
|
35564
|
+
if (lstatSync20(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
35421
35565
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
35422
|
-
if (
|
|
35566
|
+
if (dirname18(cursor) === cursor)
|
|
35423
35567
|
break;
|
|
35424
35568
|
}
|
|
35425
35569
|
}
|
|
35426
35570
|
function read(path, changes) {
|
|
35427
|
-
|
|
35571
|
+
safe5(path);
|
|
35428
35572
|
if (changes?.has(path))
|
|
35429
35573
|
return changes.get(path);
|
|
35430
|
-
const stat2 =
|
|
35574
|
+
const stat2 = lstatSync20(path, { throwIfNoEntry: false });
|
|
35431
35575
|
if (!stat2)
|
|
35432
35576
|
return null;
|
|
35433
35577
|
if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
|
|
@@ -35438,8 +35582,8 @@ function assertReviewedClaudeCacheClosures(home, config2, review, canonical8) {
|
|
|
35438
35582
|
const enabledIds = Object.entries(config2.enabledPlugins ?? {}).filter(([, enabled]) => enabled === true).map(([id]) => id);
|
|
35439
35583
|
if (enabledIds.length === 0)
|
|
35440
35584
|
return;
|
|
35441
|
-
const registryPath = canonical8(
|
|
35442
|
-
const cacheAliasRoot =
|
|
35585
|
+
const registryPath = canonical8(join28(home, ".claude/plugins/installed_plugins.json"));
|
|
35586
|
+
const cacheAliasRoot = join28(home, ".claude/plugins/cache");
|
|
35443
35587
|
const cacheRoot = canonical8(cacheAliasRoot);
|
|
35444
35588
|
const registryText = read(registryPath);
|
|
35445
35589
|
if (registryText === null)
|
|
@@ -35447,7 +35591,7 @@ function assertReviewedClaudeCacheClosures(home, config2, review, canonical8) {
|
|
|
35447
35591
|
const registry2 = parseConfig(registryText, registryPath);
|
|
35448
35592
|
if (registry2.version !== 2 || !registry2.plugins || typeof registry2.plugins !== "object" || Array.isArray(registry2.plugins))
|
|
35449
35593
|
throw new Error("Reviewed Claude installed plugin registrations are invalid");
|
|
35450
|
-
const cachePath = (path) => typeof path === "string" &&
|
|
35594
|
+
const cachePath = (path) => typeof path === "string" && isAbsolute18(path) && resolve19(path) === path && [cacheAliasRoot, cacheRoot].some((root) => path === root || path.startsWith(root + sep8));
|
|
35451
35595
|
const activeRoots = new Set;
|
|
35452
35596
|
for (const id of enabledIds) {
|
|
35453
35597
|
const rows = registry2.plugins[id];
|
|
@@ -35459,11 +35603,11 @@ function assertReviewedClaudeCacheClosures(home, config2, review, canonical8) {
|
|
|
35459
35603
|
if (!row || typeof row !== "object" || Array.isArray(row) || typeof row.installPath !== "string") {
|
|
35460
35604
|
throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
|
|
35461
35605
|
}
|
|
35462
|
-
if ([cacheAliasRoot, cacheRoot].some((root) => row.installPath === root || row.installPath.startsWith(root +
|
|
35606
|
+
if ([cacheAliasRoot, cacheRoot].some((root) => row.installPath === root || row.installPath.startsWith(root + sep8))) {
|
|
35463
35607
|
if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename9(row.installPath) !== row.version)) {
|
|
35464
35608
|
throw new Error("Reviewed Claude discovery has a malformed active cache registration");
|
|
35465
35609
|
}
|
|
35466
|
-
if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" &&
|
|
35610
|
+
if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute18(row.projectPath) && resolve19(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
|
|
35467
35611
|
throw new Error("Reviewed Claude discovery has an unsupported active cache registration scope");
|
|
35468
35612
|
activeRoots.add(canonical8(row.installPath));
|
|
35469
35613
|
}
|
|
@@ -35480,12 +35624,12 @@ function assertReviewedClaudeCacheClosures(home, config2, review, canonical8) {
|
|
|
35480
35624
|
throw new Error(`Reviewed active Claude cache plugin ${description} is not bound`);
|
|
35481
35625
|
};
|
|
35482
35626
|
for (const root of activeRoots) {
|
|
35483
|
-
|
|
35484
|
-
const rootStat =
|
|
35627
|
+
safe5(root);
|
|
35628
|
+
const rootStat = lstatSync20(root, { throwIfNoEntry: false });
|
|
35485
35629
|
if (!rootStat?.isDirectory() || realpathSync6(root) !== root)
|
|
35486
35630
|
throw new Error("Reviewed Claude active cache plugin root is missing or unsafe");
|
|
35487
|
-
const manifestPath =
|
|
35488
|
-
const manifestStat =
|
|
35631
|
+
const manifestPath = join28(root, ".claude-plugin/plugin.json");
|
|
35632
|
+
const manifestStat = lstatSync20(manifestPath, { throwIfNoEntry: false });
|
|
35489
35633
|
let manifest;
|
|
35490
35634
|
if (manifestStat) {
|
|
35491
35635
|
if (!manifestStat.isFile())
|
|
@@ -35500,37 +35644,37 @@ function assertReviewedClaudeCacheClosures(home, config2, review, canonical8) {
|
|
|
35500
35644
|
} else if (!review.sources.some((source) => source.path === manifestPath && source.hashMode === "path-bytes" && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined)) {
|
|
35501
35645
|
throw new Error("Reviewed Claude active cache plugin manifest absence requires a path-bytes witness");
|
|
35502
35646
|
}
|
|
35503
|
-
const roots = new Set([
|
|
35504
|
-
if (
|
|
35647
|
+
const roots = new Set([join28(root, "skills")]);
|
|
35648
|
+
if (lstatSync20(join28(root, "SKILL.md"), { throwIfNoEntry: false })?.isFile())
|
|
35505
35649
|
roots.add(root);
|
|
35506
35650
|
const declared = manifest?.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
35507
35651
|
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
35508
35652
|
throw new Error("Reviewed Claude active cache plugin has unsupported skill paths");
|
|
35509
35653
|
for (const value of declared) {
|
|
35510
|
-
const path =
|
|
35511
|
-
if (path !== root && !path.startsWith(root +
|
|
35654
|
+
const path = resolve19(root, value);
|
|
35655
|
+
if (path !== root && !path.startsWith(root + sep8))
|
|
35512
35656
|
throw new Error("Reviewed Claude active cache plugin skill path escapes its root");
|
|
35513
|
-
roots.add(path.endsWith(`${
|
|
35657
|
+
roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname18(path) : path);
|
|
35514
35658
|
}
|
|
35515
35659
|
for (const skillRoot of roots)
|
|
35516
35660
|
if (!review.roots.includes(skillRoot))
|
|
35517
35661
|
throw new Error("Reviewed Claude active cache plugin skill root is not covered");
|
|
35518
|
-
const hooksPath =
|
|
35519
|
-
const hooksStat =
|
|
35662
|
+
const hooksPath = join28(root, "hooks/hooks.json");
|
|
35663
|
+
const hooksStat = lstatSync20(hooksPath, { throwIfNoEntry: false });
|
|
35520
35664
|
if (hooksStat) {
|
|
35521
35665
|
if (!hooksStat.isFile())
|
|
35522
35666
|
throw new Error("Reviewed Claude plugin hook manifest is unsupported");
|
|
35523
35667
|
requireSource(hooksPath, "hook manifest");
|
|
35524
35668
|
}
|
|
35525
35669
|
if (typeof manifest?.hooks === "string")
|
|
35526
|
-
requireSource(
|
|
35670
|
+
requireSource(resolve19(root, manifest.hooks), "manifest-declared hook source");
|
|
35527
35671
|
}
|
|
35528
35672
|
}
|
|
35529
35673
|
function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
35530
|
-
const
|
|
35531
|
-
if (!
|
|
35674
|
+
const object6 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
|
|
35675
|
+
if (!object6 || typeof object6 !== "object" || Array.isArray(object6))
|
|
35532
35676
|
throw new Error("Expected native discovery configuration object");
|
|
35533
|
-
return digest(JSON.stringify(Object.fromEntries(fields2.map((field3) => [field3,
|
|
35677
|
+
return digest(JSON.stringify(Object.fromEntries(fields2.map((field3) => [field3, object6[field3] ?? null]))));
|
|
35534
35678
|
}
|
|
35535
35679
|
function isSupersededSettingsProjection(agent, configPath, source) {
|
|
35536
35680
|
if (agent === "sumi")
|
|
@@ -35543,6 +35687,8 @@ function supersedesSettingsProjection(agent, sources, configPath) {
|
|
|
35543
35687
|
return agent === "codex" && sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")));
|
|
35544
35688
|
}
|
|
35545
35689
|
function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
35690
|
+
if (source.reviewArtifact !== undefined)
|
|
35691
|
+
verifyRetainedReviewArtifact(source);
|
|
35546
35692
|
if (source.hashMode === "sumi-settings-v1") {
|
|
35547
35693
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
35548
35694
|
throw new Error("Sumi settings witnesses require exact typed metadata");
|
|
@@ -35618,9 +35764,9 @@ function absentCodexSkillRoots(binding) {
|
|
|
35618
35764
|
return [];
|
|
35619
35765
|
if (binding.agent !== "codex" || !Array.isArray(controls) || controls.length > 4096)
|
|
35620
35766
|
throw new Error("Invalid disabled Codex discovery proof");
|
|
35621
|
-
if (binding.method !== "reviewed" || controls.every((control) =>
|
|
35767
|
+
if (binding.method !== "reviewed" || controls.every((control) => lstatSync20(control.pluginParent, { throwIfNoEntry: false })))
|
|
35622
35768
|
return [];
|
|
35623
|
-
const cache =
|
|
35769
|
+
const cache = dirname18(dirname18(controls[0].pluginParent)), config2 = join28(dirname18(dirname18(cache)), "config.toml");
|
|
35624
35770
|
const source = binding.sources.find((source2) => source2.path === config2 && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
35625
35771
|
if (!source || projected(source) !== source.sha256)
|
|
35626
35772
|
throw new Error("Missing current disabled Codex configuration witness");
|
|
@@ -35628,8 +35774,8 @@ function absentCodexSkillRoots(binding) {
|
|
|
35628
35774
|
return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
|
|
35629
35775
|
}
|
|
35630
35776
|
function codexDirectHookSelects(cache, root) {
|
|
35631
|
-
const codex =
|
|
35632
|
-
const hooks = read(
|
|
35777
|
+
const codex = dirname18(dirname18(cache)), config2 = Bun.TOML.parse(read(join28(codex, "config.toml")) ?? "");
|
|
35778
|
+
const hooks = read(join28(codex, "hooks.json"));
|
|
35633
35779
|
if (hooks !== null)
|
|
35634
35780
|
hashNativeJsonControls(hooks);
|
|
35635
35781
|
return JSON.stringify(config2.hooks ?? {}).includes(root) || hooks !== null && JSON.stringify(JSON.parse(hooks)).includes(root);
|
|
@@ -35641,7 +35787,7 @@ function retiredCodexRoots(binding) {
|
|
|
35641
35787
|
const controls = binding.codexDisabledPluginSkills;
|
|
35642
35788
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !Array.isArray(controls) || !controls.length || !Array.isArray(proof.roots) || proof.roots.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.roots).size !== proof.roots.length || !Array.isArray(proof.directories) || proof.parents !== undefined && (!Array.isArray(proof.parents) || proof.parents.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.parents).size !== proof.parents.length) || !proof.roots.length && !proof.parents?.length)
|
|
35643
35789
|
throw new Error("Invalid retired Codex materialization proof");
|
|
35644
|
-
const cache =
|
|
35790
|
+
const cache = dirname18(dirname18(controls[0].pluginParent)), config2 = join28(dirname18(dirname18(cache)), "config.toml");
|
|
35645
35791
|
const source = binding.sources.find((source2) => source2.path === config2 && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
|
|
35646
35792
|
if (!source || projected(source) !== source.sha256)
|
|
35647
35793
|
throw new Error("Missing current retired Codex configuration witness");
|
|
@@ -35649,7 +35795,7 @@ function retiredCodexRoots(binding) {
|
|
|
35649
35795
|
if ((proof.parents ?? []).some((parent) => codexDirectHookSelects(cache, parent) || !reviewedDisabledCodexPluginParent(cache, parent, controls, rules)) || !reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read(path), rules))
|
|
35650
35796
|
throw new Error("Retired Codex parent capabilities changed");
|
|
35651
35797
|
for (const root of proof.roots) {
|
|
35652
|
-
const manifest = binding.sources.find((source2) => source2.path ===
|
|
35798
|
+
const manifest = binding.sources.find((source2) => source2.path === join28(root, ".codex-plugin/plugin.json") && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && [undefined, "bytes"].includes(source2.hashMode));
|
|
35653
35799
|
if (!manifest || codexDirectHookSelects(cache, root) || !reviewedRetiredCodexRoot(cache, root, controls, rules, (path) => read(path), true))
|
|
35654
35800
|
throw new Error("Retired Codex materialization changed");
|
|
35655
35801
|
}
|
|
@@ -35658,7 +35804,7 @@ function retiredCodexRoots(binding) {
|
|
|
35658
35804
|
function retainableAbsentSource(source) {
|
|
35659
35805
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined)
|
|
35660
35806
|
return false;
|
|
35661
|
-
return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(
|
|
35807
|
+
return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join28(sep8, ".claude-plugin", "plugin.json"));
|
|
35662
35808
|
}
|
|
35663
35809
|
function absentRetiredClaudeRoots(binding) {
|
|
35664
35810
|
if (binding.agent !== "claude" || binding.method !== "reviewed")
|
|
@@ -35667,17 +35813,17 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
35667
35813
|
for (const source of binding.sources) {
|
|
35668
35814
|
if (!retainableAbsentSource(source) || source.sha256 === null)
|
|
35669
35815
|
continue;
|
|
35670
|
-
const marker =
|
|
35816
|
+
const marker = sep8 + ".claude" + sep8 + "plugins" + sep8 + "cache" + sep8, at = source.path.indexOf(marker);
|
|
35671
35817
|
if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
|
|
35672
35818
|
continue;
|
|
35673
|
-
const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(
|
|
35819
|
+
const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(sep8);
|
|
35674
35820
|
if (parts.length < 4 || parts.slice(0, 3).some((part) => !part || part === "." || part === ".."))
|
|
35675
35821
|
continue;
|
|
35676
|
-
const root =
|
|
35677
|
-
|
|
35678
|
-
if (
|
|
35822
|
+
const root = join28(cache, ...parts.slice(0, 3));
|
|
35823
|
+
safe5(root);
|
|
35824
|
+
if (lstatSync20(root, { throwIfNoEntry: false }))
|
|
35679
35825
|
continue;
|
|
35680
|
-
const claude =
|
|
35826
|
+
const claude = dirname18(dirname18(cache)), settings = join28(claude, "settings.json"), registry2 = join28(claude, "plugins/installed_plugins.json");
|
|
35681
35827
|
const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
|
|
35682
35828
|
const configSource = full(settings), registrySource = full(registry2);
|
|
35683
35829
|
if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
|
|
@@ -35694,15 +35840,15 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
35694
35840
|
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
35695
35841
|
return null;
|
|
35696
35842
|
const value = row;
|
|
35697
|
-
if (typeof value.installPath !== "string" || /[\x00-\x1f\x7f]/.test(value.installPath) || !
|
|
35843
|
+
if (typeof value.installPath !== "string" || /[\x00-\x1f\x7f]/.test(value.installPath) || !isAbsolute18(value.installPath) || resolve19(value.installPath) !== value.installPath || value.installPath.length > AGENT_POLICY_LIMITS.pathCharacters)
|
|
35698
35844
|
return null;
|
|
35699
35845
|
if (value.scope === "user")
|
|
35700
35846
|
return value.projectPath === undefined || value.projectPath === null ? "user\x00" : null;
|
|
35701
35847
|
if (value.scope === "project")
|
|
35702
|
-
return typeof value.projectPath === "string" && !/[\x00-\x1f\x7f]/.test(value.projectPath) && value.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters &&
|
|
35848
|
+
return typeof value.projectPath === "string" && !/[\x00-\x1f\x7f]/.test(value.projectPath) && value.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute18(value.projectPath) && resolve19(value.projectPath) === value.projectPath ? `project\x00${value.projectPath}` : null;
|
|
35703
35849
|
return null;
|
|
35704
35850
|
};
|
|
35705
|
-
if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root || row.installPath.startsWith(root +
|
|
35851
|
+
if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root || row.installPath.startsWith(root + sep8) || root.startsWith(row.installPath + sep8))))
|
|
35706
35852
|
continue;
|
|
35707
35853
|
const rows = installed.plugins[id];
|
|
35708
35854
|
if (!Array.isArray(rows) || rows.length < 1)
|
|
@@ -35720,9 +35866,9 @@ function absentRetiredClaudeRoots(binding) {
|
|
|
35720
35866
|
if (!targetValid || !targetScopes.has("user\x00"))
|
|
35721
35867
|
continue;
|
|
35722
35868
|
const active = rows[0]?.installPath;
|
|
35723
|
-
if (typeof active !== "string" || rows[0]?.version !== basename9(active) ||
|
|
35869
|
+
if (typeof active !== "string" || rows[0]?.version !== basename9(active) || dirname18(active) !== dirname18(root) || active === root || !lstatSync20(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync6(active) !== active)
|
|
35724
35870
|
continue;
|
|
35725
|
-
const activeManifest = full(
|
|
35871
|
+
const activeManifest = full(join28(active, ".claude-plugin/plugin.json"));
|
|
35726
35872
|
if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
|
|
35727
35873
|
continue;
|
|
35728
35874
|
result.add(root);
|
|
@@ -35738,10 +35884,10 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
35738
35884
|
throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
|
|
35739
35885
|
const installationRoots = codexInstallationRoots(binding);
|
|
35740
35886
|
const retiredRoots = retiredCodexRoots(binding);
|
|
35741
|
-
const installationInput = (path) => installationRoots.some((root) => path === root || path.startsWith(root +
|
|
35887
|
+
const installationInput = (path) => installationRoots.some((root) => path === root || path.startsWith(root + sep8));
|
|
35742
35888
|
if (binding.directories !== undefined) {
|
|
35743
35889
|
const projected2 = binding.codexInstallationInputs?.directories ?? [];
|
|
35744
|
-
const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root) => root.startsWith(directory.path +
|
|
35890
|
+
const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root) => root.startsWith(directory.path + sep8)));
|
|
35745
35891
|
if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
|
|
35746
35892
|
throw new Error("Missing installation input directory projection");
|
|
35747
35893
|
const ordinary = binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory));
|
|
@@ -35749,7 +35895,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
35749
35895
|
const projection = binding.codexRetiredMaterializations.directories;
|
|
35750
35896
|
if (projection.length !== ordinary.length || projection.some((item, index) => item.path !== ordinary[index]?.path))
|
|
35751
35897
|
throw new Error("Missing retired Codex directory projection");
|
|
35752
|
-
const omitted = [...retiredRoots, ...(binding.codexRetiredMaterializations.parents ?? []).filter((parent) => !
|
|
35898
|
+
const omitted = [...retiredRoots, ...(binding.codexRetiredMaterializations.parents ?? []).filter((parent) => !lstatSync20(parent, { throwIfNoEntry: false }))];
|
|
35753
35899
|
if (projection.every((item) => Array.isArray(item.entries)))
|
|
35754
35900
|
verifyDiscoveryDirectoryProjection(projection, omitted);
|
|
35755
35901
|
else {
|
|
@@ -35763,7 +35909,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
35763
35909
|
verifyDiscoveryDirectories(ordinary);
|
|
35764
35910
|
verifyDiscoveryDirectories(projected2, installationRoots);
|
|
35765
35911
|
}
|
|
35766
|
-
const absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root) => !
|
|
35912
|
+
const absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root) => !lstatSync20(root, { throwIfNoEntry: false }))];
|
|
35767
35913
|
const budget2 = discoveryByteBudget();
|
|
35768
35914
|
for (const source of binding.sources) {
|
|
35769
35915
|
assertMarketplaceBinding(binding, source);
|
|
@@ -35774,11 +35920,11 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
35774
35920
|
if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
35775
35921
|
throw new Error("Invalid native discovery digest");
|
|
35776
35922
|
if (installationInput(source.path)) {
|
|
35777
|
-
|
|
35923
|
+
safe5(source.path);
|
|
35778
35924
|
continue;
|
|
35779
35925
|
}
|
|
35780
|
-
if (absentRoots.some((root) => source.path.startsWith(root +
|
|
35781
|
-
|
|
35926
|
+
if (absentRoots.some((root) => source.path.startsWith(root + sep8)) && retainableAbsentSource(source)) {
|
|
35927
|
+
safe5(source.path);
|
|
35782
35928
|
continue;
|
|
35783
35929
|
}
|
|
35784
35930
|
const current = projected(source, undefined, budget2);
|
|
@@ -35786,10 +35932,10 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
35786
35932
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
35787
35933
|
}
|
|
35788
35934
|
for (const root of binding.roots)
|
|
35789
|
-
|
|
35935
|
+
safe5(root);
|
|
35790
35936
|
for (const root of absentRoots) {
|
|
35791
|
-
|
|
35792
|
-
if (
|
|
35937
|
+
safe5(root);
|
|
35938
|
+
if (lstatSync20(root, { throwIfNoEntry: false }))
|
|
35793
35939
|
throw new Error("Retired native materialization changed during discovery verification");
|
|
35794
35940
|
}
|
|
35795
35941
|
}
|
|
@@ -35799,13 +35945,13 @@ function codexInstallationRoots(binding) {
|
|
|
35799
35945
|
return [];
|
|
35800
35946
|
if (binding.agent !== "codex" || binding.method !== "reviewed" || !supportsCodexNativeCapability(proof.version, "installed-plugin-review") || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
|
|
35801
35947
|
throw new Error("Invalid native installation input proof");
|
|
35802
|
-
const cache =
|
|
35803
|
-
const config2 =
|
|
35948
|
+
const cache = dirname18(dirname18(proof.plugins[0].pluginParent));
|
|
35949
|
+
const config2 = join28(dirname18(dirname18(cache)), "config.toml"), home = dirname18(dirname18(dirname18(cache)));
|
|
35804
35950
|
const configSource = binding.sources.find((source) => source.path === config2 && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
|
|
35805
|
-
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) =>
|
|
35806
|
-
if (!configSource || proof.plugins.some((input2) => nativeRoots.some((root) => root === input2.sourceRoot || root.startsWith(input2.sourceRoot +
|
|
35951
|
+
const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join28(home, path));
|
|
35952
|
+
if (!configSource || proof.plugins.some((input2) => nativeRoots.some((root) => root === input2.sourceRoot || root.startsWith(input2.sourceRoot + sep8) || input2.sourceRoot.startsWith(root + sep8)) || binding.sources.some((source) => basename9(source.path) === "config.toml" && (source.path === input2.sourceRoot || source.path.startsWith(input2.sourceRoot + sep8)))))
|
|
35807
35953
|
throw new Error("Native installation input overlaps configuration or loading roots");
|
|
35808
|
-
if (proof.plugins.some((input2) =>
|
|
35954
|
+
if (proof.plugins.some((input2) => dirname18(dirname18(input2.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join28(input2.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
|
|
35809
35955
|
throw new Error("Missing native installation input source witness");
|
|
35810
35956
|
return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
|
|
35811
35957
|
const text2 = read(path);
|
|
@@ -35815,7 +35961,7 @@ function codexInstallationRoots(binding) {
|
|
|
35815
35961
|
});
|
|
35816
35962
|
}
|
|
35817
35963
|
function assertMarketplaceBinding(binding, source) {
|
|
35818
|
-
if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(
|
|
35964
|
+
if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(join28(sep8, ".claude-plugin", "plugin.json"))))
|
|
35819
35965
|
throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
|
|
35820
35966
|
if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename9(source.path) !== "sumi.json"))
|
|
35821
35967
|
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
@@ -35831,10 +35977,10 @@ function assertMarketplaceBinding(binding, source) {
|
|
|
35831
35977
|
function agentDiscoveryConfigPath(home, agent) {
|
|
35832
35978
|
if (agent === "sumi")
|
|
35833
35979
|
return sumiConfigPath(home);
|
|
35834
|
-
return
|
|
35980
|
+
return join28(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
35835
35981
|
}
|
|
35836
35982
|
function resolveAgentDiscovery(options) {
|
|
35837
|
-
const canonical8 = options.canonical ??
|
|
35983
|
+
const canonical8 = options.canonical ?? resolve19, home = resolve19(options.home), agent = options.agent;
|
|
35838
35984
|
const sources = [], roots = new Set, builtinNames = [];
|
|
35839
35985
|
const witness = (path, format, fields2, hashMode) => {
|
|
35840
35986
|
const source = { path: canonical8(path), sha256: null, ...format ? { format, fields: fields2 } : {}, ...hashMode ? { hashMode } : {} };
|
|
@@ -35855,42 +36001,42 @@ function resolveAgentDiscovery(options) {
|
|
|
35855
36001
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
35856
36002
|
if (agent === "sumi") {
|
|
35857
36003
|
const directory = sumiConfigDirectory(home);
|
|
35858
|
-
if (witness(
|
|
36004
|
+
if (witness(join28(directory, "sumi.jsonc")) !== null)
|
|
35859
36005
|
unresolved("JSONC configuration requires a supported format adapter");
|
|
35860
|
-
for (const root of [directory,
|
|
36006
|
+
for (const root of [directory, join28(home, ".claude"), join28(home, ".agents")])
|
|
35861
36007
|
for (const name of ["skill", "skills"])
|
|
35862
|
-
roots.add(canonical8(
|
|
36008
|
+
roots.add(canonical8(join28(root, name)));
|
|
35863
36009
|
if (config2.skills !== undefined && (!Array.isArray(config2.skills) || config2.skills.some((path) => typeof path !== "string")))
|
|
35864
36010
|
unresolved("skills must be a string array");
|
|
35865
36011
|
for (const value of config2.skills ?? []) {
|
|
35866
|
-
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(
|
|
36012
|
+
if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute18(value) || value.startsWith("~/")))
|
|
35867
36013
|
unresolved("remote or relative skill sources need a dedicated discovery adapter");
|
|
35868
|
-
roots.add(canonical8(value.startsWith("~/") ?
|
|
36014
|
+
roots.add(canonical8(value.startsWith("~/") ? join28(home, value.slice(2)) : value));
|
|
35869
36015
|
}
|
|
35870
36016
|
for (const key of ["plugin", "plugins"])
|
|
35871
36017
|
if (config2[key] !== undefined && !Array.isArray(config2[key]))
|
|
35872
36018
|
unresolved(`${key} must be an array`);
|
|
35873
|
-
const plugins = [
|
|
36019
|
+
const plugins = [join28(directory, "plugin"), join28(directory, "plugins")];
|
|
35874
36020
|
for (const path of plugins) {
|
|
35875
|
-
|
|
35876
|
-
if (
|
|
36021
|
+
safe5(path);
|
|
36022
|
+
if (lstatSync20(path, { throwIfNoEntry: false })?.isDirectory())
|
|
35877
36023
|
for (const name of readdirSync7(path).sort()) {
|
|
35878
36024
|
if (name === "skills-cli.js" && path === plugins[1])
|
|
35879
36025
|
continue;
|
|
35880
|
-
const target =
|
|
35881
|
-
if (!
|
|
36026
|
+
const target = join28(path, name);
|
|
36027
|
+
if (!lstatSync20(target).isFile())
|
|
35882
36028
|
unresolved("local plugin packages need a dedicated discovery adapter");
|
|
35883
36029
|
witness(target);
|
|
35884
36030
|
}
|
|
35885
36031
|
}
|
|
35886
36032
|
for (const root of roots) {
|
|
35887
|
-
|
|
35888
|
-
if (
|
|
36033
|
+
safe5(root);
|
|
36034
|
+
if (lstatSync20(root, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(root).some((name) => name.endsWith(".md")))
|
|
35889
36035
|
unresolved("flat markdown skills require a dedicated native migration adapter");
|
|
35890
36036
|
}
|
|
35891
36037
|
}
|
|
35892
36038
|
if (agent === "hermes") {
|
|
35893
|
-
const profile = witness(
|
|
36039
|
+
const profile = witness(join28(home, ".hermes/active_profile"));
|
|
35894
36040
|
if (profile?.trim() && profile.trim() !== "default")
|
|
35895
36041
|
unresolved("a non-default profile is active");
|
|
35896
36042
|
const declared = config2.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
|
|
@@ -35904,8 +36050,8 @@ function resolveAgentDiscovery(options) {
|
|
|
35904
36050
|
continue;
|
|
35905
36051
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
35906
36052
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
35907
|
-
const path = normalized === "~" ? home : normalized.startsWith("~/") ?
|
|
35908
|
-
|
|
36053
|
+
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join28(home, normalized.slice(2)) : resolve19(home, ".hermes", normalized);
|
|
36054
|
+
safe5(path);
|
|
35909
36055
|
roots.add(path);
|
|
35910
36056
|
}
|
|
35911
36057
|
}
|
|
@@ -35913,24 +36059,24 @@ function resolveAgentDiscovery(options) {
|
|
|
35913
36059
|
const executable = options.executable === undefined ? discoveryExecutable(agent) : options.executable;
|
|
35914
36060
|
if (executable) {
|
|
35915
36061
|
let packageRoot;
|
|
35916
|
-
for (let directory =
|
|
35917
|
-
const path =
|
|
36062
|
+
for (let directory = dirname18(executable.target), depth = 0;depth < 8; directory = dirname18(directory), depth++) {
|
|
36063
|
+
const path = join28(directory, "package.json"), raw = read(path);
|
|
35918
36064
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
35919
36065
|
packageRoot = directory;
|
|
35920
36066
|
witness(path);
|
|
35921
36067
|
break;
|
|
35922
36068
|
}
|
|
35923
|
-
if (
|
|
36069
|
+
if (dirname18(directory) === directory)
|
|
35924
36070
|
break;
|
|
35925
36071
|
}
|
|
35926
36072
|
if (!packageRoot)
|
|
35927
36073
|
unresolved("installed Gemini package layout is not recognized");
|
|
35928
|
-
const builtinRoot =
|
|
35929
|
-
|
|
35930
|
-
if (!
|
|
36074
|
+
const builtinRoot = join28(packageRoot, "bundle/builtin");
|
|
36075
|
+
safe5(builtinRoot);
|
|
36076
|
+
if (!lstatSync20(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
35931
36077
|
unresolved("installed Gemini builtin root is missing");
|
|
35932
36078
|
for (const name of readdirSync7(builtinRoot).sort()) {
|
|
35933
|
-
const text2 = witness(
|
|
36079
|
+
const text2 = witness(join28(builtinRoot, name, "SKILL.md"));
|
|
35934
36080
|
const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
|
|
35935
36081
|
if (!match?.[1] || builtinNames.includes(match[1]))
|
|
35936
36082
|
unresolved("installed Gemini builtin name is missing or ambiguous");
|
|
@@ -35965,10 +36111,10 @@ function resolveAgentDiscovery(options) {
|
|
|
35965
36111
|
throw new Error("Discovery review must include the agent configuration source");
|
|
35966
36112
|
const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical8(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical8(configPath), source)) : sources;
|
|
35967
36113
|
const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
|
|
35968
|
-
if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(
|
|
36114
|
+
if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(join28(sep8, ".claude-plugin", "plugin.json")))
|
|
35969
36115
|
return source;
|
|
35970
|
-
const pluginRoot =
|
|
35971
|
-
const hookStat =
|
|
36116
|
+
const pluginRoot = dirname18(dirname18(source.path)), hooksPath = join28(pluginRoot, "hooks/hooks.json");
|
|
36117
|
+
const hookStat = lstatSync20(hooksPath, { throwIfNoEntry: false });
|
|
35972
36118
|
const text2 = read(source.path);
|
|
35973
36119
|
if (text2 === null)
|
|
35974
36120
|
throw new Error("Reviewed Claude plugin manifest is missing");
|
|
@@ -35982,8 +36128,8 @@ function resolveAgentDiscovery(options) {
|
|
|
35982
36128
|
if (manifest.hooks !== undefined) {
|
|
35983
36129
|
if (typeof manifest.hooks !== "string")
|
|
35984
36130
|
throw new Error("Reviewed Claude plugin manifest has an unsupported hooks target");
|
|
35985
|
-
const declaredHookPath =
|
|
35986
|
-
if (!declaredHookPath.startsWith(pluginRoot +
|
|
36131
|
+
const declaredHookPath = resolve19(pluginRoot, manifest.hooks);
|
|
36132
|
+
if (!declaredHookPath.startsWith(pluginRoot + sep8))
|
|
35987
36133
|
throw new Error("Reviewed Claude plugin hooks target escapes its root");
|
|
35988
36134
|
reviewedHookPaths.add(declaredHookPath);
|
|
35989
36135
|
}
|
|
@@ -36000,40 +36146,40 @@ function resolveAgentDiscovery(options) {
|
|
|
36000
36146
|
}
|
|
36001
36147
|
function plugin(root) {
|
|
36002
36148
|
root = canonical8(root);
|
|
36003
|
-
|
|
36004
|
-
const manifestPath =
|
|
36149
|
+
safe5(root);
|
|
36150
|
+
const manifestPath = join28(root, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
36005
36151
|
const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
|
|
36006
36152
|
if (raw === null)
|
|
36007
36153
|
unresolved("plugin manifest missing");
|
|
36008
36154
|
const manifest = parseConfig(raw, manifestPath);
|
|
36009
|
-
const hooks = witness(
|
|
36155
|
+
const hooks = witness(join28(root, "hooks/hooks.json"));
|
|
36010
36156
|
if (manifest.hooks !== undefined || hooks !== null)
|
|
36011
36157
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
36012
|
-
const commands =
|
|
36013
|
-
|
|
36014
|
-
if (agent === "claude" && (manifest.commands !== undefined ||
|
|
36158
|
+
const commands = join28(root, "commands");
|
|
36159
|
+
safe5(commands);
|
|
36160
|
+
if (agent === "claude" && (manifest.commands !== undefined || lstatSync20(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(commands).length))
|
|
36015
36161
|
unresolved("legacy command discovery requires retirement or a dedicated format adapter");
|
|
36016
|
-
const rootSkill = witness(
|
|
36162
|
+
const rootSkill = witness(join28(root, "SKILL.md"));
|
|
36017
36163
|
if (rootSkill !== null)
|
|
36018
36164
|
roots.add(root);
|
|
36019
36165
|
const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
36020
36166
|
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
36021
36167
|
unresolved("unsupported plugin skill paths");
|
|
36022
36168
|
if (agent === "claude")
|
|
36023
|
-
roots.add(
|
|
36169
|
+
roots.add(join28(root, "skills"));
|
|
36024
36170
|
for (const value of declared) {
|
|
36025
|
-
const path =
|
|
36026
|
-
if (path !== root && !path.startsWith(root +
|
|
36171
|
+
const path = resolve19(root, value);
|
|
36172
|
+
if (path !== root && !path.startsWith(root + sep8))
|
|
36027
36173
|
unresolved("plugin skill path escapes its root");
|
|
36028
|
-
roots.add(path.endsWith(`${
|
|
36174
|
+
roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname18(path) : path);
|
|
36029
36175
|
}
|
|
36030
36176
|
}
|
|
36031
36177
|
if (agent === "claude") {
|
|
36032
|
-
const registrations = witness(
|
|
36033
|
-
const installed = registrations === null ? {} : parseConfig(registrations,
|
|
36034
|
-
const commands = canonical8(
|
|
36035
|
-
|
|
36036
|
-
if (
|
|
36178
|
+
const registrations = witness(join28(home, ".claude/plugins/installed_plugins.json"));
|
|
36179
|
+
const installed = registrations === null ? {} : parseConfig(registrations, join28(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
36180
|
+
const commands = canonical8(join28(home, ".claude/commands"));
|
|
36181
|
+
safe5(commands);
|
|
36182
|
+
if (lstatSync20(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync7(commands).length)
|
|
36037
36183
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
36038
36184
|
for (const [id, enabled] of Object.entries(config2.enabledPlugins ?? {})) {
|
|
36039
36185
|
if (enabled === false)
|
|
@@ -36053,37 +36199,37 @@ function resolveAgentDiscovery(options) {
|
|
|
36053
36199
|
unresolved("unsupported plugin enablement");
|
|
36054
36200
|
const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
|
|
36055
36201
|
const market = config2.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
36056
|
-
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !
|
|
36202
|
+
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute18(market.source))
|
|
36057
36203
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
36058
|
-
const catalogText = witness(
|
|
36204
|
+
const catalogText = witness(join28(market.source, ".agents/plugins/marketplace.json"));
|
|
36059
36205
|
if (catalogText === null)
|
|
36060
36206
|
unresolved("marketplace catalog missing");
|
|
36061
|
-
const catalog = parseConfig(catalogText,
|
|
36207
|
+
const catalog = parseConfig(catalogText, join28(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
36062
36208
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
36063
36209
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
36064
|
-
plugin(
|
|
36210
|
+
plugin(resolve19(market.source, matches[0].source.path));
|
|
36065
36211
|
}
|
|
36066
36212
|
} else if (agent === "opencode") {
|
|
36067
|
-
if (witness(
|
|
36213
|
+
if (witness(join28(home, ".config/opencode/opencode.jsonc")) !== null || witness(join28(home, ".config/opencode/config.json")) !== null)
|
|
36068
36214
|
unresolved("additional config layers require review");
|
|
36069
36215
|
if ((config2.plugin?.length ?? 0) > 0 || config2.skills?.urls?.length)
|
|
36070
36216
|
unresolved("external plugin or remote skill source");
|
|
36071
36217
|
for (const path of config2.skills?.paths ?? []) {
|
|
36072
|
-
if (typeof path !== "string" || !
|
|
36218
|
+
if (typeof path !== "string" || !isAbsolute18(path))
|
|
36073
36219
|
unresolved("relative or malformed added skill root");
|
|
36074
36220
|
roots.add(path);
|
|
36075
36221
|
}
|
|
36076
|
-
const localPlugins =
|
|
36077
|
-
|
|
36078
|
-
if (
|
|
36222
|
+
const localPlugins = join28(home, ".config/opencode/plugins");
|
|
36223
|
+
safe5(localPlugins);
|
|
36224
|
+
if (lstatSync20(localPlugins, { throwIfNoEntry: false }) && readdirSync7(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
36079
36225
|
unresolved("additional local plugin hooks require review");
|
|
36080
36226
|
} else if (agent === "sumi") {
|
|
36081
|
-
if ((config2.plugin?.length ?? 0) > 0 || (config2.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical8(configPath) && source.path !== canonical8(
|
|
36227
|
+
if ((config2.plugin?.length ?? 0) > 0 || (config2.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical8(configPath) && source.path !== canonical8(join28(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
|
|
36082
36228
|
unresolved("external or foreign local plugins require review");
|
|
36083
36229
|
} else if (agent === "hermes") {
|
|
36084
|
-
for (const directory of [
|
|
36085
|
-
|
|
36086
|
-
if (
|
|
36230
|
+
for (const directory of [join28(home, ".hermes/plugins"), join28(home, ".hermes/hermes-agent")]) {
|
|
36231
|
+
safe5(directory);
|
|
36232
|
+
if (lstatSync20(directory, { throwIfNoEntry: false }))
|
|
36087
36233
|
unresolved("installed runtime/plugin discovery requires reviewed source bindings");
|
|
36088
36234
|
}
|
|
36089
36235
|
if (Bun.which("hermes") || Object.keys(config2.plugins ?? {}).length)
|
|
@@ -36091,24 +36237,24 @@ function resolveAgentDiscovery(options) {
|
|
|
36091
36237
|
} else if (agent === "gemini") {
|
|
36092
36238
|
if (Object.keys(config2.extensions ?? {}).length || config2.skills?.paths?.length)
|
|
36093
36239
|
unresolved("custom extension or skill paths");
|
|
36094
|
-
const directory =
|
|
36095
|
-
|
|
36096
|
-
if (
|
|
36240
|
+
const directory = join28(home, ".gemini/extensions");
|
|
36241
|
+
safe5(directory);
|
|
36242
|
+
if (lstatSync20(directory, { throwIfNoEntry: false }))
|
|
36097
36243
|
for (const name of readdirSync7(directory)) {
|
|
36098
|
-
const root =
|
|
36099
|
-
|
|
36100
|
-
if (!
|
|
36244
|
+
const root = join28(directory, name);
|
|
36245
|
+
safe5(root);
|
|
36246
|
+
if (!lstatSync20(root)?.isDirectory())
|
|
36101
36247
|
continue;
|
|
36102
|
-
const raw = witness(
|
|
36248
|
+
const raw = witness(join28(root, "gemini-extension.json"));
|
|
36103
36249
|
if (raw === null)
|
|
36104
36250
|
unresolved("extension registration missing");
|
|
36105
|
-
const manifest = parseConfig(raw,
|
|
36106
|
-
if (manifest.hooks !== undefined || witness(
|
|
36251
|
+
const manifest = parseConfig(raw, join28(root, "gemini-extension.json"));
|
|
36252
|
+
if (manifest.hooks !== undefined || witness(join28(root, "hooks/hooks.json")) !== null || witness(join28(root, ".gemini-extension-install.json")) !== null)
|
|
36107
36253
|
unresolved("extension hooks or linked registration require review");
|
|
36108
|
-
roots.add(
|
|
36254
|
+
roots.add(join28(root, "skills"));
|
|
36109
36255
|
}
|
|
36110
36256
|
}
|
|
36111
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([
|
|
36257
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join28(home, ".hermes/plugins"), join28(home, ".hermes/hermes-agent")].map((path) => canonical8(path))) } : {} };
|
|
36112
36258
|
}
|
|
36113
36259
|
var DISCOVERY_RUNTIME_COMMANDS, realpathOrNull = (path) => {
|
|
36114
36260
|
try {
|
|
@@ -36116,7 +36262,7 @@ var DISCOVERY_RUNTIME_COMMANDS, realpathOrNull = (path) => {
|
|
|
36116
36262
|
} catch {
|
|
36117
36263
|
return null;
|
|
36118
36264
|
}
|
|
36119
|
-
}, digest = (text2) =>
|
|
36265
|
+
}, digest = (text2) => createHash20("sha256").update(text2).digest("hex");
|
|
36120
36266
|
var init_agent_discovery = __esm(() => {
|
|
36121
36267
|
init_sumi_settings_witness();
|
|
36122
36268
|
init_agent_policy_limits();
|
|
@@ -36136,16 +36282,17 @@ var init_agent_discovery = __esm(() => {
|
|
|
36136
36282
|
init_agent_sumi();
|
|
36137
36283
|
init_native_discovery_roots();
|
|
36138
36284
|
init_codex_native_compatibility();
|
|
36285
|
+
init_retained_review_artifacts();
|
|
36139
36286
|
init_agent_discovery_directories();
|
|
36140
36287
|
DISCOVERY_RUNTIME_COMMANDS = Object.freeze({ gemini: "gemini" });
|
|
36141
36288
|
});
|
|
36142
36289
|
|
|
36143
36290
|
// src/lib/authoring-path.ts
|
|
36144
|
-
import { lstatSync as
|
|
36291
|
+
import { lstatSync as lstatSync21, realpathSync as realpathSync7 } from "fs";
|
|
36145
36292
|
import { homedir as homedir3 } from "os";
|
|
36146
|
-
import { basename as basename10, dirname as
|
|
36293
|
+
import { basename as basename10, dirname as dirname19, join as join29, resolve as resolve20, sep as sep9 } from "path";
|
|
36147
36294
|
function pathParts(path) {
|
|
36148
|
-
return
|
|
36295
|
+
return resolve20(path).split(sep9).filter(Boolean);
|
|
36149
36296
|
}
|
|
36150
36297
|
function nativeRootMatch(path) {
|
|
36151
36298
|
const parts = pathParts(path);
|
|
@@ -36159,24 +36306,24 @@ function nativeRootMatch(path) {
|
|
|
36159
36306
|
return;
|
|
36160
36307
|
}
|
|
36161
36308
|
function physicalPath(path) {
|
|
36162
|
-
let cursor =
|
|
36309
|
+
let cursor = resolve20(path);
|
|
36163
36310
|
const suffix = [];
|
|
36164
|
-
while (!
|
|
36311
|
+
while (!lstatSync21(cursor, { throwIfNoEntry: false })) {
|
|
36165
36312
|
suffix.unshift(basename10(cursor));
|
|
36166
|
-
const parent =
|
|
36313
|
+
const parent = dirname19(cursor);
|
|
36167
36314
|
if (parent === cursor)
|
|
36168
|
-
return
|
|
36315
|
+
return resolve20(path);
|
|
36169
36316
|
cursor = parent;
|
|
36170
36317
|
}
|
|
36171
|
-
return
|
|
36318
|
+
return resolve20(realpathSync7(cursor), ...suffix);
|
|
36172
36319
|
}
|
|
36173
36320
|
function contains(root, path) {
|
|
36174
|
-
return path === root || path.startsWith(root +
|
|
36321
|
+
return path === root || path.startsWith(root + sep9);
|
|
36175
36322
|
}
|
|
36176
36323
|
function configuredRoots(home, explicitHome) {
|
|
36177
|
-
const homeData =
|
|
36324
|
+
const homeData = join29(home, ".hasna", "skills");
|
|
36178
36325
|
const homePolicy = readManagedSkillPolicySnapshot(homeData);
|
|
36179
|
-
const configuredPolicy = !explicitHome &&
|
|
36326
|
+
const configuredPolicy = !explicitHome && resolve20(getDataDirReadOnly()) !== resolve20(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
|
|
36180
36327
|
const policy = homePolicy ?? configuredPolicy;
|
|
36181
36328
|
const bridge = policy?.value.bridge;
|
|
36182
36329
|
if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
|
|
@@ -36205,8 +36352,8 @@ function configuredRoots(home, explicitHome) {
|
|
|
36205
36352
|
admitBinding(binding);
|
|
36206
36353
|
continue;
|
|
36207
36354
|
}
|
|
36208
|
-
const nativeHome =
|
|
36209
|
-
if (!
|
|
36355
|
+
const nativeHome = join29(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
|
|
36356
|
+
if (!lstatSync21(nativeHome, { throwIfNoEntry: false }))
|
|
36210
36357
|
continue;
|
|
36211
36358
|
roots.push(...resolveAgentDiscovery({ home, agent, canonical: physicalPath }).roots);
|
|
36212
36359
|
}
|
|
@@ -36215,22 +36362,22 @@ function configuredRoots(home, explicitHome) {
|
|
|
36215
36362
|
function assertPortableAuthoringPath(path, options = {}) {
|
|
36216
36363
|
if (typeof path !== "string" || !path || path.includes("\x00"))
|
|
36217
36364
|
throw new Error("Invalid portable authoring path");
|
|
36218
|
-
const lexical =
|
|
36365
|
+
const lexical = resolve20(path);
|
|
36219
36366
|
const physical = physicalPath(lexical);
|
|
36220
36367
|
const candidates = [lexical, physical];
|
|
36221
36368
|
let native = candidates.map(nativeRootMatch).find(Boolean);
|
|
36222
|
-
const home =
|
|
36223
|
-
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) =>
|
|
36224
|
-
defaults.push(
|
|
36369
|
+
const home = resolve20(options.homeDir ?? homedir3());
|
|
36370
|
+
const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join29(home, ...parts));
|
|
36371
|
+
defaults.push(join29(home, ".codex/plugins/cache"), join29(home, ".claude/plugins/cache"), join29(home, ".claude/plugins/synced"), join29(home, ".gemini/extensions"));
|
|
36225
36372
|
for (const root of defaults) {
|
|
36226
|
-
if (candidates.some((candidate) => contains(
|
|
36373
|
+
if (candidates.some((candidate) => contains(resolve20(root), candidate) || contains(physicalPath(root), candidate)))
|
|
36227
36374
|
native ??= root;
|
|
36228
36375
|
}
|
|
36229
36376
|
if (native) {
|
|
36230
36377
|
throw new Error(`Refusing portable Skills authoring below native discovery root '${native}': ${lexical}. Set HASNA_SKILLS_DIR to a private authoring corpus outside native roots.`);
|
|
36231
36378
|
}
|
|
36232
36379
|
for (const root of configuredRoots(home, options.homeDir !== undefined)) {
|
|
36233
|
-
if (candidates.some((candidate) => contains(
|
|
36380
|
+
if (candidates.some((candidate) => contains(resolve20(root), candidate) || contains(physicalPath(root), candidate))) {
|
|
36234
36381
|
throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
|
|
36235
36382
|
}
|
|
36236
36383
|
}
|
|
@@ -36248,7 +36395,7 @@ var init_authoring_path = __esm(() => {
|
|
|
36248
36395
|
// src/lib/portable-skills.ts
|
|
36249
36396
|
import {
|
|
36250
36397
|
existsSync as existsSync12,
|
|
36251
|
-
mkdirSync as
|
|
36398
|
+
mkdirSync as mkdirSync8,
|
|
36252
36399
|
mkdtempSync,
|
|
36253
36400
|
readdirSync as readdirSync8,
|
|
36254
36401
|
realpathSync as realpathSync8,
|
|
@@ -36257,22 +36404,22 @@ import {
|
|
|
36257
36404
|
statSync as statSync8,
|
|
36258
36405
|
writeFileSync as writeFileSync6
|
|
36259
36406
|
} from "fs";
|
|
36260
|
-
import { basename as basename11, dirname as
|
|
36407
|
+
import { basename as basename11, dirname as dirname20, isAbsolute as isAbsolute19, join as join30, normalize as normalize4, relative as relative6, resolve as resolve21, sep as sep10 } from "path";
|
|
36261
36408
|
function getPortableSkillsRoot(options = {}) {
|
|
36262
36409
|
return resolvePortableSkillsRoot(options, true);
|
|
36263
36410
|
}
|
|
36264
36411
|
function resolvePortableSkillsRoot(options, migrate) {
|
|
36265
36412
|
if (options.rootDir)
|
|
36266
36413
|
return options.rootDir;
|
|
36267
|
-
const appDir = options.homeDir ?
|
|
36268
|
-
const cache =
|
|
36414
|
+
const appDir = options.homeDir ? join30(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
|
|
36415
|
+
const cache = join30(appDir, SKILLS_CACHE_DIRNAME);
|
|
36269
36416
|
if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
|
|
36270
36417
|
return cache;
|
|
36271
|
-
const installed =
|
|
36418
|
+
const installed = join30(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
36272
36419
|
return installed;
|
|
36273
36420
|
}
|
|
36274
36421
|
function getPortableSkillPath(name, options = {}) {
|
|
36275
|
-
return
|
|
36422
|
+
return join30(getPortableSkillsRoot(options), normalizePortableSkillName(name));
|
|
36276
36423
|
}
|
|
36277
36424
|
function findPortableSkill(name, options = {}) {
|
|
36278
36425
|
let normalized;
|
|
@@ -36298,7 +36445,7 @@ function listPortableSkills(options = {}) {
|
|
|
36298
36445
|
for (const entry of readdirSync8(root).sort()) {
|
|
36299
36446
|
if (entry.startsWith("."))
|
|
36300
36447
|
continue;
|
|
36301
|
-
const path =
|
|
36448
|
+
const path = join30(root, entry);
|
|
36302
36449
|
if (!safeIsDirectory(path))
|
|
36303
36450
|
continue;
|
|
36304
36451
|
try {
|
|
@@ -36331,9 +36478,9 @@ function isOfficialSkillName(name) {
|
|
|
36331
36478
|
function scaffoldPortableSkill(name, options = {}) {
|
|
36332
36479
|
const skillName = normalizeNewPortableSkillName(name);
|
|
36333
36480
|
const readOnlyRoot = resolvePortableSkillsRoot(options, false);
|
|
36334
|
-
assertPortableAuthoringPath(
|
|
36481
|
+
assertPortableAuthoringPath(join30(readOnlyRoot, skillName), options);
|
|
36335
36482
|
const root = getPortableSkillsRoot(options);
|
|
36336
|
-
const skillPath =
|
|
36483
|
+
const skillPath = join30(root, skillName);
|
|
36337
36484
|
assertPortableAuthoringPath(skillPath, options);
|
|
36338
36485
|
if (existsSync12(skillPath)) {
|
|
36339
36486
|
if (!options.overwrite)
|
|
@@ -36352,27 +36499,27 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
36352
36499
|
return { name: skillName, path: skillPath, manifest, created: true };
|
|
36353
36500
|
}
|
|
36354
36501
|
function physicalImportPath(path) {
|
|
36355
|
-
let ancestor =
|
|
36502
|
+
let ancestor = resolve21(path);
|
|
36356
36503
|
const suffix = [];
|
|
36357
36504
|
while (!existsSync12(ancestor)) {
|
|
36358
36505
|
suffix.unshift(basename11(ancestor));
|
|
36359
|
-
ancestor =
|
|
36506
|
+
ancestor = dirname20(ancestor);
|
|
36360
36507
|
}
|
|
36361
|
-
return
|
|
36508
|
+
return join30(realpathSync8(ancestor), ...suffix);
|
|
36362
36509
|
}
|
|
36363
36510
|
function assertDisjointImportPaths(source, destination) {
|
|
36364
36511
|
const sourcePath = realpathSync8(source);
|
|
36365
36512
|
const destinationPath = physicalImportPath(destination);
|
|
36366
36513
|
const contains2 = (parent, child2) => {
|
|
36367
36514
|
const path = relative6(parent, child2);
|
|
36368
|
-
return path === "" || !
|
|
36515
|
+
return path === "" || !isAbsolute19(path) && path !== ".." && !path.startsWith(`..${sep10}`);
|
|
36369
36516
|
};
|
|
36370
36517
|
if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
|
|
36371
36518
|
throw new Error("Source and destination skill directories must not overlap");
|
|
36372
36519
|
}
|
|
36373
36520
|
}
|
|
36374
36521
|
function portPortableSkill(sourcePath, options = {}) {
|
|
36375
|
-
const absoluteSource =
|
|
36522
|
+
const absoluteSource = normalize4(sourcePath);
|
|
36376
36523
|
if (!existsSync12(absoluteSource) || !statSync8(absoluteSource).isDirectory()) {
|
|
36377
36524
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
36378
36525
|
}
|
|
@@ -36384,11 +36531,11 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
36384
36531
|
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.`;
|
|
36385
36532
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
36386
36533
|
}
|
|
36387
|
-
const readOnlyDestination =
|
|
36534
|
+
const readOnlyDestination = join30(resolvePortableSkillsRoot(options, false), skillName);
|
|
36388
36535
|
assertPortableAuthoringPath(readOnlyDestination, options);
|
|
36389
36536
|
assertDisjointImportPaths(absoluteSource, readOnlyDestination);
|
|
36390
36537
|
const root = getPortableSkillsRoot(options);
|
|
36391
|
-
const destination =
|
|
36538
|
+
const destination = join30(root, skillName);
|
|
36392
36539
|
assertPortableAuthoringPath(destination, options);
|
|
36393
36540
|
assertDisjointImportPaths(absoluteSource, destination);
|
|
36394
36541
|
if (existsSync12(destination)) {
|
|
@@ -36396,7 +36543,7 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
36396
36543
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
36397
36544
|
rmSync3(destination, { recursive: true, force: true });
|
|
36398
36545
|
}
|
|
36399
|
-
|
|
36546
|
+
mkdirSync8(dirname20(destination), { recursive: true });
|
|
36400
36547
|
copySkillDirectory(absoluteSource, destination);
|
|
36401
36548
|
const base = {
|
|
36402
36549
|
...inferred,
|
|
@@ -36420,8 +36567,8 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
36420
36567
|
const warnings = [...base.warnings];
|
|
36421
36568
|
let manifest;
|
|
36422
36569
|
if (existsSync12(skillPath)) {
|
|
36423
|
-
const skillJsonPath =
|
|
36424
|
-
const skillMdPath =
|
|
36570
|
+
const skillJsonPath = join30(skillPath, "skill.json");
|
|
36571
|
+
const skillMdPath = join30(skillPath, "SKILL.md");
|
|
36425
36572
|
if (!existsSync12(skillJsonPath) && !existsSync12(skillMdPath)) {
|
|
36426
36573
|
add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
|
|
36427
36574
|
}
|
|
@@ -36441,7 +36588,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
36441
36588
|
add3(issues, "portable.version_missing", "Portable manifest missing version");
|
|
36442
36589
|
}
|
|
36443
36590
|
const contractIssues = validatePortableManifestContract(manifest, {
|
|
36444
|
-
strict: existsSync12(
|
|
36591
|
+
strict: existsSync12(join30(skillPath, "skill.json")),
|
|
36445
36592
|
skillPath
|
|
36446
36593
|
});
|
|
36447
36594
|
for (const issue2 of contractIssues)
|
|
@@ -36481,7 +36628,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
36481
36628
|
add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
|
|
36482
36629
|
continue;
|
|
36483
36630
|
}
|
|
36484
|
-
const entryPath =
|
|
36631
|
+
const entryPath = join30(skillPath, command.entry);
|
|
36485
36632
|
if (!existsSync12(entryPath))
|
|
36486
36633
|
add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
|
|
36487
36634
|
else if (statSync8(entryPath).isDirectory())
|
|
@@ -36492,7 +36639,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
36492
36639
|
} catch (error61) {
|
|
36493
36640
|
add3(issues, "portable.manifest_invalid", error61.message);
|
|
36494
36641
|
}
|
|
36495
|
-
if (manifest?.kind !== "instruction" && !existsSync12(
|
|
36642
|
+
if (manifest?.kind !== "instruction" && !existsSync12(join30(skillPath, "AGENTS.md"))) {
|
|
36496
36643
|
add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
|
|
36497
36644
|
}
|
|
36498
36645
|
}
|
|
@@ -36530,9 +36677,9 @@ function safeIsDirectory(path) {
|
|
|
36530
36677
|
}
|
|
36531
36678
|
}
|
|
36532
36679
|
function isSafeRelativePath2(value) {
|
|
36533
|
-
if (!value.trim() ||
|
|
36680
|
+
if (!value.trim() || isAbsolute19(value))
|
|
36534
36681
|
return false;
|
|
36535
|
-
const normalized =
|
|
36682
|
+
const normalized = normalize4(value).replace(/\\/g, "/");
|
|
36536
36683
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
36537
36684
|
}
|
|
36538
36685
|
function add3(target, code, message) {
|
|
@@ -36720,7 +36867,7 @@ var init_search = __esm(() => {
|
|
|
36720
36867
|
|
|
36721
36868
|
// src/lib/registry.ts
|
|
36722
36869
|
import { existsSync as existsSync13, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
|
|
36723
|
-
import { join as
|
|
36870
|
+
import { join as join31 } from "path";
|
|
36724
36871
|
function parseSkillMdFrontmatter(content) {
|
|
36725
36872
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
36726
36873
|
if (!match)
|
|
@@ -36761,7 +36908,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
36761
36908
|
for (const entry of entries) {
|
|
36762
36909
|
if (!entry.isDirectory())
|
|
36763
36910
|
continue;
|
|
36764
|
-
const skillMdPath =
|
|
36911
|
+
const skillMdPath = join31(dir, entry.name, "SKILL.md");
|
|
36765
36912
|
if (!existsSync13(skillMdPath))
|
|
36766
36913
|
continue;
|
|
36767
36914
|
let content;
|
|
@@ -36781,7 +36928,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
36781
36928
|
category: fm.category || "Development Tools",
|
|
36782
36929
|
tags: fm.tags || [],
|
|
36783
36930
|
...fm.kind ? { kind: fm.kind } : {},
|
|
36784
|
-
...isHostedMetadataSkillDir(
|
|
36931
|
+
...isHostedMetadataSkillDir(join31(dir, entry.name)) ? { serverOwned: true } : {},
|
|
36785
36932
|
source
|
|
36786
36933
|
});
|
|
36787
36934
|
}
|
|
@@ -36797,8 +36944,8 @@ function findExtensionSkillPath(name) {
|
|
|
36797
36944
|
for (const entry of entries) {
|
|
36798
36945
|
if (!entry.isDirectory())
|
|
36799
36946
|
continue;
|
|
36800
|
-
const skillDir =
|
|
36801
|
-
const skillMdPath =
|
|
36947
|
+
const skillDir = join31(config2.extensionsDir, entry.name);
|
|
36948
|
+
const skillMdPath = join31(skillDir, "SKILL.md");
|
|
36802
36949
|
if (!existsSync13(skillMdPath))
|
|
36803
36950
|
continue;
|
|
36804
36951
|
let content;
|
|
@@ -37215,7 +37362,7 @@ var init_v4 = __esm(() => {
|
|
|
37215
37362
|
});
|
|
37216
37363
|
|
|
37217
37364
|
// src/lib/canonical-json.ts
|
|
37218
|
-
import { createHash as
|
|
37365
|
+
import { createHash as createHash21 } from "crypto";
|
|
37219
37366
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
37220
37367
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
37221
37368
|
}
|
|
@@ -37259,7 +37406,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
37259
37406
|
return visit3(value, 0);
|
|
37260
37407
|
}
|
|
37261
37408
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
37262
|
-
return
|
|
37409
|
+
return createHash21("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
37263
37410
|
}
|
|
37264
37411
|
var init_canonical_json = () => {};
|
|
37265
37412
|
|
|
@@ -37602,7 +37749,7 @@ function invitationFailure(value, status) {
|
|
|
37602
37749
|
function invitationInput(action, input2) {
|
|
37603
37750
|
if (!record4(input2))
|
|
37604
37751
|
return inputFailure();
|
|
37605
|
-
const
|
|
37752
|
+
const keys2 = {
|
|
37606
37753
|
list: ["after"],
|
|
37607
37754
|
get: ["invitationId"],
|
|
37608
37755
|
issue: ["email", "role", "idempotencyKey", "confirm"],
|
|
@@ -37610,7 +37757,7 @@ function invitationInput(action, input2) {
|
|
|
37610
37757
|
revoke: ["invitationId", "expectedGeneration", "confirm"],
|
|
37611
37758
|
accept: ["invitationId", "token", "confirm"]
|
|
37612
37759
|
};
|
|
37613
|
-
if (Object.keys(input2).some((key) => !
|
|
37760
|
+
if (Object.keys(input2).some((key) => !keys2[action].includes(key)) || action !== "list" && keys2[action].some((key) => !Object.hasOwn(input2, key)))
|
|
37614
37761
|
return inputFailure();
|
|
37615
37762
|
const value = { ...input2 };
|
|
37616
37763
|
if (action === "list") {
|
|
@@ -38009,14 +38156,14 @@ var init_remote_workspace_leave = __esm(() => {
|
|
|
38009
38156
|
});
|
|
38010
38157
|
|
|
38011
38158
|
// src/lib/auth-store.ts
|
|
38012
|
-
import { chmodSync, existsSync as existsSync14, mkdirSync as
|
|
38013
|
-
import { basename as basename12, dirname as
|
|
38159
|
+
import { chmodSync, existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync18, renameSync as renameSync4, statSync as statSync9, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
38160
|
+
import { basename as basename12, dirname as dirname21, join as join32 } from "path";
|
|
38014
38161
|
function getAuthFilePath(env = process.env) {
|
|
38015
38162
|
return skillsCredentialFilePath(env);
|
|
38016
38163
|
}
|
|
38017
38164
|
function getIdentityFilePath(env = process.env) {
|
|
38018
38165
|
const file2 = skillsCredentialFilePath(env);
|
|
38019
|
-
return
|
|
38166
|
+
return join32(dirname21(file2), basename12(file2).replace(/^credentials/, "identity") + ".json");
|
|
38020
38167
|
}
|
|
38021
38168
|
function getApiUrl(action, env = process.env, options = {}) {
|
|
38022
38169
|
return requireSkillsApiOrigin(action, env, options);
|
|
@@ -38098,15 +38245,15 @@ function runQuoteReceipt(value) {
|
|
|
38098
38245
|
return value;
|
|
38099
38246
|
}
|
|
38100
38247
|
function parseRemoteRunQuote(value) {
|
|
38101
|
-
const quote3 =
|
|
38248
|
+
const quote3 = object6(value);
|
|
38102
38249
|
runQuoteReceipt(quote3.quoteReceipt);
|
|
38103
|
-
const pricing =
|
|
38250
|
+
const pricing = object6(quote3.pricing);
|
|
38104
38251
|
if (typeof quote3.skill !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(quote3.skill))
|
|
38105
38252
|
throw new Error("Invalid quoted skill");
|
|
38106
38253
|
const costCents = creditCount(pricing.costCredits ?? pricing.costCents);
|
|
38107
38254
|
if (pricing.costCredits !== undefined && pricing.costCents !== undefined && pricing.costCents !== costCents)
|
|
38108
38255
|
throw new Error("Inconsistent quoted credit count");
|
|
38109
|
-
if (quote3.availability &&
|
|
38256
|
+
if (quote3.availability && object6(quote3.availability).status !== "available")
|
|
38110
38257
|
throw new Error("This skill is unavailable for remote execution");
|
|
38111
38258
|
return { ...quote3, skill: quote3.skill, pricing: { ...pricing, costCredits: costCents, costCents, formattedCost: `${costCents} credits` } };
|
|
38112
38259
|
}
|
|
@@ -38115,7 +38262,7 @@ function parseRemoteCreditPacks(value) {
|
|
|
38115
38262
|
throw new Error("Invalid credit pack response");
|
|
38116
38263
|
const ids = new Set;
|
|
38117
38264
|
return value.map((value2) => {
|
|
38118
|
-
const row =
|
|
38265
|
+
const row = object6(value2);
|
|
38119
38266
|
const counts = [row.credits, row.creditsCents, row.amountCents].filter((value3) => value3 !== undefined).map(creditCount);
|
|
38120
38267
|
if (!counts.length || counts[0] === 0 || counts.some((count) => count !== counts[0]))
|
|
38121
38268
|
throw new Error("Inconsistent credit pack counts");
|
|
@@ -38130,7 +38277,7 @@ function parseRemoteCreditPacks(value) {
|
|
|
38130
38277
|
});
|
|
38131
38278
|
}
|
|
38132
38279
|
function parseRemoteBillingStatus(value) {
|
|
38133
|
-
const row =
|
|
38280
|
+
const row = object6(value);
|
|
38134
38281
|
const counts = [row.creditBalance, row.balanceCents].filter((value2) => value2 !== undefined).map(creditCount);
|
|
38135
38282
|
if (!counts.length || counts.some((count) => count !== counts[0]))
|
|
38136
38283
|
throw new Error("Inconsistent credit balance");
|
|
@@ -38142,7 +38289,7 @@ function parseRemoteBillingStatus(value) {
|
|
|
38142
38289
|
};
|
|
38143
38290
|
}
|
|
38144
38291
|
function parseRemoteCheckout(value) {
|
|
38145
|
-
const row =
|
|
38292
|
+
const row = object6(value);
|
|
38146
38293
|
if (typeof row.url !== "string")
|
|
38147
38294
|
throw new Error("Invalid checkout URL");
|
|
38148
38295
|
const url2 = new URL(row.url);
|
|
@@ -38150,7 +38297,7 @@ function parseRemoteCheckout(value) {
|
|
|
38150
38297
|
throw new Error("Invalid checkout URL");
|
|
38151
38298
|
return { url: row.url };
|
|
38152
38299
|
}
|
|
38153
|
-
function
|
|
38300
|
+
function object6(value) {
|
|
38154
38301
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
38155
38302
|
throw new Error("Invalid Skills server response");
|
|
38156
38303
|
return value;
|
|
@@ -38244,10 +38391,10 @@ async function readQuoteUnavailableCode(response) {
|
|
|
38244
38391
|
let timer;
|
|
38245
38392
|
let deadlineExceeded = false;
|
|
38246
38393
|
const expired = Symbol("quote body deadline");
|
|
38247
|
-
const deadline = new Promise((
|
|
38394
|
+
const deadline = new Promise((resolve22) => {
|
|
38248
38395
|
timer = setTimeout(() => {
|
|
38249
38396
|
deadlineExceeded = true;
|
|
38250
|
-
|
|
38397
|
+
resolve22(expired);
|
|
38251
38398
|
reader.cancel().catch(() => {});
|
|
38252
38399
|
}, 1500);
|
|
38253
38400
|
});
|
|
@@ -39390,8 +39537,8 @@ function invitationEmailIds(invitationId, challengeId) {
|
|
|
39390
39537
|
return { invitationId, challengeId };
|
|
39391
39538
|
}
|
|
39392
39539
|
function invitationEmailInput(action, input2) {
|
|
39393
|
-
const
|
|
39394
|
-
if (!record8(input2) || Object.keys(input2).length !==
|
|
39540
|
+
const keys2 = ["invitationId", "token", "challengeId", "confirm", ...action === "accept" ? ["code"] : []];
|
|
39541
|
+
if (!record8(input2) || Object.keys(input2).length !== keys2.length || keys2.some((key) => !Object.hasOwn(input2, key)))
|
|
39395
39542
|
throw new InvitationEmailInputError;
|
|
39396
39543
|
const value = { ...input2 };
|
|
39397
39544
|
const ids = invitationEmailIds(value.invitationId, value.challengeId);
|
|
@@ -39485,7 +39632,7 @@ var init_remote_invitation_recovery = __esm(() => {
|
|
|
39485
39632
|
});
|
|
39486
39633
|
|
|
39487
39634
|
// src/lib/remote-private-publications.ts
|
|
39488
|
-
import { createHash as
|
|
39635
|
+
import { createHash as createHash22 } from "crypto";
|
|
39489
39636
|
function checkedPublicationDeclaration(value) {
|
|
39490
39637
|
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)
|
|
39491
39638
|
return bad();
|
|
@@ -39730,12 +39877,12 @@ class RemotePrivatePublicationsClient {
|
|
|
39730
39877
|
previous = view;
|
|
39731
39878
|
if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
|
|
39732
39879
|
return view;
|
|
39733
|
-
await new Promise((
|
|
39880
|
+
await new Promise((resolve22) => {
|
|
39734
39881
|
const timer = setTimeout(done, Math.min(1000, until - Date.now()));
|
|
39735
39882
|
function done() {
|
|
39736
39883
|
clearTimeout(timer);
|
|
39737
39884
|
options.signal?.removeEventListener("abort", done);
|
|
39738
|
-
|
|
39885
|
+
resolve22();
|
|
39739
39886
|
}
|
|
39740
39887
|
options.signal?.addEventListener("abort", done, { once: true });
|
|
39741
39888
|
});
|
|
@@ -39746,7 +39893,7 @@ var PRIVATE_PUBLICATION_MAX_BYTES, failures2, PrivatePublicationError, bad = ()
|
|
|
39746
39893
|
throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
|
|
39747
39894
|
}, invalid4 = () => {
|
|
39748
39895
|
throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
|
|
39749
|
-
}, publicationUuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v,
|
|
39896
|
+
}, publicationUuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v, keys2) => record9(v) && Object.keys(v).sort().join(",") === keys2.sort().join(","), 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)), publicationSha256 = (bytes) => createHash22("sha256").update(bytes).digest("hex");
|
|
39750
39897
|
var init_remote_private_publications = __esm(() => {
|
|
39751
39898
|
init_fleet_credentials();
|
|
39752
39899
|
init_remote_workspace_selection();
|
|
@@ -40023,10 +40170,10 @@ var init_remote_auth = __esm(() => {
|
|
|
40023
40170
|
});
|
|
40024
40171
|
|
|
40025
40172
|
// src/lib/workspace-profile.ts
|
|
40026
|
-
import { constants as
|
|
40173
|
+
import { constants as constants14, closeSync as closeSync16, fstatSync as fstatSync16, lstatSync as lstatSync22, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, openSync as openSync16, readFileSync as readFileSync19, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
40027
40174
|
function stat2(path) {
|
|
40028
40175
|
try {
|
|
40029
|
-
return
|
|
40176
|
+
return lstatSync22(path);
|
|
40030
40177
|
} catch (error61) {
|
|
40031
40178
|
if (error61.code === "ENOENT")
|
|
40032
40179
|
return null;
|
|
@@ -40036,14 +40183,14 @@ function stat2(path) {
|
|
|
40036
40183
|
function safeText(file2) {
|
|
40037
40184
|
if (stat2(file2) === null)
|
|
40038
40185
|
return null;
|
|
40039
|
-
const fd =
|
|
40186
|
+
const fd = openSync16(file2, constants14.O_RDONLY | constants14.O_NOFOLLOW | constants14.O_NONBLOCK);
|
|
40040
40187
|
try {
|
|
40041
|
-
const s =
|
|
40188
|
+
const s = fstatSync16(fd);
|
|
40042
40189
|
if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
|
|
40043
40190
|
return fail("The selected profile must use bounded owner-only regular files.");
|
|
40044
40191
|
return readFileSync19(fd, "utf8");
|
|
40045
40192
|
} finally {
|
|
40046
|
-
|
|
40193
|
+
closeSync16(fd);
|
|
40047
40194
|
}
|
|
40048
40195
|
}
|
|
40049
40196
|
function checkIdentityMetadata(file2, identity3) {
|
|
@@ -40255,8 +40402,8 @@ var init_recurring_customer = __esm(() => {
|
|
|
40255
40402
|
});
|
|
40256
40403
|
|
|
40257
40404
|
// src/lib/recurring-recovery.ts
|
|
40258
|
-
import { constants as
|
|
40259
|
-
import { dirname as
|
|
40405
|
+
import { constants as constants15, closeSync as closeSync17, fsyncSync as fsyncSync5, fstatSync as fstatSync17, lstatSync as lstatSync23, mkdirSync as mkdirSync11, openSync as openSync17, readSync as readSync11, realpathSync as realpathSync9, renameSync as renameSync6, unlinkSync as unlinkSync5, writeFileSync as writeFileSync9 } from "fs";
|
|
40406
|
+
import { dirname as dirname22, isAbsolute as isAbsolute20, join as join33, resolve as resolve22 } from "path";
|
|
40260
40407
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
40261
40408
|
|
|
40262
40409
|
class RecoveryOwner {
|
|
@@ -40264,14 +40411,14 @@ class RecoveryOwner {
|
|
|
40264
40411
|
parents = [];
|
|
40265
40412
|
constructor(directory) {
|
|
40266
40413
|
this.directory = directory;
|
|
40267
|
-
if (!
|
|
40414
|
+
if (!isAbsolute20(directory) || resolve22(directory) !== directory || realpathSync9(directory) !== directory)
|
|
40268
40415
|
invalid5();
|
|
40269
|
-
for (let path = directory;; path =
|
|
40270
|
-
const stat3 =
|
|
40416
|
+
for (let path = directory;; path = dirname22(path)) {
|
|
40417
|
+
const stat3 = lstatSync23(path);
|
|
40271
40418
|
if (!stat3.isDirectory() || stat3.isSymbolicLink())
|
|
40272
40419
|
invalid5();
|
|
40273
40420
|
this.parents.push({ path, dev: stat3.dev, ino: stat3.ino });
|
|
40274
|
-
if (path ===
|
|
40421
|
+
if (path === dirname22(path))
|
|
40275
40422
|
break;
|
|
40276
40423
|
}
|
|
40277
40424
|
this.assert();
|
|
@@ -40280,59 +40427,59 @@ class RecoveryOwner {
|
|
|
40280
40427
|
if (realpathSync9(this.directory) !== this.directory)
|
|
40281
40428
|
invalid5();
|
|
40282
40429
|
for (const prior of this.parents) {
|
|
40283
|
-
const now =
|
|
40430
|
+
const now = lstatSync23(prior.path);
|
|
40284
40431
|
if (!now.isDirectory() || now.isSymbolicLink() || now.dev !== prior.dev || now.ino !== prior.ino)
|
|
40285
40432
|
invalid5();
|
|
40286
40433
|
}
|
|
40287
|
-
const own2 =
|
|
40434
|
+
const own2 = lstatSync23(this.directory);
|
|
40288
40435
|
if ((own2.mode & 511) !== 448 || (own2.mode & 3584) !== 0 || process.getuid && own2.uid !== process.getuid())
|
|
40289
40436
|
invalid5();
|
|
40290
40437
|
}
|
|
40291
40438
|
read(name) {
|
|
40292
40439
|
this.assert();
|
|
40293
|
-
const fd =
|
|
40440
|
+
const fd = openSync17(join33(this.directory, name), constants15.O_RDONLY | constants15.O_NOFOLLOW | constants15.O_NONBLOCK);
|
|
40294
40441
|
try {
|
|
40295
|
-
const before =
|
|
40442
|
+
const before = fstatSync17(fd);
|
|
40296
40443
|
if (!before.isFile() || before.nlink !== 1 || (before.mode & 4095) !== 384 || before.size < 1 || before.size > 8192 || process.getuid && before.uid !== process.getuid())
|
|
40297
40444
|
invalid5();
|
|
40298
40445
|
const bytes = Buffer.alloc(before.size + 1);
|
|
40299
40446
|
let size = 0;
|
|
40300
40447
|
while (size < bytes.length) {
|
|
40301
|
-
const n2 =
|
|
40448
|
+
const n2 = readSync11(fd, bytes, size, bytes.length - size, size);
|
|
40302
40449
|
if (!n2)
|
|
40303
40450
|
break;
|
|
40304
40451
|
size += n2;
|
|
40305
40452
|
}
|
|
40306
|
-
const after =
|
|
40453
|
+
const after = fstatSync17(fd), path = lstatSync23(join33(this.directory, name));
|
|
40307
40454
|
if (size !== before.size || after.size !== before.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || path.dev !== before.dev || path.ino !== before.ino || !path.isFile() || path.isSymbolicLink())
|
|
40308
40455
|
invalid5();
|
|
40309
40456
|
this.assert();
|
|
40310
40457
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size));
|
|
40311
40458
|
} finally {
|
|
40312
|
-
|
|
40459
|
+
closeSync17(fd);
|
|
40313
40460
|
}
|
|
40314
40461
|
}
|
|
40315
40462
|
write(name, value) {
|
|
40316
40463
|
this.assert();
|
|
40317
40464
|
const bytes = canonicalJson2(value, 8192) + `
|
|
40318
40465
|
`;
|
|
40319
|
-
const fd =
|
|
40466
|
+
const fd = openSync17(join33(this.directory, name), constants15.O_WRONLY | constants15.O_CREAT | constants15.O_EXCL | constants15.O_NOFOLLOW, 384);
|
|
40320
40467
|
try {
|
|
40321
40468
|
writeFileSync9(fd, bytes);
|
|
40322
|
-
|
|
40469
|
+
fsyncSync5(fd);
|
|
40323
40470
|
} finally {
|
|
40324
|
-
|
|
40471
|
+
closeSync17(fd);
|
|
40325
40472
|
}
|
|
40326
40473
|
this.assert();
|
|
40327
40474
|
this.sync();
|
|
40328
40475
|
}
|
|
40329
40476
|
sync() {
|
|
40330
40477
|
this.assert();
|
|
40331
|
-
const fd =
|
|
40478
|
+
const fd = openSync17(this.directory, constants15.O_RDONLY | constants15.O_NOFOLLOW);
|
|
40332
40479
|
try {
|
|
40333
|
-
|
|
40480
|
+
fsyncSync5(fd);
|
|
40334
40481
|
} finally {
|
|
40335
|
-
|
|
40482
|
+
closeSync17(fd);
|
|
40336
40483
|
}
|
|
40337
40484
|
}
|
|
40338
40485
|
saveState(state, expected) {
|
|
@@ -40343,30 +40490,30 @@ class RecoveryOwner {
|
|
|
40343
40490
|
if (this.read("state.json") !== expected)
|
|
40344
40491
|
invalid5();
|
|
40345
40492
|
this.assert();
|
|
40346
|
-
renameSync6(
|
|
40493
|
+
renameSync6(join33(this.directory, name), join33(this.directory, "state.json"));
|
|
40347
40494
|
this.sync();
|
|
40348
40495
|
}
|
|
40349
40496
|
async locked(action) {
|
|
40350
40497
|
this.assert();
|
|
40351
40498
|
let fd;
|
|
40352
40499
|
try {
|
|
40353
|
-
fd =
|
|
40500
|
+
fd = openSync17(join33(this.directory, "operation.lock"), constants15.O_WRONLY | constants15.O_CREAT | constants15.O_EXCL | constants15.O_NOFOLLOW, 384);
|
|
40354
40501
|
} catch {
|
|
40355
40502
|
throw new RecurringCustomerError("RECURRING_RECOVERY_BUSY", "Recovery is locked. Confirm the original process has stopped before explicitly reconciling the retained lock; no request was retried.");
|
|
40356
40503
|
}
|
|
40357
|
-
const owner2 =
|
|
40504
|
+
const owner2 = fstatSync17(fd);
|
|
40358
40505
|
try {
|
|
40359
40506
|
writeFileSync9(fd, JSON.stringify({ pid: process.pid }));
|
|
40360
|
-
|
|
40507
|
+
fsyncSync5(fd);
|
|
40361
40508
|
this.sync();
|
|
40362
40509
|
return await action();
|
|
40363
40510
|
} finally {
|
|
40364
|
-
|
|
40511
|
+
closeSync17(fd);
|
|
40365
40512
|
this.assert();
|
|
40366
|
-
const now =
|
|
40513
|
+
const now = lstatSync23(join33(this.directory, "operation.lock"));
|
|
40367
40514
|
if (owner2.dev !== now.dev || owner2.ino !== now.ino || !now.isFile() || now.isSymbolicLink())
|
|
40368
40515
|
invalid5();
|
|
40369
|
-
unlinkSync5(
|
|
40516
|
+
unlinkSync5(join33(this.directory, "operation.lock"));
|
|
40370
40517
|
this.sync();
|
|
40371
40518
|
}
|
|
40372
40519
|
}
|
|
@@ -40399,9 +40546,9 @@ function base(customer, organizationId) {
|
|
|
40399
40546
|
};
|
|
40400
40547
|
}
|
|
40401
40548
|
function create(directory, intent) {
|
|
40402
|
-
if (!
|
|
40549
|
+
if (!isAbsolute20(directory) || resolve22(directory) !== directory || realpathSync9(dirname22(directory)) !== dirname22(directory))
|
|
40403
40550
|
invalid5();
|
|
40404
|
-
|
|
40551
|
+
mkdirSync11(directory, { mode: 448 });
|
|
40405
40552
|
const owner2 = new RecoveryOwner(directory), checked = intentSchema.parse(intent), intentSha256 = canonicalJsonSha256(checked);
|
|
40406
40553
|
owner2.write(`intent-${intentSha256}.json`, checked);
|
|
40407
40554
|
owner2.write("state.json", { contractVersion: 1, intentSha256, phase: "prepared", consentId: null });
|
|
@@ -40524,7 +40671,7 @@ async function continueRecurringRecovery(customer, directory, options) {
|
|
|
40524
40671
|
throw error61;
|
|
40525
40672
|
}
|
|
40526
40673
|
}
|
|
40527
|
-
var uuid12,
|
|
40674
|
+
var uuid12, sha5, common, intentSchema, stateSchema, invalid5 = () => {
|
|
40528
40675
|
throw new RecurringCustomerError("RECURRING_RECOVERY_INVALID", "The original recovery directory or record is invalid or changed. Preserve it; do not create a replacement activation automatically.");
|
|
40529
40676
|
};
|
|
40530
40677
|
var init_recurring_recovery = __esm(() => {
|
|
@@ -40534,7 +40681,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
40534
40681
|
init_remote_recurring();
|
|
40535
40682
|
init_recurring_customer();
|
|
40536
40683
|
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}$/);
|
|
40537
|
-
|
|
40684
|
+
sha5 = exports_external.string().regex(/^[a-f0-9]{64}$/);
|
|
40538
40685
|
common = exports_external.object({
|
|
40539
40686
|
contractVersion: exports_external.literal(1),
|
|
40540
40687
|
apiOrigin: exports_external.string().min(1).max(4096),
|
|
@@ -40547,7 +40694,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
40547
40694
|
common.extend({
|
|
40548
40695
|
operation: exports_external.literal("activate"),
|
|
40549
40696
|
draftId: uuid12,
|
|
40550
|
-
termsSha256:
|
|
40697
|
+
termsSha256: sha5,
|
|
40551
40698
|
approvalDeadline: exports_external.string().datetime(),
|
|
40552
40699
|
approval: recurringActivationSchema
|
|
40553
40700
|
}).strict(),
|
|
@@ -40555,14 +40702,14 @@ var init_recurring_recovery = __esm(() => {
|
|
|
40555
40702
|
]);
|
|
40556
40703
|
stateSchema = exports_external.object({
|
|
40557
40704
|
contractVersion: exports_external.literal(1),
|
|
40558
|
-
intentSha256:
|
|
40705
|
+
intentSha256: sha5,
|
|
40559
40706
|
phase: exports_external.enum(["prepared", "pending", "observed"]),
|
|
40560
40707
|
consentId: uuid12.nullable()
|
|
40561
40708
|
}).strict();
|
|
40562
40709
|
});
|
|
40563
40710
|
|
|
40564
40711
|
// src/lib/recurring-surface.ts
|
|
40565
|
-
import { isAbsolute as
|
|
40712
|
+
import { isAbsolute as isAbsolute21 } from "path";
|
|
40566
40713
|
function recurringMcpSchema(action) {
|
|
40567
40714
|
return action === "activate" ? recurringSurfaceSchemas.activate.extend({ code, confirm: exports_external.literal(true) }) : recurringSurfaceSchemas[action];
|
|
40568
40715
|
}
|
|
@@ -40667,7 +40814,7 @@ var init_recurring_surface = __esm(() => {
|
|
|
40667
40814
|
uuid13 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
40668
40815
|
context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
|
|
40669
40816
|
page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
|
|
40670
|
-
directory = exports_external.string().max(4096).refine(
|
|
40817
|
+
directory = exports_external.string().max(4096).refine(isAbsolute21, "Use an absolute recovery directory");
|
|
40671
40818
|
email4 = exports_external.string().trim().toLowerCase().email().max(254);
|
|
40672
40819
|
code = exports_external.string().regex(/^\d{6}$/);
|
|
40673
40820
|
recurringSurfaceSchemas = {
|
|
@@ -42496,21 +42643,21 @@ var init_credential_state = __esm(() => {
|
|
|
42496
42643
|
});
|
|
42497
42644
|
|
|
42498
42645
|
// src/lib/run-state.ts
|
|
42499
|
-
import { createHash as
|
|
42500
|
-
import { existsSync as existsSync15, mkdirSync as
|
|
42501
|
-
import { extname, join as
|
|
42646
|
+
import { createHash as createHash23, randomBytes } from "crypto";
|
|
42647
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync20, readdirSync as readdirSync10, statSync as statSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
42648
|
+
import { extname, join as join34, relative as relative7 } from "path";
|
|
42502
42649
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
42503
42650
|
const now = new Date;
|
|
42504
42651
|
const id = createRunId(now);
|
|
42505
42652
|
const day = now.toISOString().slice(0, 10);
|
|
42506
42653
|
const skillName = normalizeSkillName(params.skill);
|
|
42507
42654
|
const root = getProjectStateDir(targetDir);
|
|
42508
|
-
const runDir =
|
|
42509
|
-
const logsDir =
|
|
42510
|
-
const exportDir =
|
|
42511
|
-
|
|
42512
|
-
|
|
42513
|
-
|
|
42655
|
+
const runDir = join34(root, "runs", day, id);
|
|
42656
|
+
const logsDir = join34(runDir, "logs");
|
|
42657
|
+
const exportDir = join34(root, "exports", skillName, id);
|
|
42658
|
+
mkdirSync12(logsDir, { recursive: true });
|
|
42659
|
+
mkdirSync12(exportDir, { recursive: true });
|
|
42660
|
+
mkdirSync12(join34(root, "tmp"), { recursive: true });
|
|
42514
42661
|
const record10 = {
|
|
42515
42662
|
id,
|
|
42516
42663
|
skill: skillName,
|
|
@@ -42562,22 +42709,22 @@ function updateSkillRun(context2, patch) {
|
|
|
42562
42709
|
return context2.record;
|
|
42563
42710
|
}
|
|
42564
42711
|
function writeRunLogs(context2, stdout = "", stderr = "") {
|
|
42565
|
-
writeFileSync10(
|
|
42566
|
-
writeFileSync10(
|
|
42712
|
+
writeFileSync10(join34(context2.logsDir, "stdout.log"), stdout);
|
|
42713
|
+
writeFileSync10(join34(context2.logsDir, "stderr.log"), stderr);
|
|
42567
42714
|
}
|
|
42568
42715
|
function appendRunEvent(context2, event, data = {}) {
|
|
42569
42716
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
42570
42717
|
`;
|
|
42571
|
-
const path =
|
|
42718
|
+
const path = join34(context2.runDir, "events.ndjson");
|
|
42572
42719
|
const previous = existsSync15(path) ? readFileSync20(path, "utf-8") : "";
|
|
42573
42720
|
writeFileSync10(path, previous + line);
|
|
42574
42721
|
}
|
|
42575
42722
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
42576
|
-
const runsRoot =
|
|
42723
|
+
const runsRoot = join34(getProjectStateDir(targetDir), "runs");
|
|
42577
42724
|
if (!existsSync15(runsRoot))
|
|
42578
42725
|
return null;
|
|
42579
42726
|
for (const day of readdirSync10(runsRoot)) {
|
|
42580
|
-
const record10 = readRunRecord(
|
|
42727
|
+
const record10 = readRunRecord(join34(runsRoot, day, runId));
|
|
42581
42728
|
if (record10)
|
|
42582
42729
|
return record10;
|
|
42583
42730
|
}
|
|
@@ -42594,11 +42741,11 @@ function skillRunEnv(context2) {
|
|
|
42594
42741
|
};
|
|
42595
42742
|
}
|
|
42596
42743
|
function writeRunRecord(context2) {
|
|
42597
|
-
writeFileSync10(
|
|
42744
|
+
writeFileSync10(join34(context2.runDir, "run.json"), JSON.stringify(context2.record, null, 2) + `
|
|
42598
42745
|
`);
|
|
42599
42746
|
}
|
|
42600
42747
|
function writeArtifactsManifest(context2, artifacts) {
|
|
42601
|
-
writeFileSync10(
|
|
42748
|
+
writeFileSync10(join34(context2.runDir, "artifacts.json"), JSON.stringify({ runId: context2.record.id, artifacts }, null, 2) + `
|
|
42602
42749
|
`);
|
|
42603
42750
|
}
|
|
42604
42751
|
function collectRunArtifacts(context2) {
|
|
@@ -42611,14 +42758,14 @@ function collectRunArtifacts(context2) {
|
|
|
42611
42758
|
artifacts.push({
|
|
42612
42759
|
path: toProjectRelative(context2.targetDir, path),
|
|
42613
42760
|
mime: mimeForPath(path),
|
|
42614
|
-
sha256:
|
|
42761
|
+
sha256: createHash23("sha256").update(bytes).digest("hex"),
|
|
42615
42762
|
sizeBytes: stat3.size
|
|
42616
42763
|
});
|
|
42617
42764
|
}
|
|
42618
42765
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
42619
42766
|
}
|
|
42620
42767
|
function readRunRecord(runDir) {
|
|
42621
|
-
const path =
|
|
42768
|
+
const path = join34(runDir, "run.json");
|
|
42622
42769
|
if (!existsSync15(path))
|
|
42623
42770
|
return null;
|
|
42624
42771
|
try {
|
|
@@ -42630,7 +42777,7 @@ function readRunRecord(runDir) {
|
|
|
42630
42777
|
function walkFiles(dir) {
|
|
42631
42778
|
const files = [];
|
|
42632
42779
|
for (const entry of readdirSync10(dir)) {
|
|
42633
|
-
const full =
|
|
42780
|
+
const full = join34(dir, entry);
|
|
42634
42781
|
if (statSync10(full).isDirectory())
|
|
42635
42782
|
files.push(...walkFiles(full));
|
|
42636
42783
|
else
|
|
@@ -42883,11 +43030,11 @@ var init_cloud_executions = __esm(() => {
|
|
|
42883
43030
|
// node_modules/@hasna/secrets/dist/sdk.js
|
|
42884
43031
|
import { isIP as isIP2 } from "net";
|
|
42885
43032
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
42886
|
-
import { closeSync as
|
|
43033
|
+
import { closeSync as closeSync18, fstatSync as fstatSync18, openSync as openSync18, readFileSync as readFileSync21 } from "fs";
|
|
42887
43034
|
import { O_NOFOLLOW as O_NOFOLLOW3, O_NONBLOCK as O_NONBLOCK3, O_RDONLY as O_RDONLY3 } from "constants";
|
|
42888
43035
|
import { createRequire as createRequire2 } from "module";
|
|
42889
43036
|
import { hostname as osHostname2 } from "os";
|
|
42890
|
-
import { isAbsolute as
|
|
43037
|
+
import { isAbsolute as isAbsolute22, join as join35 } from "path";
|
|
42891
43038
|
import { createRequire as createRequire22 } from "module";
|
|
42892
43039
|
function envToken2(name) {
|
|
42893
43040
|
return name.toUpperCase().replace(/-/g, "_");
|
|
@@ -42911,21 +43058,21 @@ function homeDir2(env) {
|
|
|
42911
43058
|
}
|
|
42912
43059
|
function absoluteOverride2(env, key) {
|
|
42913
43060
|
const value = env[key]?.trim();
|
|
42914
|
-
return value &&
|
|
43061
|
+
return value && isAbsolute22(value) ? value : null;
|
|
42915
43062
|
}
|
|
42916
43063
|
function hasnaHomeDir2(env) {
|
|
42917
43064
|
const override = absoluteOverride2(env, HASNA_HOME_ENV_KEY2);
|
|
42918
43065
|
if (override)
|
|
42919
43066
|
return override;
|
|
42920
43067
|
const home = homeDir2(env);
|
|
42921
|
-
return home ?
|
|
43068
|
+
return home ? join35(home, HASNA_HOME_DIR2) : null;
|
|
42922
43069
|
}
|
|
42923
43070
|
function appConfigDir2(name, env) {
|
|
42924
43071
|
const configRoot = absoluteOverride2(env, HASNA_CONFIG_HOME_ENV_KEY2);
|
|
42925
43072
|
if (configRoot)
|
|
42926
|
-
return
|
|
43073
|
+
return join35(configRoot, name);
|
|
42927
43074
|
const root = hasnaHomeDir2(env);
|
|
42928
|
-
return root ?
|
|
43075
|
+
return root ? join35(root, name, CONFIG_SUBDIR2) : null;
|
|
42929
43076
|
}
|
|
42930
43077
|
function credentialDiskSourceList2(name, env, profile = null) {
|
|
42931
43078
|
if (!SAFE_APP_SLUG2.test(name))
|
|
@@ -42934,7 +43081,7 @@ function credentialDiskSourceList2(name, env, profile = null) {
|
|
|
42934
43081
|
if (!directory2)
|
|
42935
43082
|
return [];
|
|
42936
43083
|
const file2 = profile ? `${CREDENTIALS_FILE2}-${profile}` : CREDENTIALS_FILE2;
|
|
42937
|
-
return [{ path:
|
|
43084
|
+
return [{ path: join35(directory2, file2), tier: "disk" }];
|
|
42938
43085
|
}
|
|
42939
43086
|
function credentialDiskSources2(name, env) {
|
|
42940
43087
|
return credentialDiskSourceList2(name, env, null).map((s) => s.path);
|
|
@@ -42988,7 +43135,7 @@ function readAppConfigFile2(path) {
|
|
|
42988
43135
|
};
|
|
42989
43136
|
let fd = -1;
|
|
42990
43137
|
try {
|
|
42991
|
-
fd =
|
|
43138
|
+
fd = openSync18(path, O_RDONLY3 | O_NOFOLLOW3 | O_NONBLOCK3);
|
|
42992
43139
|
} catch (error61) {
|
|
42993
43140
|
const code2 = error61.code;
|
|
42994
43141
|
if (code2 === "ENOENT" || code2 === "ENOTDIR")
|
|
@@ -42998,7 +43145,7 @@ function readAppConfigFile2(path) {
|
|
|
42998
43145
|
unsafe(`the path could not be opened (${code2 ?? "unknown error"})`);
|
|
42999
43146
|
}
|
|
43000
43147
|
try {
|
|
43001
|
-
const before =
|
|
43148
|
+
const before = fstatSync18(fd);
|
|
43002
43149
|
if (!before.isFile())
|
|
43003
43150
|
unsafe("the path is not a regular file");
|
|
43004
43151
|
if (!configFileModeAllowed2(before.mode)) {
|
|
@@ -43010,14 +43157,14 @@ function readAppConfigFile2(path) {
|
|
|
43010
43157
|
if (before.size > MAX_CREDENTIAL_FILE_BYTES2)
|
|
43011
43158
|
unsafe("the file exceeds the size limit");
|
|
43012
43159
|
const bytes = readFileSync21(fd);
|
|
43013
|
-
const after =
|
|
43160
|
+
const after = fstatSync18(fd);
|
|
43014
43161
|
if (!configFileReadsCoherent2(before, after)) {
|
|
43015
43162
|
unsafe("the file changed while being read");
|
|
43016
43163
|
}
|
|
43017
43164
|
return parseEnvFile2(bytes.toString("utf8"));
|
|
43018
43165
|
} finally {
|
|
43019
43166
|
if (fd !== -1)
|
|
43020
|
-
|
|
43167
|
+
closeSync18(fd);
|
|
43021
43168
|
}
|
|
43022
43169
|
}
|
|
43023
43170
|
function readCredentialFile2(path, apiKeyKeys) {
|
|
@@ -43035,8 +43182,8 @@ function readCredentialFile2(path, apiKeyKeys) {
|
|
|
43035
43182
|
}
|
|
43036
43183
|
return values[0] ?? null;
|
|
43037
43184
|
}
|
|
43038
|
-
function appConfigDiskValue2(name, env,
|
|
43039
|
-
const wanted =
|
|
43185
|
+
function appConfigDiskValue2(name, env, keys2) {
|
|
43186
|
+
const wanted = keys2.filter((key) => !CREDENTIAL_SHAPED_KEY2.test(key));
|
|
43040
43187
|
if (wanted.length === 0)
|
|
43041
43188
|
return null;
|
|
43042
43189
|
for (const path of credentialDiskSources2(name, env)) {
|
|
@@ -43143,8 +43290,8 @@ function validateAndSealResolvedCredential(appName, credential) {
|
|
|
43143
43290
|
...credential.pointerVaultKey !== undefined ? { pointerVaultKey: credential.pointerVaultKey } : {}
|
|
43144
43291
|
});
|
|
43145
43292
|
}
|
|
43146
|
-
function firstEnvValue2(env,
|
|
43147
|
-
for (const key of
|
|
43293
|
+
function firstEnvValue2(env, keys2) {
|
|
43294
|
+
for (const key of keys2) {
|
|
43148
43295
|
if (!Object.prototype.hasOwnProperty.call(env, key))
|
|
43149
43296
|
continue;
|
|
43150
43297
|
const value = env[key]?.trim();
|
|
@@ -43221,12 +43368,12 @@ function keychainConfigValue2(name, env, options = {}) {
|
|
|
43221
43368
|
return readKeychainItem2(name, env, "api-url", options);
|
|
43222
43369
|
}
|
|
43223
43370
|
function snapshotClientEnvironment2(name, env) {
|
|
43224
|
-
const
|
|
43371
|
+
const keys2 = clientTransportEnvKeys2(name);
|
|
43225
43372
|
const ambient = isAmbientEnvironment2(env);
|
|
43226
43373
|
const snapshot2 = Object.create(null);
|
|
43227
43374
|
for (const key of [
|
|
43228
|
-
...
|
|
43229
|
-
...
|
|
43375
|
+
...keys2.apiUrlKeys,
|
|
43376
|
+
...keys2.apiKeyKeys,
|
|
43230
43377
|
credentialOverrideEnvKey2(name),
|
|
43231
43378
|
credentialPointerEnvKey2(name),
|
|
43232
43379
|
CREDENTIAL_PROFILE_ENV_KEY2,
|
|
@@ -43553,8 +43700,8 @@ function toV1BaseUrl2(apiUrl) {
|
|
|
43553
43700
|
}
|
|
43554
43701
|
function resolveClientTransportSnapshot(name, env = process.env, options = {}) {
|
|
43555
43702
|
env = snapshotClientEnvironment2(name, env);
|
|
43556
|
-
const
|
|
43557
|
-
const definedUrlEntries =
|
|
43703
|
+
const keys2 = clientTransportEnvKeys2(name);
|
|
43704
|
+
const definedUrlEntries = keys2.apiUrlKeys.filter((key) => Object.prototype.hasOwnProperty.call(env, key) && env[key] !== undefined).map((key) => ({ key, raw: String(env[key]) }));
|
|
43558
43705
|
const blankUrl = definedUrlEntries.find((entry) => entry.raw.trim().length === 0);
|
|
43559
43706
|
if (blankUrl) {
|
|
43560
43707
|
throw new ClientTransportConfigurationError2(name, `${blankUrl.key} is set but blank; public clients require an explicit HTTPS API URL and never select local storage.`, [blankUrl.key]);
|
|
@@ -43569,7 +43716,7 @@ function resolveClientTransportSnapshot(name, env = process.env, options = {}) {
|
|
|
43569
43716
|
}
|
|
43570
43717
|
const envUrlHit = usableUrlEntries[0] ?? null;
|
|
43571
43718
|
const keychainUrlHit = keychainConfigValue2(name, env, options.credentials?.keychain);
|
|
43572
|
-
const diskConfigUrlHit = appConfigDiskValue2(name, env,
|
|
43719
|
+
const diskConfigUrlHit = appConfigDiskValue2(name, env, keys2.apiUrlKeys);
|
|
43573
43720
|
if (diskConfigUrlHit?.unusable) {
|
|
43574
43721
|
throw new ClientTransportConfigurationError2(name, `${diskConfigUrlHit.key} in ${diskConfigUrlHit.path} is declared but blank or malformed; public clients require a valid HTTPS service authority.`, [diskConfigUrlHit.path]);
|
|
43575
43722
|
}
|
|
@@ -43585,14 +43732,14 @@ function resolveClientTransportSnapshot(name, env = process.env, options = {}) {
|
|
|
43585
43732
|
}
|
|
43586
43733
|
const warnings = [];
|
|
43587
43734
|
if (configuredUrl && !envUrlHit) {
|
|
43588
|
-
warnings.push(`No ${
|
|
43735
|
+
warnings.push(`No ${keys2.apiUrlKeys[0]} in the environment; the server URL in ${configuredUrl.key} was used, so this client connects to the server. ` + `Keep that entry aligned with the intended service authority.`);
|
|
43589
43736
|
}
|
|
43590
43737
|
const credential = resolveCredential2(name, env, options.credentials);
|
|
43591
43738
|
if (!credential) {
|
|
43592
43739
|
const diskHint = credentialDiskSourcesForMessage(name, env);
|
|
43593
|
-
const lead = configuredUrl ? `${configuredUrl.key} selects the HTTP server for '${name}', but no API key could be resolved` : `${
|
|
43594
|
-
warnings.push(`${lead}; refusing to create an unauthenticated client \u2014 public clients never fall back to SQLite or another local store. ` + `Looked in the Keychain (macOS only), then for a credential file at ${diskHint}, then for ${
|
|
43595
|
-
throw new ClientTransportConfigurationError2(name, warnings.join(" "), [configuredUrl?.key ??
|
|
43740
|
+
const lead = configuredUrl ? `${configuredUrl.key} selects the HTTP server for '${name}', but no API key could be resolved` : `${keys2.apiUrlKeys[0]} is not set and no API key could be resolved for '${name}'; a credential is required before the default fleet gateway authority applies`;
|
|
43741
|
+
warnings.push(`${lead}; refusing to create an unauthenticated client \u2014 public clients never fall back to SQLite or another local store. ` + `Looked in the Keychain (macOS only), then for a credential file at ${diskHint}, then for ${keys2.apiKeyKeys[0]} in the environment.`);
|
|
43742
|
+
throw new ClientTransportConfigurationError2(name, warnings.join(" "), [configuredUrl?.key ?? keys2.apiUrlKeys[0]]);
|
|
43596
43743
|
}
|
|
43597
43744
|
if (credential.warning)
|
|
43598
43745
|
warnings.push(credential.warning);
|
|
@@ -43604,7 +43751,7 @@ function resolveClientTransportSnapshot(name, env = process.env, options = {}) {
|
|
|
43604
43751
|
urlHit = { key: DEFAULT_AUTHORITY_SOURCE, value: defaultFleetGatewayBaseUrl2(name) };
|
|
43605
43752
|
} catch (error61) {
|
|
43606
43753
|
const message = error61 instanceof Error ? error61.message : String(error61);
|
|
43607
|
-
throw new ClientTransportConfigurationError2(name, `No ${
|
|
43754
|
+
throw new ClientTransportConfigurationError2(name, `No ${keys2.apiUrlKeys[0]} is configured and the default fleet gateway authority cannot be composed for '${name}': ${message}`, [keys2.apiUrlKeys[0]]);
|
|
43608
43755
|
}
|
|
43609
43756
|
}
|
|
43610
43757
|
const apiUrlSource = urlHit.key;
|
|
@@ -44048,7 +44195,7 @@ function createSecretsClientFromEnv(env = process.env, overrides = {}) {
|
|
|
44048
44195
|
});
|
|
44049
44196
|
return new SecretsClient({ ...clientOverrides, baseUrl, apiKey });
|
|
44050
44197
|
}
|
|
44051
|
-
var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), CREDENTIAL_PROFILE_ENV_KEY2 = "HASNA_PROFILE", CredentialResolutionError2, CredentialFileUnsafeError2, HASNA_HOME_ENV_KEY2 = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY2 = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY2 = "HASNA_STATION", HASNA_HOME_DIR2 = ".hasna", CONFIG_SUBDIR2 = "config", CREDENTIALS_FILE2 = "credentials", KEYCHAIN_SECURITY_BIN2 = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX2 = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS2 = 44, KEYCHAIN_SPAWN_TIMEOUT_MS2 = 1e4, MAX_CREDENTIAL_FILE_BYTES2, SAFE_APP_SLUG2, SAFE_PROFILE2, ILLEGAL_IN_HEADER_VALUE2, VAULT_POINTER_SHAPE2, CREDENTIAL_SHAPED_KEY2, INSPECT_CUSTOM2, CREDENTIAL_SEAL2, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE = "caller-supplied CredentialProvider", AMBIENT_ENVIRONMENT2, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk2, DEFAULT_FLEET_GATEWAY_ORIGIN2 = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN2, DNS_LABEL_PATTERN2, ClientTransportConfigurationError2, HasnaHttpError, DEFAULT_RETRY_STATUSES, IDEMPOTENT_METHODS2, AUTHORITY_OVERRIDE_HEADERS2, defaultSleep = (ms) => new Promise((
|
|
44198
|
+
var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), CREDENTIAL_PROFILE_ENV_KEY2 = "HASNA_PROFILE", CredentialResolutionError2, CredentialFileUnsafeError2, HASNA_HOME_ENV_KEY2 = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY2 = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY2 = "HASNA_STATION", HASNA_HOME_DIR2 = ".hasna", CONFIG_SUBDIR2 = "config", CREDENTIALS_FILE2 = "credentials", KEYCHAIN_SECURITY_BIN2 = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX2 = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS2 = 44, KEYCHAIN_SPAWN_TIMEOUT_MS2 = 1e4, MAX_CREDENTIAL_FILE_BYTES2, SAFE_APP_SLUG2, SAFE_PROFILE2, ILLEGAL_IN_HEADER_VALUE2, VAULT_POINTER_SHAPE2, CREDENTIAL_SHAPED_KEY2, INSPECT_CUSTOM2, CREDENTIAL_SEAL2, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE = "caller-supplied CredentialProvider", AMBIENT_ENVIRONMENT2, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk2, DEFAULT_FLEET_GATEWAY_ORIGIN2 = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN2, DNS_LABEL_PATTERN2, ClientTransportConfigurationError2, HasnaHttpError, DEFAULT_RETRY_STATUSES, IDEMPOTENT_METHODS2, AUTHORITY_OVERRIDE_HEADERS2, defaultSleep = (ms) => new Promise((resolve23) => setTimeout(resolve23, ms)), init_transport2, MAX_CREDENTIAL_FILE_BYTES22, INSPECT_CUSTOM22, CREDENTIAL_SEAL22, AMBIENT_ENVIRONMENT22, SECRETS_PACKAGE_SPECIFIER2, requireSecretsSdk22, IDEMPOTENT_METHODS22, AUTHORITY_OVERRIDE_HEADERS22, init_storage, PATHS_RESOLVER_APP_SLUG_RE2, SQLITE_SIDECAR_FILES, LEGACY_SERVICE_FILES, init_data_dir, SecretsTestIsolationError, TEST_ENTRYPOINT_RE, init_test_isolation, init_client, ApiError;
|
|
44052
44199
|
var init_sdk = __esm(() => {
|
|
44053
44200
|
init_transport2 = __esm2(() => {
|
|
44054
44201
|
CredentialResolutionError2 = class CredentialResolutionError3 extends Error {
|
|
@@ -44183,14 +44330,14 @@ var init_sdk = __esm(() => {
|
|
|
44183
44330
|
});
|
|
44184
44331
|
|
|
44185
44332
|
// src/lib/execution-secrets.ts
|
|
44186
|
-
import { constants as
|
|
44333
|
+
import { constants as constants16, closeSync as closeSync19, fstatSync as fstatSync19, openSync as openSync19, readSync as readSync12, realpathSync as realpathSync10 } from "fs";
|
|
44187
44334
|
import { hostname as stationHostname } from "os";
|
|
44188
44335
|
function selectedSecretBindingsTemplate(scope, names, createClient = createSecretsClientFromEnv) {
|
|
44189
44336
|
let secretsAuthority;
|
|
44190
44337
|
try {
|
|
44191
44338
|
secretsAuthority = createClient().baseUrl;
|
|
44192
44339
|
} catch {
|
|
44193
|
-
return
|
|
44340
|
+
return refuse4("LOCAL_SECRET_UNAVAILABLE", "Configure the Secrets CLI authority and authentication before preparing execution bindings.");
|
|
44194
44341
|
}
|
|
44195
44342
|
const { authority, workspaceId, profileRevision, slug: slug2, version: version2, bundleDigest } = scope.selection;
|
|
44196
44343
|
return {
|
|
@@ -44201,14 +44348,14 @@ function selectedSecretBindingsTemplate(scope, names, createClient = createSecre
|
|
|
44201
44348
|
bindings: Object.fromEntries(names.map((name) => [name, ""]))
|
|
44202
44349
|
};
|
|
44203
44350
|
}
|
|
44204
|
-
function
|
|
44351
|
+
function refuse4(code2, message) {
|
|
44205
44352
|
throw new SkillSelectionError(code2, message);
|
|
44206
44353
|
}
|
|
44207
44354
|
function record10(value) {
|
|
44208
44355
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
44209
44356
|
}
|
|
44210
|
-
function exactKeys2(value,
|
|
44211
|
-
return Object.keys(value).length ===
|
|
44357
|
+
function exactKeys2(value, keys2) {
|
|
44358
|
+
return Object.keys(value).length === keys2.length && keys2.every((key) => Object.hasOwn(value, key));
|
|
44212
44359
|
}
|
|
44213
44360
|
function identifier3(value) {
|
|
44214
44361
|
return typeof value === "string" && value.length > 0 && value.length <= 2048 && !/[\x00-\x20\x7f]/.test(value);
|
|
@@ -44217,29 +44364,29 @@ function declaredSecretNames(value) {
|
|
|
44217
44364
|
if (value === undefined)
|
|
44218
44365
|
return [];
|
|
44219
44366
|
if (!Array.isArray(value) || value.length > 32 || value.some((name) => typeof name !== "string" || !/^[A-Z][A-Z0-9_]{0,127}$/.test(name)) || new Set(value).size !== value.length) {
|
|
44220
|
-
return
|
|
44367
|
+
return refuse4("INVALID_SKILL_MANIFEST", "Runtime environment references must be unique uppercase identifiers (at most 32).");
|
|
44221
44368
|
}
|
|
44222
44369
|
if (value.some((name) => /^(?:PATH|HOME|TMPDIR|TEMP|TMP|LANG|LC_ALL|TERM|SHELL|ENV|BASH_ENV|IFS|NODE_OPTIONS|NODE_PATH)$/.test(name) || /^(?:SKILLS_|BUN_|PYTHON|LD_|DYLD_)/.test(name))) {
|
|
44223
|
-
return
|
|
44370
|
+
return refuse4("INVALID_SKILL_MANIFEST", "Runtime environment references cannot override execution controls.");
|
|
44224
44371
|
}
|
|
44225
44372
|
return value;
|
|
44226
44373
|
}
|
|
44227
44374
|
function validateSelectedSecretBindings(value, scope, names) {
|
|
44228
44375
|
if (!record10(value) || !exactKeys2(value, ["schema", "selection", "consumer", "secretsAuthority", "bindings"]) || value.schema !== "hasna.skills-secret-bindings.v1" || !record10(value.selection) || !exactKeys2(value.selection, ["authority", "workspaceId", "profileId", "profileRevision", "slug", "version", "bundleDigest"]) || !record10(value.consumer) || !exactKeys2(value.consumer, ["stationId", "workspaceDirectory"]) || !record10(value.bindings)) {
|
|
44229
|
-
return
|
|
44376
|
+
return refuse4("INVALID_SECRET_BINDINGS", "Secret bindings do not match the hasna.skills-secret-bindings.v1 contract.");
|
|
44230
44377
|
}
|
|
44231
44378
|
for (const [key, expected] of Object.entries({ authority: scope.selection.authority, workspaceId: scope.selection.workspaceId, profileId: scope.profileId, profileRevision: scope.selection.profileRevision, slug: scope.selection.slug, version: scope.selection.version, bundleDigest: scope.selection.bundleDigest })) {
|
|
44232
44379
|
if (value.selection[key] !== expected)
|
|
44233
|
-
return
|
|
44380
|
+
return refuse4("SECRET_BINDING_SCOPE_MISMATCH", "Secret bindings do not match the exact selected authority, workspace, profile, revision, version and digest.");
|
|
44234
44381
|
}
|
|
44235
44382
|
let workspaceDirectory;
|
|
44236
44383
|
try {
|
|
44237
44384
|
workspaceDirectory = realpathSync10(scope.cwd);
|
|
44238
44385
|
} catch {
|
|
44239
|
-
return
|
|
44386
|
+
return refuse4("SECRET_BINDING_CONSUMER_MISMATCH", "The execution workspace cannot be resolved.");
|
|
44240
44387
|
}
|
|
44241
44388
|
if (value.consumer.stationId !== (process.env.HASNA_STATION || stationHostname()) || value.consumer.workspaceDirectory !== workspaceDirectory) {
|
|
44242
|
-
return
|
|
44389
|
+
return refuse4("SECRET_BINDING_CONSUMER_MISMATCH", "Secret bindings do not match this station and canonical workspace directory.");
|
|
44243
44390
|
}
|
|
44244
44391
|
try {
|
|
44245
44392
|
if (!identifier3(value.secretsAuthority))
|
|
@@ -44248,10 +44395,10 @@ function validateSelectedSecretBindings(value, scope, names) {
|
|
|
44248
44395
|
if (url2.protocol !== "https:" && !(url2.protocol === "http:" && ["127.0.0.1", "[::1]", "localhost"].includes(url2.hostname)) || url2.username || url2.password || url2.search || url2.hash || !url2.pathname.endsWith("/v1") || url2.href !== value.secretsAuthority)
|
|
44249
44396
|
throw new Error;
|
|
44250
44397
|
} catch {
|
|
44251
|
-
return
|
|
44398
|
+
return refuse4("INVALID_SECRET_BINDINGS", "Secret bindings require a canonical HTTPS Secrets /v1 authority (loopback HTTP is allowed).");
|
|
44252
44399
|
}
|
|
44253
44400
|
if (!exactKeys2(value.bindings, names) || Object.values(value.bindings).some((key) => !identifier3(key) || !/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(key) || key.split("/").some((part) => !part || part === "." || part === ".."))) {
|
|
44254
|
-
return
|
|
44401
|
+
return refuse4("INVALID_SECRET_BINDINGS", "Bind every declared environment name exactly once to a vault key; undeclared bindings and values are refused.");
|
|
44255
44402
|
}
|
|
44256
44403
|
return structuredClone(value);
|
|
44257
44404
|
}
|
|
@@ -44262,10 +44409,10 @@ async function resolveSelectedSecrets(bindings, createClient = createSecretsClie
|
|
|
44262
44409
|
try {
|
|
44263
44410
|
client = createClient();
|
|
44264
44411
|
} catch {
|
|
44265
|
-
return
|
|
44412
|
+
return refuse4("LOCAL_SECRET_UNAVAILABLE", "The configured Secrets client could not authenticate. No ambient credential fallback was attempted.");
|
|
44266
44413
|
}
|
|
44267
44414
|
if (client.baseUrl !== bindings.secretsAuthority)
|
|
44268
|
-
return
|
|
44415
|
+
return refuse4("SECRET_BINDING_AUTHORITY_MISMATCH", "The configured Secrets authority differs from the reviewed binding. No secret was requested.");
|
|
44269
44416
|
const env = {};
|
|
44270
44417
|
const signal = AbortSignal.timeout(15000);
|
|
44271
44418
|
try {
|
|
@@ -44277,7 +44424,7 @@ async function resolveSelectedSecrets(bindings, createClient = createSecretsClie
|
|
|
44277
44424
|
}
|
|
44278
44425
|
return env;
|
|
44279
44426
|
} catch {
|
|
44280
|
-
return
|
|
44427
|
+
return refuse4("LOCAL_SECRET_UNAVAILABLE", "A declared secret could not be resolved, was invalid, or has expired. The skill was not started; no fallback was attempted.");
|
|
44281
44428
|
}
|
|
44282
44429
|
}
|
|
44283
44430
|
function redactExecutionSecrets(text3, env) {
|
|
@@ -44305,7 +44452,7 @@ function grantIdentifier(v) {
|
|
|
44305
44452
|
function grantRecord(v) {
|
|
44306
44453
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
44307
44454
|
}
|
|
44308
|
-
function
|
|
44455
|
+
function keys2(v, required2, optional2 = []) {
|
|
44309
44456
|
return required2.every((k) => Object.hasOwn(v, k)) && Object.keys(v).every((k) => required2.includes(k) || optional2.includes(k));
|
|
44310
44457
|
}
|
|
44311
44458
|
function invalid6() {
|
|
@@ -44333,10 +44480,10 @@ function grantWorkspace(v) {
|
|
|
44333
44480
|
return flavor.isAbsolute(v) && flavor.normalize(v) === v;
|
|
44334
44481
|
}
|
|
44335
44482
|
function selection(v) {
|
|
44336
|
-
return grantRecord(v) &&
|
|
44483
|
+
return grantRecord(v) && keys2(v, ["slug", "version", "bundleDigest"]) && grantIdentifier(v.slug) && isValidSkillVersion(v.version) && typeof v.bundleDigest === "string" && /^sha256:[a-f0-9]{64}$/.test(v.bundleDigest);
|
|
44337
44484
|
}
|
|
44338
44485
|
function validateExecutionGrantRequest(value) {
|
|
44339
|
-
if (!grantRecord(value) || !
|
|
44486
|
+
if (!grantRecord(value) || !keys2(value, ["selection", "consumer"]) || !grantRecord(value.selection) || !keys2(value.selection, [
|
|
44340
44487
|
"authority",
|
|
44341
44488
|
"workspaceId",
|
|
44342
44489
|
"profileId",
|
|
@@ -44344,7 +44491,7 @@ function validateExecutionGrantRequest(value) {
|
|
|
44344
44491
|
"slug",
|
|
44345
44492
|
"version",
|
|
44346
44493
|
"bundleDigest"
|
|
44347
|
-
]) || !grantRecord(value.consumer) || !
|
|
44494
|
+
]) || !grantRecord(value.consumer) || !keys2(value.consumer, ["stationId", "workspaceDirectory"]))
|
|
44348
44495
|
invalid6();
|
|
44349
44496
|
const { selection: s, consumer: c } = value;
|
|
44350
44497
|
if (!selection({
|
|
@@ -44425,10 +44572,10 @@ __export(exports_selected_run, {
|
|
|
44425
44572
|
prepareSelectedSecretBindings: () => prepareSelectedSecretBindings,
|
|
44426
44573
|
executeSelectedLocal: () => executeSelectedLocal
|
|
44427
44574
|
});
|
|
44428
|
-
import { createHash as
|
|
44575
|
+
import { createHash as createHash24 } from "crypto";
|
|
44429
44576
|
import { spawn as spawn2 } from "child_process";
|
|
44430
|
-
import { mkdirSync as
|
|
44431
|
-
import { dirname as
|
|
44577
|
+
import { mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync11, realpathSync as realpathSync11 } from "fs";
|
|
44578
|
+
import { dirname as dirname23, join as join36, resolve as resolve23 } from "path";
|
|
44432
44579
|
import { tmpdir, hostname as stationHostname2 } from "os";
|
|
44433
44580
|
async function resolveSelectedRun(spec, profileId, options = {}) {
|
|
44434
44581
|
const context2 = await resolveSelectionContext(profileId, options);
|
|
@@ -44508,7 +44655,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
44508
44655
|
throw new SkillSelectionError("INVALID_SECRET_BINDINGS", "Explicit environment values must be bounded strings without NUL bytes.");
|
|
44509
44656
|
secretEnv[name] = value;
|
|
44510
44657
|
}
|
|
44511
|
-
const runDirectory = mkdtempSync3(
|
|
44658
|
+
const runDirectory = mkdtempSync3(join36(tmpdir(), "skills-execution-"));
|
|
44512
44659
|
const paths = new SkillEntryPaths;
|
|
44513
44660
|
for (const entry of entries) {
|
|
44514
44661
|
paths.add(entry.path, 100, () => {
|
|
@@ -44516,12 +44663,12 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
44516
44663
|
}, () => {
|
|
44517
44664
|
throw new SkillSelectionError("INVALID_BUNDLE_PATH", "The execution bundle path exceeds its limit.");
|
|
44518
44665
|
});
|
|
44519
|
-
const destination =
|
|
44520
|
-
|
|
44666
|
+
const destination = join36(runDirectory, entry.path);
|
|
44667
|
+
mkdirSync13(dirname23(destination), { recursive: true, mode: 448 });
|
|
44521
44668
|
writeFileSync11(destination, entry.bytes, { flag: "wx", mode: entry.mode & 511 });
|
|
44522
44669
|
}
|
|
44523
44670
|
env.SKILLS_INPUT_JSON = input2;
|
|
44524
|
-
env.SKILLS_WORKSPACE_DIR =
|
|
44671
|
+
env.SKILLS_WORKSPACE_DIR = resolve23(options.cwd ?? process.cwd());
|
|
44525
44672
|
env.SKILLS_RUN_DIR = runDirectory;
|
|
44526
44673
|
if (runtime.version) {
|
|
44527
44674
|
const version2 = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [], "--version"], runDirectory, env, "", Math.min(timeoutMs, 5000));
|
|
@@ -44544,20 +44691,20 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
44544
44691
|
}
|
|
44545
44692
|
if (bindings)
|
|
44546
44693
|
secretEnv = await resolveSelectedSecrets(bindings, options.createSecretsClient);
|
|
44547
|
-
const result = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [],
|
|
44694
|
+
const result = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [], join36(runDirectory, declaredEntry), ...args], runDirectory, { ...env, ...secretEnv }, input2, timeoutMs);
|
|
44548
44695
|
result.stdout = redactExecutionSecrets(result.stdout, secretEnv);
|
|
44549
44696
|
result.stderr = redactExecutionSecrets(result.stderr, secretEnv);
|
|
44550
44697
|
const receipt = {
|
|
44551
44698
|
selection: selection2,
|
|
44552
44699
|
target: "local",
|
|
44553
44700
|
runtime: runtimeName,
|
|
44554
|
-
inputDigest: `sha256:${
|
|
44701
|
+
inputDigest: `sha256:${createHash24("sha256").update(input2).digest("hex")}`,
|
|
44555
44702
|
runDirectory,
|
|
44556
44703
|
...executionGrant ? { executionGrant } : {},
|
|
44557
44704
|
...bindings ? { secretBinding: { schema: bindings.schema, selection: bindings.selection, consumer: bindings.consumer, secretsAuthority: bindings.secretsAuthority, bindings: bindings.bindings } } : {},
|
|
44558
44705
|
...result
|
|
44559
44706
|
};
|
|
44560
|
-
writeFileSync11(
|
|
44707
|
+
writeFileSync11(join36(runDirectory, ".execution-receipt.json"), JSON.stringify({ ...receipt, stdout: undefined, stderr: undefined }), { mode: 384 });
|
|
44561
44708
|
return receipt;
|
|
44562
44709
|
}
|
|
44563
44710
|
async function subprocess(command, cwd, env, input2, timeoutMs) {
|
|
@@ -44747,7 +44894,7 @@ var init_blog_article = __esm(() => {
|
|
|
44747
44894
|
|
|
44748
44895
|
// src/mcp/operation-tools.ts
|
|
44749
44896
|
import { existsSync as existsSync16, readdirSync as readdirSync11, statSync as statSync11 } from "fs";
|
|
44750
|
-
import { join as
|
|
44897
|
+
import { join as join37 } from "path";
|
|
44751
44898
|
function registerOperationTools(server) {
|
|
44752
44899
|
server.registerTool("cloud_skill_eligibility", {
|
|
44753
44900
|
title: "Cloud Skill Eligibility",
|
|
@@ -45224,7 +45371,7 @@ function registerOperationTools(server) {
|
|
|
45224
45371
|
if (exists) {
|
|
45225
45372
|
try {
|
|
45226
45373
|
skillCount = readdirSync11(agentSkillsPath).filter((f) => {
|
|
45227
|
-
const full =
|
|
45374
|
+
const full = join37(agentSkillsPath, f);
|
|
45228
45375
|
return !f.startsWith(".") && statSync11(full).isDirectory();
|
|
45229
45376
|
}).length;
|
|
45230
45377
|
} catch {}
|
|
@@ -45291,17 +45438,17 @@ var init_operation_tools = __esm(() => {
|
|
|
45291
45438
|
});
|
|
45292
45439
|
|
|
45293
45440
|
// src/lib/feedback.ts
|
|
45294
|
-
import { appendFileSync, existsSync as existsSync17, mkdirSync as
|
|
45295
|
-
import { dirname as
|
|
45441
|
+
import { appendFileSync, existsSync as existsSync17, mkdirSync as mkdirSync14 } from "fs";
|
|
45442
|
+
import { dirname as dirname24, join as join38 } from "path";
|
|
45296
45443
|
import { Database } from "bun:sqlite";
|
|
45297
45444
|
function getFeedbackDbPath() {
|
|
45298
|
-
return
|
|
45445
|
+
return join38(getDataDir(), "skills.db");
|
|
45299
45446
|
}
|
|
45300
45447
|
function getFeedbackDb() {
|
|
45301
45448
|
const dbPath = getFeedbackDbPath();
|
|
45302
|
-
const dir =
|
|
45449
|
+
const dir = dirname24(dbPath);
|
|
45303
45450
|
if (!existsSync17(dir))
|
|
45304
|
-
|
|
45451
|
+
mkdirSync14(dir, { recursive: true });
|
|
45305
45452
|
const db = new Database(dbPath);
|
|
45306
45453
|
db.exec("PRAGMA journal_mode = WAL");
|
|
45307
45454
|
db.exec([
|
|
@@ -45327,10 +45474,10 @@ function saveFeedback(input2) {
|
|
|
45327
45474
|
throw new Error("Feedback message is required");
|
|
45328
45475
|
const category = input2.category ?? "general";
|
|
45329
45476
|
if (isApiMode()) {
|
|
45330
|
-
const path =
|
|
45331
|
-
const dir =
|
|
45477
|
+
const path = join38(getDataDir(), "feedback.jsonl");
|
|
45478
|
+
const dir = dirname24(path);
|
|
45332
45479
|
if (!existsSync17(dir))
|
|
45333
|
-
|
|
45480
|
+
mkdirSync14(dir, { recursive: true });
|
|
45334
45481
|
appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
45335
45482
|
`);
|
|
45336
45483
|
return { saved: true, category, path };
|
|
@@ -45491,10 +45638,10 @@ var init_resource_meta_tools = __esm(() => {
|
|
|
45491
45638
|
});
|
|
45492
45639
|
|
|
45493
45640
|
// src/lib/scheduler.ts
|
|
45494
|
-
import { existsSync as existsSync18, readFileSync as readFileSync22, writeFileSync as writeFileSync12, mkdirSync as
|
|
45495
|
-
import { join as
|
|
45641
|
+
import { existsSync as existsSync18, readFileSync as readFileSync22, writeFileSync as writeFileSync12, mkdirSync as mkdirSync15 } from "fs";
|
|
45642
|
+
import { join as join39 } from "path";
|
|
45496
45643
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
45497
|
-
return
|
|
45644
|
+
return join39(targetDir, ".skills", "schedules.json");
|
|
45498
45645
|
}
|
|
45499
45646
|
function loadSchedules(targetDir = process.cwd()) {
|
|
45500
45647
|
const path = getSchedulesPath(targetDir);
|
|
@@ -45507,9 +45654,9 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
45507
45654
|
}
|
|
45508
45655
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
45509
45656
|
const path = getSchedulesPath(targetDir);
|
|
45510
|
-
const dir =
|
|
45657
|
+
const dir = join39(targetDir, ".skills");
|
|
45511
45658
|
if (!existsSync18(dir))
|
|
45512
|
-
|
|
45659
|
+
mkdirSync15(dir, { recursive: true });
|
|
45513
45660
|
writeFileSync12(path, JSON.stringify(data, null, 2));
|
|
45514
45661
|
}
|
|
45515
45662
|
function validateCronField(expr, min, max, label) {
|
|
@@ -45779,16 +45926,16 @@ var init_schedule_tools = __esm(() => {
|
|
|
45779
45926
|
});
|
|
45780
45927
|
|
|
45781
45928
|
// src/lib/native-storage.ts
|
|
45782
|
-
import { createHash as
|
|
45929
|
+
import { createHash as createHash25, createHmac } from "crypto";
|
|
45783
45930
|
import {
|
|
45784
45931
|
existsSync as existsSync19,
|
|
45785
|
-
mkdirSync as
|
|
45932
|
+
mkdirSync as mkdirSync16,
|
|
45786
45933
|
readFileSync as readFileSync23,
|
|
45787
45934
|
readdirSync as readdirSync12,
|
|
45788
45935
|
statSync as statSync12,
|
|
45789
45936
|
writeFileSync as writeFileSync13
|
|
45790
45937
|
} from "fs";
|
|
45791
|
-
import { dirname as
|
|
45938
|
+
import { dirname as dirname25, join as join40, normalize as normalize5, relative as relative8, sep as sep11 } from "path";
|
|
45792
45939
|
function resolveSkillsNativeStorageConfig(env = process.env) {
|
|
45793
45940
|
assertNoRetiredModeEnvVars(env, {
|
|
45794
45941
|
app: SKILLS_ENV_NAMESPACE,
|
|
@@ -45829,7 +45976,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
45829
45976
|
local: {
|
|
45830
45977
|
dataDir: getDataDir(),
|
|
45831
45978
|
projectStateDir: getProjectStateDir(targetDir),
|
|
45832
|
-
feedbackDbPath:
|
|
45979
|
+
feedbackDbPath: join40(getDataDir(), "skills.db")
|
|
45833
45980
|
},
|
|
45834
45981
|
remote: {
|
|
45835
45982
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -45856,7 +46003,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
45856
46003
|
files.push({
|
|
45857
46004
|
path: relativePath,
|
|
45858
46005
|
sizeBytes: bytes.byteLength,
|
|
45859
|
-
sha256:
|
|
46006
|
+
sha256: createHash25("sha256").update(bytes).digest("hex"),
|
|
45860
46007
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
45861
46008
|
});
|
|
45862
46009
|
}
|
|
@@ -45910,7 +46057,7 @@ function parsePositiveInteger(value) {
|
|
|
45910
46057
|
function walkFiles2(dir) {
|
|
45911
46058
|
const files = [];
|
|
45912
46059
|
for (const entry of readdirSync12(dir)) {
|
|
45913
|
-
const full =
|
|
46060
|
+
const full = join40(dir, entry);
|
|
45914
46061
|
const stats = statSync12(full);
|
|
45915
46062
|
if (stats.isDirectory())
|
|
45916
46063
|
files.push(...walkFiles2(full));
|
|
@@ -46148,20 +46295,20 @@ var init_private_publication_customer = __esm(() => {
|
|
|
46148
46295
|
});
|
|
46149
46296
|
|
|
46150
46297
|
// src/lib/private-publication-recovery.ts
|
|
46151
|
-
import { constants as
|
|
46152
|
-
import { dirname as
|
|
46298
|
+
import { constants as constants17, closeSync as closeSync20, fsyncSync as fsyncSync6, fstatSync as fstatSync20, lstatSync as lstatSync24, mkdirSync as mkdirSync17, openSync as openSync20, readSync as readSync13, realpathSync as realpathSync12, renameSync as renameSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync14 } from "fs";
|
|
46299
|
+
import { dirname as dirname26, isAbsolute as isAbsolute23, join as join41, resolve as resolve24 } from "path";
|
|
46153
46300
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
46154
46301
|
function safeDirectory(directory2) {
|
|
46155
|
-
if (!
|
|
46302
|
+
if (!isAbsolute23(directory2) || directory2 !== resolve24(directory2) || realpathSync12(directory2) !== directory2)
|
|
46156
46303
|
return fail2();
|
|
46157
|
-
for (let path = directory2;; path =
|
|
46158
|
-
const stat3 =
|
|
46304
|
+
for (let path = directory2;; path = dirname26(path)) {
|
|
46305
|
+
const stat3 = lstatSync24(path);
|
|
46159
46306
|
if (!stat3.isDirectory() || stat3.isSymbolicLink())
|
|
46160
46307
|
return fail2();
|
|
46161
|
-
if (path ===
|
|
46308
|
+
if (path === dirname26(path))
|
|
46162
46309
|
break;
|
|
46163
46310
|
}
|
|
46164
|
-
const own2 =
|
|
46311
|
+
const own2 = lstatSync24(directory2);
|
|
46165
46312
|
if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
|
|
46166
46313
|
return fail2();
|
|
46167
46314
|
return { dev: own2.dev, ino: own2.ino };
|
|
@@ -46172,36 +46319,36 @@ function unchangedDirectory(directory2, identity3) {
|
|
|
46172
46319
|
return fail2();
|
|
46173
46320
|
}
|
|
46174
46321
|
function readOwned(directory2, name, max) {
|
|
46175
|
-
const identity3 = safeDirectory(directory2), file2 =
|
|
46176
|
-
const fd =
|
|
46322
|
+
const identity3 = safeDirectory(directory2), file2 = join41(directory2, name);
|
|
46323
|
+
const fd = openSync20(file2, constants17.O_RDONLY | constants17.O_NOFOLLOW);
|
|
46177
46324
|
try {
|
|
46178
|
-
const stat3 =
|
|
46325
|
+
const stat3 = fstatSync20(fd);
|
|
46179
46326
|
if (!stat3.isFile() || stat3.nlink !== 1 || stat3.size > max || stat3.size < 1 || (stat3.mode & 63) !== 0 || process.getuid && stat3.uid !== process.getuid())
|
|
46180
46327
|
return fail2();
|
|
46181
46328
|
const buffer = Buffer.alloc(Math.min(max, stat3.size) + 1);
|
|
46182
46329
|
let length = 0;
|
|
46183
46330
|
while (length < buffer.length) {
|
|
46184
|
-
const count =
|
|
46331
|
+
const count = readSync13(fd, buffer, length, buffer.length - length, length);
|
|
46185
46332
|
if (count === 0)
|
|
46186
46333
|
break;
|
|
46187
46334
|
length += count;
|
|
46188
46335
|
}
|
|
46189
|
-
const bytes = buffer.subarray(0, length), after =
|
|
46336
|
+
const bytes = buffer.subarray(0, length), after = fstatSync20(fd);
|
|
46190
46337
|
unchangedDirectory(directory2, identity3);
|
|
46191
46338
|
if (bytes.length !== stat3.size || stat3.size !== after.size || stat3.mtimeMs !== after.mtimeMs || stat3.ctimeMs !== after.ctimeMs)
|
|
46192
46339
|
return fail2();
|
|
46193
46340
|
return bytes;
|
|
46194
46341
|
} finally {
|
|
46195
|
-
|
|
46342
|
+
closeSync20(fd);
|
|
46196
46343
|
}
|
|
46197
46344
|
}
|
|
46198
46345
|
function writeOwned(directory2, name, bytes) {
|
|
46199
|
-
const fd =
|
|
46346
|
+
const fd = openSync20(join41(directory2, name), constants17.O_WRONLY | constants17.O_CREAT | constants17.O_EXCL | constants17.O_NOFOLLOW, 384);
|
|
46200
46347
|
try {
|
|
46201
46348
|
writeFileSync14(fd, bytes);
|
|
46202
|
-
|
|
46349
|
+
fsyncSync6(fd);
|
|
46203
46350
|
} finally {
|
|
46204
|
-
|
|
46351
|
+
closeSync20(fd);
|
|
46205
46352
|
}
|
|
46206
46353
|
}
|
|
46207
46354
|
function save(directory2, value) {
|
|
@@ -46209,12 +46356,12 @@ function save(directory2, value) {
|
|
|
46209
46356
|
writeOwned(directory2, temporary, JSON.stringify(value) + `
|
|
46210
46357
|
`);
|
|
46211
46358
|
unchangedDirectory(directory2, identity3);
|
|
46212
|
-
renameSync7(
|
|
46213
|
-
const fd =
|
|
46359
|
+
renameSync7(join41(directory2, temporary), join41(directory2, "receipt.json"));
|
|
46360
|
+
const fd = openSync20(directory2, constants17.O_RDONLY | constants17.O_NOFOLLOW);
|
|
46214
46361
|
try {
|
|
46215
|
-
|
|
46362
|
+
fsyncSync6(fd);
|
|
46216
46363
|
} finally {
|
|
46217
|
-
|
|
46364
|
+
closeSync20(fd);
|
|
46218
46365
|
}
|
|
46219
46366
|
}
|
|
46220
46367
|
function bind(client, receipt) {
|
|
@@ -46222,23 +46369,23 @@ function bind(client, receipt) {
|
|
|
46222
46369
|
throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
|
|
46223
46370
|
}
|
|
46224
46371
|
async function locked(directory2, action) {
|
|
46225
|
-
const identity3 = safeDirectory(directory2), file2 =
|
|
46372
|
+
const identity3 = safeDirectory(directory2), file2 = join41(directory2, "operation.lock");
|
|
46226
46373
|
let fd;
|
|
46227
46374
|
try {
|
|
46228
|
-
fd =
|
|
46375
|
+
fd = openSync20(file2, constants17.O_WRONLY | constants17.O_CREAT | constants17.O_EXCL | constants17.O_NOFOLLOW, 384);
|
|
46229
46376
|
} catch {
|
|
46230
46377
|
throw new PrivatePublicationError("PUBLICATION_RECOVERY_BUSY", "Another operation holds this recovery directory. If it crashed, confirm that process has stopped before explicitly removing operation.lock and resuming.");
|
|
46231
46378
|
}
|
|
46232
|
-
const lock =
|
|
46379
|
+
const lock = fstatSync20(fd);
|
|
46233
46380
|
try {
|
|
46234
46381
|
writeFileSync14(fd, JSON.stringify({ pid: process.pid }) + `
|
|
46235
46382
|
`);
|
|
46236
|
-
|
|
46383
|
+
fsyncSync6(fd);
|
|
46237
46384
|
return await action();
|
|
46238
46385
|
} finally {
|
|
46239
|
-
|
|
46386
|
+
closeSync20(fd);
|
|
46240
46387
|
unchangedDirectory(directory2, identity3);
|
|
46241
|
-
const now =
|
|
46388
|
+
const now = lstatSync24(file2);
|
|
46242
46389
|
if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
|
|
46243
46390
|
fail2();
|
|
46244
46391
|
unlinkSync6(file2);
|
|
@@ -46290,15 +46437,15 @@ async function preparePrivatePublication(client, sourceDirectory, recoveryDirect
|
|
|
46290
46437
|
phase: "prepared",
|
|
46291
46438
|
intent: null
|
|
46292
46439
|
};
|
|
46293
|
-
if (!
|
|
46440
|
+
if (!isAbsolute23(recoveryDirectory) || resolve24(recoveryDirectory) !== recoveryDirectory || realpathSync12(dirname26(recoveryDirectory)) !== dirname26(recoveryDirectory))
|
|
46294
46441
|
return fail2();
|
|
46295
|
-
|
|
46442
|
+
mkdirSync17(recoveryDirectory, { mode: 448 });
|
|
46296
46443
|
safeDirectory(recoveryDirectory);
|
|
46297
|
-
const parent =
|
|
46444
|
+
const parent = openSync20(dirname26(recoveryDirectory), constants17.O_RDONLY | constants17.O_NOFOLLOW);
|
|
46298
46445
|
try {
|
|
46299
|
-
|
|
46446
|
+
fsyncSync6(parent);
|
|
46300
46447
|
} finally {
|
|
46301
|
-
|
|
46448
|
+
closeSync20(parent);
|
|
46302
46449
|
}
|
|
46303
46450
|
writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
|
|
46304
46451
|
save(recoveryDirectory, receipt);
|
|
@@ -46391,10 +46538,10 @@ var init_private_publication_recovery = __esm(() => {
|
|
|
46391
46538
|
});
|
|
46392
46539
|
|
|
46393
46540
|
// src/mcp/private-publication-tools.ts
|
|
46394
|
-
import { isAbsolute as
|
|
46541
|
+
import { isAbsolute as isAbsolute24 } from "path";
|
|
46395
46542
|
function registerPrivatePublicationTools(server) {
|
|
46396
46543
|
const uuid9 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
46397
|
-
const directory2 = exports_external.string().max(4096).refine(
|
|
46544
|
+
const directory2 = exports_external.string().max(4096).refine(isAbsolute24, "Use an absolute local directory");
|
|
46398
46545
|
const verification2 = { email: exports_external.string().email().max(254), code: exports_external.string().regex(/^\d{6}$/), userId: uuid9, membershipId: uuid9, recoveryDirectory: directory2 };
|
|
46399
46546
|
const description = "Manage a private source publication using fresh workspace-bound verification and a host-local recovery directory. Publishing requires explicit UUID current-version comparison and upload consent. Resume reuses immutable saved bytes and intent; an uncertain PUT is finalized for server verification, never uploaded twice. Sessions and signed URLs are not returned or persisted. Private execution is unavailable. Host request history may contain the supplied verification code.";
|
|
46400
46547
|
const handler = (action) => async (value) => {
|
|
@@ -46827,19 +46974,19 @@ async function connectMcpForNode() {
|
|
|
46827
46974
|
return { server: server2, transport };
|
|
46828
46975
|
}
|
|
46829
46976
|
function readBody(req) {
|
|
46830
|
-
return new Promise((
|
|
46977
|
+
return new Promise((resolve25, reject) => {
|
|
46831
46978
|
const chunks = [];
|
|
46832
46979
|
req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
46833
46980
|
req.on("end", () => {
|
|
46834
46981
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
46835
46982
|
if (!raw.trim()) {
|
|
46836
|
-
|
|
46983
|
+
resolve25(undefined);
|
|
46837
46984
|
return;
|
|
46838
46985
|
}
|
|
46839
46986
|
try {
|
|
46840
|
-
|
|
46987
|
+
resolve25(JSON.parse(raw));
|
|
46841
46988
|
} catch {
|
|
46842
|
-
|
|
46989
|
+
resolve25(undefined);
|
|
46843
46990
|
}
|
|
46844
46991
|
});
|
|
46845
46992
|
req.on("error", reject);
|
|
@@ -46881,9 +47028,9 @@ async function startSkillsMcpHttpServer(options = {}) {
|
|
|
46881
47028
|
}
|
|
46882
47029
|
}
|
|
46883
47030
|
});
|
|
46884
|
-
await new Promise((
|
|
47031
|
+
await new Promise((resolve25, reject) => {
|
|
46885
47032
|
httpServer.once("error", reject);
|
|
46886
|
-
httpServer.listen(port, hostname4, () =>
|
|
47033
|
+
httpServer.listen(port, hostname4, () => resolve25());
|
|
46887
47034
|
});
|
|
46888
47035
|
const address = httpServer.address();
|
|
46889
47036
|
const listenPort = typeof address === "object" && address ? address.port : port;
|