@hasna/skills 0.10.15 → 0.10.16
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 +49 -4
- package/bin/index.js +4958 -4413
- package/bin/mcp.js +664 -502
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4193 -3563
- package/dist/lib/agent-discovery.d.ts +1 -1
- package/dist/lib/agent-integration.d.ts +40 -0
- package/dist/lib/claude-settings-witness.d.ts +4 -1
- package/dist/lib/codex-hook-rpc.d.ts +2 -1
- package/dist/lib/codex-native-skill-catalog.d.ts +36 -0
- package/dist/lib/codex-plugin-skill-controls.d.ts +16 -0
- package/dist/lib/codex-settings-witness.d.ts +12 -0
- package/dist/lib/hook-diagnostics.d.ts +2 -0
- package/dist/sdk/index.js +2152 -1528
- package/package.json +1 -1
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.10.
|
|
20022
|
+
version: "0.10.16",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -38251,6 +38251,17 @@ function assertAgentPolicyCollections(policy) {
|
|
|
38251
38251
|
for (const key of ["agent", "home", "alias", "target", "link", "aliasIdentity", "targetIdentity"])
|
|
38252
38252
|
text(alias[key]);
|
|
38253
38253
|
}
|
|
38254
|
+
if (bridge.codexPluginSkills !== undefined)
|
|
38255
|
+
for (const control of array3(bridge.codexPluginSkills, 4096)) {
|
|
38256
|
+
requireBound(object4(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256"].includes(key)));
|
|
38257
|
+
text(control.name, 129);
|
|
38258
|
+
text(control.pluginId, 1024);
|
|
38259
|
+
text(control.namespace, 64);
|
|
38260
|
+
text(control.pluginParent);
|
|
38261
|
+
requireBound(isAbsolute3(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
|
|
38262
|
+
if (control.appSha256 !== undefined)
|
|
38263
|
+
requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
|
|
38264
|
+
}
|
|
38254
38265
|
if (bridge.disabledBuiltins !== undefined)
|
|
38255
38266
|
for (const builtin of array3(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
|
|
38256
38267
|
requireBound(object4(builtin));
|
|
@@ -38270,7 +38281,9 @@ function assertAgentPolicyCollections(policy) {
|
|
|
38270
38281
|
text(source.path);
|
|
38271
38282
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
38272
38283
|
if (source.hashMode !== undefined)
|
|
38273
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
38284
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
38285
|
+
if (source.hashMode === "codex-settings-v1")
|
|
38286
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve2(source.path) === source.path && basename3(source.path) === "config.toml");
|
|
38274
38287
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
38275
38288
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve2(source.path) === source.path && basename3(source.path) === "settings.json");
|
|
38276
38289
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
|
|
@@ -45605,294 +45618,8 @@ var init_agent_discovery_path_bytes = __esm(() => {
|
|
|
45605
45618
|
init_agent_discovery_bytes();
|
|
45606
45619
|
});
|
|
45607
45620
|
|
|
45608
|
-
// src/lib/remote-files.ts
|
|
45609
|
-
var exports_remote_files = {};
|
|
45610
|
-
__export(exports_remote_files, {
|
|
45611
|
-
sha256: () => sha256,
|
|
45612
|
-
readBoundedResponse: () => readBoundedResponse,
|
|
45613
|
-
describeRemoteFiles: () => describeRemoteFiles,
|
|
45614
|
-
decodeRemoteFiles: () => decodeRemoteFiles,
|
|
45615
|
-
MAX_REMOTE_FILE_BYTES: () => MAX_REMOTE_FILE_BYTES
|
|
45616
|
-
});
|
|
45617
|
-
import { createHash as createHash5 } from "crypto";
|
|
45618
|
-
function describeRemoteFiles(files) {
|
|
45619
|
-
if (files.length > 10)
|
|
45620
|
-
throw new Error("At most 10 input files are supported");
|
|
45621
|
-
const names = new Set;
|
|
45622
|
-
let total = 0;
|
|
45623
|
-
return files.map((file2) => {
|
|
45624
|
-
if (typeof file2.name !== "string" || file2.name.length === 0 || file2.name.length > 255 || /[/\\\x00-\x1f\x7f]/.test(file2.name) || /[\ud800-\udfff]/u.test(file2.name) || file2.name === "." || file2.name === ".." || names.has(file2.name))
|
|
45625
|
-
throw new Error("Input file names must be unique safe basenames");
|
|
45626
|
-
names.add(file2.name);
|
|
45627
|
-
total += file2.bytes.byteLength;
|
|
45628
|
-
if (file2.bytes.byteLength > 20 * 1024 * 1024 || total > 50 * 1024 * 1024)
|
|
45629
|
-
throw new Error("Input files exceed the supported size limit");
|
|
45630
|
-
return { name: file2.name, sizeBytes: file2.bytes.byteLength, sha256: sha256(file2.bytes), contentType: file2.contentType ?? "application/octet-stream" };
|
|
45631
|
-
});
|
|
45632
|
-
}
|
|
45633
|
-
function sha256(bytes) {
|
|
45634
|
-
return createHash5("sha256").update(bytes).digest("hex");
|
|
45635
|
-
}
|
|
45636
|
-
async function readBoundedResponse(response, maximum) {
|
|
45637
|
-
if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
|
|
45638
|
-
throw new Error("Invalid artifact size limit");
|
|
45639
|
-
const length = response.headers.get("content-length");
|
|
45640
|
-
if (length && (!/^\d+$/.test(length) || Number(length) > maximum)) {
|
|
45641
|
-
await response.body?.cancel();
|
|
45642
|
-
throw new Error("Artifact exceeds its declared size limit");
|
|
45643
|
-
}
|
|
45644
|
-
const reader = response.body?.getReader();
|
|
45645
|
-
if (!reader)
|
|
45646
|
-
return new Uint8Array;
|
|
45647
|
-
const chunks = [];
|
|
45648
|
-
let size = 0;
|
|
45649
|
-
try {
|
|
45650
|
-
while (true) {
|
|
45651
|
-
const next = await reader.read();
|
|
45652
|
-
if (next.done)
|
|
45653
|
-
break;
|
|
45654
|
-
size += next.value.byteLength;
|
|
45655
|
-
if (size > maximum)
|
|
45656
|
-
throw new Error("Artifact exceeds its declared size limit");
|
|
45657
|
-
chunks.push(next.value);
|
|
45658
|
-
}
|
|
45659
|
-
} catch (error61) {
|
|
45660
|
-
await reader.cancel().catch(() => {});
|
|
45661
|
-
throw error61;
|
|
45662
|
-
} finally {
|
|
45663
|
-
reader.releaseLock();
|
|
45664
|
-
}
|
|
45665
|
-
const bytes = new Uint8Array(size);
|
|
45666
|
-
let offset = 0;
|
|
45667
|
-
for (const chunk of chunks) {
|
|
45668
|
-
bytes.set(chunk, offset);
|
|
45669
|
-
offset += chunk.byteLength;
|
|
45670
|
-
}
|
|
45671
|
-
return bytes;
|
|
45672
|
-
}
|
|
45673
|
-
function decodeRemoteFiles(files) {
|
|
45674
|
-
let total = 0;
|
|
45675
|
-
const decoded = files.map((file2) => {
|
|
45676
|
-
if (file2.base64.length > 1398104 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(file2.base64))
|
|
45677
|
-
throw new Error("Invalid inline input encoding");
|
|
45678
|
-
const bytes = Buffer.from(file2.base64, "base64");
|
|
45679
|
-
total += bytes.byteLength;
|
|
45680
|
-
if (total > 1024 * 1024 || bytes.toString("base64") !== file2.base64)
|
|
45681
|
-
throw new Error("Inline inputs must total at most 1 MiB");
|
|
45682
|
-
return { name: file2.name, bytes, contentType: file2.contentType };
|
|
45683
|
-
});
|
|
45684
|
-
describeRemoteFiles(decoded);
|
|
45685
|
-
return decoded;
|
|
45686
|
-
}
|
|
45687
|
-
var MAX_REMOTE_FILE_BYTES;
|
|
45688
|
-
var init_remote_files = __esm(() => {
|
|
45689
|
-
MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
45690
|
-
});
|
|
45691
|
-
|
|
45692
|
-
// src/lib/selection-aliases.ts
|
|
45693
|
-
function selectionAliasError(selections) {
|
|
45694
|
-
const names = new Set(selections.map((selection) => selection.slug));
|
|
45695
|
-
for (const selection of selections) {
|
|
45696
|
-
if (selection.aliases === undefined)
|
|
45697
|
-
continue;
|
|
45698
|
-
if (!Array.isArray(selection.aliases) || selection.aliases.length > 32)
|
|
45699
|
-
return "Selection aliases must be an array of at most 32 names.";
|
|
45700
|
-
for (const alias of selection.aliases) {
|
|
45701
|
-
if (typeof alias !== "string" || alias.length > 128 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(alias))
|
|
45702
|
-
return "Selection aliases must be bounded kebab-case names.";
|
|
45703
|
-
if (names.has(alias))
|
|
45704
|
-
return "Selection aliases must be unique and cannot shadow a selected canonical name.";
|
|
45705
|
-
names.add(alias);
|
|
45706
|
-
}
|
|
45707
|
-
}
|
|
45708
|
-
}
|
|
45709
|
-
function selectionSnapshotsEqual(left, right) {
|
|
45710
|
-
const snapshot = (selections) => selections.map((selection) => [
|
|
45711
|
-
selection.slug,
|
|
45712
|
-
selection.version,
|
|
45713
|
-
selection.bundleDigest,
|
|
45714
|
-
selection.aliases ?? [],
|
|
45715
|
-
selection.triggers?.keywords ?? [],
|
|
45716
|
-
selection.triggers?.paths ?? [],
|
|
45717
|
-
selection.triggers?.always ?? null
|
|
45718
|
-
]).sort((a, b) => String(a[0]).localeCompare(String(b[0])));
|
|
45719
|
-
return JSON.stringify(snapshot(left)) === JSON.stringify(snapshot(right));
|
|
45720
|
-
}
|
|
45721
|
-
|
|
45722
|
-
// src/lib/profile-limits.ts
|
|
45723
|
-
function profileDocumentBytes(value) {
|
|
45724
|
-
return new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
45725
|
-
}
|
|
45726
|
-
function requiresProfileCapacity(selections, bodyBytes) {
|
|
45727
|
-
return selections.length > LEGACY_PROFILE_SELECTIONS || bodyBytes > LEGACY_PROFILE_DOCUMENT_BYTES;
|
|
45728
|
-
}
|
|
45729
|
-
function assertAdvertisedProfileCapacity(value, count, bodyBytes) {
|
|
45730
|
-
const limits = value?.profileLimits;
|
|
45731
|
-
if (!limits || !Number.isSafeInteger(limits.maxSelections) || Number(limits.maxSelections) < count || !Number.isSafeInteger(limits.maxDocumentBytes) || Number(limits.maxDocumentBytes) < bodyBytes || !Number.isSafeInteger(limits.requestBodyLimitBytes) || Number(limits.requestBodyLimitBytes) < bodyBytes || !Number.isSafeInteger(limits.maxResolvedProfileBytes) || Number(limits.maxResolvedProfileBytes) <= 0) {
|
|
45732
|
-
throw new Error("The Skills API does not advertise enough profile capacity. Review the API version and its configured request-body limit before saving this profile.");
|
|
45733
|
-
}
|
|
45734
|
-
}
|
|
45735
|
-
function profileSelectionSnapshot(selections) {
|
|
45736
|
-
return selections.map(({ slug, version: version2, bundleDigest, aliases, triggers }) => ({
|
|
45737
|
-
slug,
|
|
45738
|
-
version: version2,
|
|
45739
|
-
bundleDigest,
|
|
45740
|
-
...aliases !== undefined ? { aliases } : {},
|
|
45741
|
-
...triggers !== undefined ? { triggers } : {}
|
|
45742
|
-
}));
|
|
45743
|
-
}
|
|
45744
|
-
var MAX_PROFILE_SELECTIONS = 4096, MAX_PROFILE_DOCUMENT_BYTES, MAX_SKILL_SESSION_ID_CHARS = 256, PROFILE_SESSION_ENVELOPE_BYTES, MAX_RESOLVED_PROFILE_BYTES, LEGACY_PROFILE_SELECTIONS = 256, LEGACY_PROFILE_DOCUMENT_BYTES = 1e6;
|
|
45745
|
-
var init_profile_limits = __esm(() => {
|
|
45746
|
-
MAX_PROFILE_DOCUMENT_BYTES = 8 * 1024 * 1024;
|
|
45747
|
-
PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 12 + 512;
|
|
45748
|
-
MAX_RESOLVED_PROFILE_BYTES = MAX_PROFILE_DOCUMENT_BYTES - PROFILE_SESSION_ENVELOPE_BYTES;
|
|
45749
|
-
});
|
|
45750
|
-
|
|
45751
|
-
// src/lib/profile-client.ts
|
|
45752
|
-
function object6(value) {
|
|
45753
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
45754
|
-
}
|
|
45755
|
-
function identifier(value) {
|
|
45756
|
-
return typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(value) && !value.includes("..");
|
|
45757
|
-
}
|
|
45758
|
-
function invalid() {
|
|
45759
|
-
throw new Error("The Skills API returned an invalid profile response");
|
|
45760
|
-
}
|
|
45761
|
-
function validateSelection(value) {
|
|
45762
|
-
if (!object6(value) || !identifier(value.slug) || typeof value.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(value.version) || value.version.includes("..") || typeof value.bundleDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.bundleDigest))
|
|
45763
|
-
invalid();
|
|
45764
|
-
if (selectionAliasError([value]))
|
|
45765
|
-
invalid();
|
|
45766
|
-
if (value.triggers !== undefined) {
|
|
45767
|
-
if (!object6(value.triggers) || Object.keys(value.triggers).some((key) => !["keywords", "paths", "always"].includes(key)))
|
|
45768
|
-
invalid();
|
|
45769
|
-
if (value.triggers.always !== undefined && typeof value.triggers.always !== "boolean")
|
|
45770
|
-
invalid();
|
|
45771
|
-
for (const key of ["keywords", "paths"]) {
|
|
45772
|
-
const values = value.triggers[key];
|
|
45773
|
-
if (values !== undefined && (!Array.isArray(values) || values.length > 32 || values.some((term) => typeof term !== "string" || !term.trim() || term.length > 256)))
|
|
45774
|
-
invalid();
|
|
45775
|
-
}
|
|
45776
|
-
}
|
|
45777
|
-
}
|
|
45778
|
-
|
|
45779
|
-
class HttpProfileClient {
|
|
45780
|
-
key;
|
|
45781
|
-
authority;
|
|
45782
|
-
origin;
|
|
45783
|
-
constructor(key, origin) {
|
|
45784
|
-
this.key = key;
|
|
45785
|
-
if (!key.trim())
|
|
45786
|
-
throw new Error("A Skills credential is required");
|
|
45787
|
-
this.origin = normalizeSkillsApiOrigin(origin);
|
|
45788
|
-
this.authority = skillsApiRequestUrl(this.origin, "/api/v1/").replace(/\/+$/, "");
|
|
45789
|
-
}
|
|
45790
|
-
async requestRoute(route, init = {}) {
|
|
45791
|
-
let response;
|
|
45792
|
-
try {
|
|
45793
|
-
response = await fetch(skillsApiRequestUrl(this.origin, route), {
|
|
45794
|
-
...init,
|
|
45795
|
-
redirect: "error",
|
|
45796
|
-
credentials: "omit",
|
|
45797
|
-
signal: AbortSignal.timeout(15000),
|
|
45798
|
-
headers: {
|
|
45799
|
-
"Content-Type": "application/json",
|
|
45800
|
-
"User-Agent": `hasna-skills/${package_default.version}`,
|
|
45801
|
-
Authorization: `Bearer ${this.key}`,
|
|
45802
|
-
...init.headers
|
|
45803
|
-
}
|
|
45804
|
-
});
|
|
45805
|
-
} catch {
|
|
45806
|
-
throw new Error("Unable to reach the configured Skills API");
|
|
45807
|
-
}
|
|
45808
|
-
if (!response.ok) {
|
|
45809
|
-
response.body?.cancel().catch(() => {});
|
|
45810
|
-
throw new Error(`Skills API request failed (HTTP ${response.status})`);
|
|
45811
|
-
}
|
|
45812
|
-
return response;
|
|
45813
|
-
}
|
|
45814
|
-
request(path, init = {}) {
|
|
45815
|
-
return this.requestRoute(`/api/v1${path}`, init);
|
|
45816
|
-
}
|
|
45817
|
-
async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
|
|
45818
|
-
try {
|
|
45819
|
-
return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit)));
|
|
45820
|
-
} catch {
|
|
45821
|
-
invalid();
|
|
45822
|
-
}
|
|
45823
|
-
}
|
|
45824
|
-
async resolveProfile(id) {
|
|
45825
|
-
if (!identifier(id))
|
|
45826
|
-
throw new Error("Invalid selection profile id");
|
|
45827
|
-
const result = await this.read(await this.request(`/profiles/${encodeURIComponent(id)}/resolve`), MAX_RESOLVED_PROFILE_BYTES);
|
|
45828
|
-
if (!object6(result) || result.profileId !== id || result.authority !== this.authority || !identifier(result.workspaceId) || !identifier(result.profileRevision) || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
|
|
45829
|
-
invalid();
|
|
45830
|
-
const seen = new Set;
|
|
45831
|
-
for (const entry of result.selections) {
|
|
45832
|
-
validateSelection(entry);
|
|
45833
|
-
const item = entry;
|
|
45834
|
-
if (seen.has(item.slug) || item.authority !== this.authority || item.workspaceId !== result.workspaceId || item.profileRevision !== result.profileRevision)
|
|
45835
|
-
invalid();
|
|
45836
|
-
seen.add(item.slug);
|
|
45837
|
-
}
|
|
45838
|
-
if (selectionAliasError(result.selections))
|
|
45839
|
-
invalid();
|
|
45840
|
-
return result;
|
|
45841
|
-
}
|
|
45842
|
-
async resolvePrincipal() {
|
|
45843
|
-
const result = await this.read(await this.requestRoute("/api/auth/whoami"), 16 * 1024);
|
|
45844
|
-
if (!object6(result) || Object.keys(result).sort().join(",") !== "organization,user" || !object6(result.user) || Object.keys(result.user).sort().join(",") !== "email,id,role" || !object6(result.organization) || Object.keys(result.organization).sort().join(",") !== "id,name,slug" || !identifier(result.user.id) || typeof result.user.email !== "string" || !result.user.email.trim() || !["owner", "admin", "member", "viewer"].includes(String(result.user.role)) || !identifier(result.organization.id) || !identifier(result.organization.slug) || typeof result.organization.name !== "string" || !result.organization.name.trim())
|
|
45845
|
-
invalid();
|
|
45846
|
-
return { userId: result.user.id, accountId: result.organization.id, role: result.user.role };
|
|
45847
|
-
}
|
|
45848
|
-
async recordStation(id, input2) {
|
|
45849
|
-
if (!identifier(id) || !identifier(input2.profileId) || !identifier(input2.profileRevision) || !Array.isArray(input2.selections) || input2.selections.length > MAX_PROFILE_SELECTIONS)
|
|
45850
|
-
throw new Error("Invalid station state");
|
|
45851
|
-
input2.selections.forEach(validateSelection);
|
|
45852
|
-
if (selectionAliasError(input2.selections))
|
|
45853
|
-
throw new Error("Invalid station selection aliases");
|
|
45854
|
-
const snapshot = { ...input2, selections: profileSelectionSnapshot(input2.selections) };
|
|
45855
|
-
const bodyBytes = profileDocumentBytes(snapshot);
|
|
45856
|
-
if (bodyBytes > MAX_PROFILE_DOCUMENT_BYTES)
|
|
45857
|
-
throw new Error("Station state exceeds the profile document size limit");
|
|
45858
|
-
if (requiresProfileCapacity(input2.selections, bodyBytes)) {
|
|
45859
|
-
const capabilities = await this.read(await this.request("/capabilities"));
|
|
45860
|
-
assertAdvertisedProfileCapacity(capabilities, input2.selections.length, bodyBytes);
|
|
45861
|
-
}
|
|
45862
|
-
const result = await this.read(await this.request(`/stations/${encodeURIComponent(id)}/state`, {
|
|
45863
|
-
method: "PUT",
|
|
45864
|
-
body: JSON.stringify(snapshot)
|
|
45865
|
-
}));
|
|
45866
|
-
if (!object6(result) || result.stationId !== id || result.profileId !== input2.profileId || result.profileRevision !== input2.profileRevision || !identifier(result.workspaceId) || !identifier(result.actorId) || typeof result.appliedAt !== "string" || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
|
|
45867
|
-
invalid();
|
|
45868
|
-
result.selections.forEach(validateSelection);
|
|
45869
|
-
if (selectionAliasError(result.selections))
|
|
45870
|
-
invalid();
|
|
45871
|
-
if (!selectionSnapshotsEqual(input2.selections, result.selections))
|
|
45872
|
-
invalid();
|
|
45873
|
-
return result;
|
|
45874
|
-
}
|
|
45875
|
-
async getBundle(slug, version2) {
|
|
45876
|
-
if (!identifier(slug) || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2) || version2.includes(".."))
|
|
45877
|
-
throw new Error("Invalid immutable skill reference");
|
|
45878
|
-
return this.request(`/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version2)}/bundle`);
|
|
45879
|
-
}
|
|
45880
|
-
}
|
|
45881
|
-
async function createProfileClient() {
|
|
45882
|
-
const connection = await resolveSkillsConnection();
|
|
45883
|
-
if (!connection)
|
|
45884
|
-
throw new Error("Selection profiles require a configured Skills API");
|
|
45885
|
-
return new HttpProfileClient(connection.apiKey, connection.apiOrigin);
|
|
45886
|
-
}
|
|
45887
|
-
var init_profile_client = __esm(() => {
|
|
45888
|
-
init_package();
|
|
45889
|
-
init_fleet_credentials();
|
|
45890
|
-
init_remote_files();
|
|
45891
|
-
init_profile_limits();
|
|
45892
|
-
});
|
|
45893
|
-
|
|
45894
45621
|
// src/lib/skill-bundle.ts
|
|
45895
|
-
import { createHash as
|
|
45622
|
+
import { createHash as createHash5 } from "crypto";
|
|
45896
45623
|
import { readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync6 } from "fs";
|
|
45897
45624
|
import { join as join13, relative as relative3 } from "path";
|
|
45898
45625
|
import { createGunzip } from "zlib";
|
|
@@ -45924,7 +45651,7 @@ function ownBytes(view) {
|
|
|
45924
45651
|
return out;
|
|
45925
45652
|
}
|
|
45926
45653
|
function sha256Hex(bytes) {
|
|
45927
|
-
return
|
|
45654
|
+
return createHash5("sha256").update(bytes).digest("hex");
|
|
45928
45655
|
}
|
|
45929
45656
|
function collectSkillBundleEntries(dir) {
|
|
45930
45657
|
const entries = [];
|
|
@@ -46076,7 +45803,7 @@ async function inspectSkillBundle(bundle, options = {}) {
|
|
|
46076
45803
|
throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Compressed bundle exceeds byte limit");
|
|
46077
45804
|
const snapshot = ownBytes(bundle);
|
|
46078
45805
|
check2();
|
|
46079
|
-
const
|
|
45806
|
+
const sha256 = sha256Hex(snapshot);
|
|
46080
45807
|
check2();
|
|
46081
45808
|
const parser2 = new BoundedTarReader(limits, check2);
|
|
46082
45809
|
const streamOptions = { chunkSize: 16 * 1024, highWaterMark: 16 * 1024 };
|
|
@@ -46111,7 +45838,7 @@ async function inspectSkillBundle(bundle, options = {}) {
|
|
|
46111
45838
|
const entries = parser2.finish();
|
|
46112
45839
|
return {
|
|
46113
45840
|
entries,
|
|
46114
|
-
sha256
|
|
45841
|
+
sha256,
|
|
46115
45842
|
compressedByteSize: snapshot.byteLength,
|
|
46116
45843
|
decompressedByteSize,
|
|
46117
45844
|
unpackedByteSize: parser2.fileBytes,
|
|
@@ -46399,8 +46126,67 @@ var init_skill_version = __esm(() => {
|
|
|
46399
46126
|
SKILL_VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/;
|
|
46400
46127
|
});
|
|
46401
46128
|
|
|
46129
|
+
// src/lib/selection-aliases.ts
|
|
46130
|
+
function selectionAliasError(selections) {
|
|
46131
|
+
const names = new Set(selections.map((selection) => selection.slug));
|
|
46132
|
+
for (const selection of selections) {
|
|
46133
|
+
if (selection.aliases === undefined)
|
|
46134
|
+
continue;
|
|
46135
|
+
if (!Array.isArray(selection.aliases) || selection.aliases.length > 32)
|
|
46136
|
+
return "Selection aliases must be an array of at most 32 names.";
|
|
46137
|
+
for (const alias of selection.aliases) {
|
|
46138
|
+
if (typeof alias !== "string" || alias.length > 128 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(alias))
|
|
46139
|
+
return "Selection aliases must be bounded kebab-case names.";
|
|
46140
|
+
if (names.has(alias))
|
|
46141
|
+
return "Selection aliases must be unique and cannot shadow a selected canonical name.";
|
|
46142
|
+
names.add(alias);
|
|
46143
|
+
}
|
|
46144
|
+
}
|
|
46145
|
+
}
|
|
46146
|
+
function selectionSnapshotsEqual(left, right) {
|
|
46147
|
+
const snapshot = (selections) => selections.map((selection) => [
|
|
46148
|
+
selection.slug,
|
|
46149
|
+
selection.version,
|
|
46150
|
+
selection.bundleDigest,
|
|
46151
|
+
selection.aliases ?? [],
|
|
46152
|
+
selection.triggers?.keywords ?? [],
|
|
46153
|
+
selection.triggers?.paths ?? [],
|
|
46154
|
+
selection.triggers?.always ?? null
|
|
46155
|
+
]).sort((a, b) => String(a[0]).localeCompare(String(b[0])));
|
|
46156
|
+
return JSON.stringify(snapshot(left)) === JSON.stringify(snapshot(right));
|
|
46157
|
+
}
|
|
46158
|
+
|
|
46159
|
+
// src/lib/profile-limits.ts
|
|
46160
|
+
function profileDocumentBytes(value) {
|
|
46161
|
+
return new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
46162
|
+
}
|
|
46163
|
+
function requiresProfileCapacity(selections, bodyBytes) {
|
|
46164
|
+
return selections.length > LEGACY_PROFILE_SELECTIONS || bodyBytes > LEGACY_PROFILE_DOCUMENT_BYTES;
|
|
46165
|
+
}
|
|
46166
|
+
function assertAdvertisedProfileCapacity(value, count, bodyBytes) {
|
|
46167
|
+
const limits = value?.profileLimits;
|
|
46168
|
+
if (!limits || !Number.isSafeInteger(limits.maxSelections) || Number(limits.maxSelections) < count || !Number.isSafeInteger(limits.maxDocumentBytes) || Number(limits.maxDocumentBytes) < bodyBytes || !Number.isSafeInteger(limits.requestBodyLimitBytes) || Number(limits.requestBodyLimitBytes) < bodyBytes || !Number.isSafeInteger(limits.maxResolvedProfileBytes) || Number(limits.maxResolvedProfileBytes) <= 0) {
|
|
46169
|
+
throw new Error("The Skills API does not advertise enough profile capacity. Review the API version and its configured request-body limit before saving this profile.");
|
|
46170
|
+
}
|
|
46171
|
+
}
|
|
46172
|
+
function profileSelectionSnapshot(selections) {
|
|
46173
|
+
return selections.map(({ slug, version: version2, bundleDigest, aliases, triggers }) => ({
|
|
46174
|
+
slug,
|
|
46175
|
+
version: version2,
|
|
46176
|
+
bundleDigest,
|
|
46177
|
+
...aliases !== undefined ? { aliases } : {},
|
|
46178
|
+
...triggers !== undefined ? { triggers } : {}
|
|
46179
|
+
}));
|
|
46180
|
+
}
|
|
46181
|
+
var MAX_PROFILE_SELECTIONS = 4096, MAX_PROFILE_DOCUMENT_BYTES, MAX_SKILL_SESSION_ID_CHARS = 256, PROFILE_SESSION_ENVELOPE_BYTES, MAX_RESOLVED_PROFILE_BYTES, LEGACY_PROFILE_SELECTIONS = 256, LEGACY_PROFILE_DOCUMENT_BYTES = 1e6;
|
|
46182
|
+
var init_profile_limits = __esm(() => {
|
|
46183
|
+
MAX_PROFILE_DOCUMENT_BYTES = 8 * 1024 * 1024;
|
|
46184
|
+
PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 12 + 512;
|
|
46185
|
+
MAX_RESOLVED_PROFILE_BYTES = MAX_PROFILE_DOCUMENT_BYTES - PROFILE_SESSION_ENVELOPE_BYTES;
|
|
46186
|
+
});
|
|
46187
|
+
|
|
46402
46188
|
// src/lib/selection-cache.ts
|
|
46403
|
-
import { createHash as
|
|
46189
|
+
import { createHash as createHash6, randomUUID } from "crypto";
|
|
46404
46190
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
46405
46191
|
import { closeSync as closeSync6, constants as constants3, fstatSync as fstatSync6, fsyncSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync6, readFileSync as readFileSync10, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
46406
46192
|
import { hostname as hostname3 } from "os";
|
|
@@ -46410,13 +46196,13 @@ function selectionCacheRoot(options = {}) {
|
|
|
46410
46196
|
return resolve7(options.cacheDir ?? join14(getDataDirReadOnly(), "selection-cache"));
|
|
46411
46197
|
}
|
|
46412
46198
|
function selectionKey(selection) {
|
|
46413
|
-
|
|
46199
|
+
validateSelection(selection);
|
|
46414
46200
|
return hash2([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
|
|
46415
46201
|
}
|
|
46416
46202
|
function hash2(value) {
|
|
46417
|
-
return
|
|
46203
|
+
return createHash6("sha256").update(JSON.stringify(value)).digest("hex");
|
|
46418
46204
|
}
|
|
46419
|
-
function
|
|
46205
|
+
function validateSelection(selection) {
|
|
46420
46206
|
if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
|
|
46421
46207
|
throw new SkillSelectionError("INVALID_SELECTION", "A skill selection must carry an exact version, digest, workspace and profile revision.");
|
|
46422
46208
|
}
|
|
@@ -46439,7 +46225,7 @@ function validateResolvedProfile(profile, authority) {
|
|
|
46439
46225
|
throw new SkillSelectionError("RECEIPT_TOO_LARGE", "The resolved Skills profile exceeds its size limit.");
|
|
46440
46226
|
const slugs = new Set;
|
|
46441
46227
|
for (const selection of profile.selections) {
|
|
46442
|
-
|
|
46228
|
+
validateSelection(selection);
|
|
46443
46229
|
if (selection.authority !== profile.authority || selection.workspaceId !== profile.workspaceId || selection.profileRevision !== profile.profileRevision || slugs.has(selection.slug)) {
|
|
46444
46230
|
throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "The resolved profile contains conflicting selection identities.");
|
|
46445
46231
|
}
|
|
@@ -46450,7 +46236,7 @@ function validateResolvedProfile(profile, authority) {
|
|
|
46450
46236
|
throw new SkillSelectionError("INVALID_SELECTION_ALIASES", aliasError);
|
|
46451
46237
|
}
|
|
46452
46238
|
function selectionBundlePath(selection, options = {}) {
|
|
46453
|
-
|
|
46239
|
+
validateSelection(selection);
|
|
46454
46240
|
return join14(selectionCacheRoot(options), "objects", hash2(selection.authority), hash2(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
|
|
46455
46241
|
}
|
|
46456
46242
|
function assertRegularPath(path, createParents = false) {
|
|
@@ -46554,7 +46340,7 @@ async function readCachedSelection(selection, options = {}) {
|
|
|
46554
46340
|
return bytes ? verifiedEntries(selection, bytes) : null;
|
|
46555
46341
|
}
|
|
46556
46342
|
async function cacheSelectionBundle(selection, response, options = {}) {
|
|
46557
|
-
|
|
46343
|
+
validateSelection(selection);
|
|
46558
46344
|
if (!response?.ok)
|
|
46559
46345
|
throw new SkillSelectionError("BUNDLE_UNAVAILABLE", `The selected skill bundle is unavailable${response ? ` (HTTP ${response.status})` : ""}.`);
|
|
46560
46346
|
const declared = response.headers.get("X-Skill-Bundle-Sha256");
|
|
@@ -46618,7 +46404,11 @@ function assertFreshCachedProfile(receipt, options = {}) {
|
|
|
46618
46404
|
validateSelectionReceipt(receipt);
|
|
46619
46405
|
const age = (options.now ?? Date.now)() - Date.parse(receipt.verifiedAt);
|
|
46620
46406
|
const maximum = options.maxAgeMs ?? MAX_CACHED_PROFILE_AGE_MS;
|
|
46621
|
-
if (!Number.isFinite(maximum) || maximum <= 0 || maximum > MAX_CACHED_PROFILE_AGE_MS
|
|
46407
|
+
if (!Number.isFinite(maximum) || maximum <= 0 || maximum > MAX_CACHED_PROFILE_AGE_MS)
|
|
46408
|
+
throw new SkillSelectionError("INVALID_CONTEXT_BUDGET", "The cached profile maximum age must be positive and within the supported bound.");
|
|
46409
|
+
if (!Number.isFinite(age) || age < 0)
|
|
46410
|
+
throw new SkillSelectionError("INVALID_RECEIPT", "The cached profile verification timestamp is invalid or in the future.");
|
|
46411
|
+
if (age > maximum) {
|
|
46622
46412
|
throw new SkillSelectionError("CACHED_PROFILE_EXPIRED", "The cached Skills profile has expired; authenticate and sync it again.");
|
|
46623
46413
|
}
|
|
46624
46414
|
}
|
|
@@ -46667,8 +46457,8 @@ function readSkillSessionSnapshotIfExists(sessionId, options = {}) {
|
|
|
46667
46457
|
throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt is unreadable.");
|
|
46668
46458
|
}
|
|
46669
46459
|
validateSkillSessionReceipt(receipt, sessionId);
|
|
46670
|
-
const
|
|
46671
|
-
return { path, bytes, receipt, sha256
|
|
46460
|
+
const sha256 = sha256Hex(bytes);
|
|
46461
|
+
return { path, bytes, receipt, sha256, sessionId, generation: skillSessionGeneration(receipt), receiptSha256: sha256 };
|
|
46672
46462
|
}
|
|
46673
46463
|
function skillSessionSnapshotBinding(snapshot) {
|
|
46674
46464
|
return { sessionId: snapshot.sessionId, generation: snapshot.generation, receiptSha256: snapshot.receiptSha256 };
|
|
@@ -46734,12 +46524,12 @@ function readSessionLock(sessionId, options) {
|
|
|
46734
46524
|
const before = lstatSync8(path, { throwIfNoEntry: false });
|
|
46735
46525
|
if (!before)
|
|
46736
46526
|
return null;
|
|
46737
|
-
if (!before.isFile() || before.nlink !== 1 || process.getuid && before.uid !== process.getuid()) {
|
|
46527
|
+
if (!before.isFile() || before.nlink !== 1 || (before.mode & 63) !== 0 || process.getuid && before.uid !== process.getuid()) {
|
|
46738
46528
|
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is not a private regular file; recovery requires review.");
|
|
46739
46529
|
}
|
|
46740
46530
|
const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
|
|
46741
46531
|
const after = lstatSync8(path, { throwIfNoEntry: false });
|
|
46742
|
-
if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs) {
|
|
46532
|
+
if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.mode !== after.mode || before.uid !== after.uid || before.gid !== after.gid || before.nlink !== after.nlink) {
|
|
46743
46533
|
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock changed during inspection; recovery requires review.");
|
|
46744
46534
|
}
|
|
46745
46535
|
let marker;
|
|
@@ -46865,8 +46655,11 @@ function withSessionWriteLocks(sessionIds, options, action) {
|
|
|
46865
46655
|
try {
|
|
46866
46656
|
fd = openSync6(lock.path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
46867
46657
|
} catch (retryError) {
|
|
46868
|
-
if (retryError.code === "EEXIST")
|
|
46869
|
-
|
|
46658
|
+
if (retryError.code === "EEXIST") {
|
|
46659
|
+
if (!readSessionLock(lock.sessionId, options))
|
|
46660
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "The session lock changed during acquisition.");
|
|
46661
|
+
throw new SkillSelectionError("SESSION_WRITE_BUSY", "Another Skills process owns a required session write lock; retry after it completes. A surviving legacy lock requires explicit recovery review.");
|
|
46662
|
+
}
|
|
46870
46663
|
throw retryError;
|
|
46871
46664
|
}
|
|
46872
46665
|
}
|
|
@@ -46908,7 +46701,7 @@ function writeSkillSession(receipt, expected, options = {}) {
|
|
|
46908
46701
|
withSessionWriteLocks(lockIds, options, (assertOwned) => {
|
|
46909
46702
|
const current = readSkillSessionSnapshotIfExists(receipt.sessionId, options);
|
|
46910
46703
|
if (!sameSnapshot(current, expected.current) || current && !isDeepStrictEqual(current.receipt.profile, receipt.profile)) {
|
|
46911
|
-
throw new SkillSelectionError("
|
|
46704
|
+
throw new SkillSelectionError("SESSION_CONTEXT_CONFLICT", "The Skills session receipt changed while context was loading; resolve context again without replacing the current pin.");
|
|
46912
46705
|
}
|
|
46913
46706
|
let parent = null;
|
|
46914
46707
|
if (expected.parent) {
|
|
@@ -46963,6 +46756,246 @@ var init_selection_cache = __esm(() => {
|
|
|
46963
46756
|
MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
46964
46757
|
});
|
|
46965
46758
|
|
|
46759
|
+
// src/lib/remote-files.ts
|
|
46760
|
+
var exports_remote_files = {};
|
|
46761
|
+
__export(exports_remote_files, {
|
|
46762
|
+
sha256: () => sha256,
|
|
46763
|
+
readBoundedResponse: () => readBoundedResponse,
|
|
46764
|
+
describeRemoteFiles: () => describeRemoteFiles,
|
|
46765
|
+
decodeRemoteFiles: () => decodeRemoteFiles,
|
|
46766
|
+
MAX_REMOTE_FILE_BYTES: () => MAX_REMOTE_FILE_BYTES
|
|
46767
|
+
});
|
|
46768
|
+
import { createHash as createHash7 } from "crypto";
|
|
46769
|
+
function describeRemoteFiles(files) {
|
|
46770
|
+
if (files.length > 10)
|
|
46771
|
+
throw new Error("At most 10 input files are supported");
|
|
46772
|
+
const names = new Set;
|
|
46773
|
+
let total = 0;
|
|
46774
|
+
return files.map((file2) => {
|
|
46775
|
+
if (typeof file2.name !== "string" || file2.name.length === 0 || file2.name.length > 255 || /[/\\\x00-\x1f\x7f]/.test(file2.name) || /[\ud800-\udfff]/u.test(file2.name) || file2.name === "." || file2.name === ".." || names.has(file2.name))
|
|
46776
|
+
throw new Error("Input file names must be unique safe basenames");
|
|
46777
|
+
names.add(file2.name);
|
|
46778
|
+
total += file2.bytes.byteLength;
|
|
46779
|
+
if (file2.bytes.byteLength > 20 * 1024 * 1024 || total > 50 * 1024 * 1024)
|
|
46780
|
+
throw new Error("Input files exceed the supported size limit");
|
|
46781
|
+
return { name: file2.name, sizeBytes: file2.bytes.byteLength, sha256: sha256(file2.bytes), contentType: file2.contentType ?? "application/octet-stream" };
|
|
46782
|
+
});
|
|
46783
|
+
}
|
|
46784
|
+
function sha256(bytes) {
|
|
46785
|
+
return createHash7("sha256").update(bytes).digest("hex");
|
|
46786
|
+
}
|
|
46787
|
+
async function readBoundedResponse(response, maximum) {
|
|
46788
|
+
if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
|
|
46789
|
+
throw new Error("Invalid artifact size limit");
|
|
46790
|
+
const length = response.headers.get("content-length");
|
|
46791
|
+
if (length && (!/^\d+$/.test(length) || Number(length) > maximum)) {
|
|
46792
|
+
await response.body?.cancel();
|
|
46793
|
+
throw new Error("Artifact exceeds its declared size limit");
|
|
46794
|
+
}
|
|
46795
|
+
const reader = response.body?.getReader();
|
|
46796
|
+
if (!reader)
|
|
46797
|
+
return new Uint8Array;
|
|
46798
|
+
const chunks = [];
|
|
46799
|
+
let size = 0;
|
|
46800
|
+
try {
|
|
46801
|
+
while (true) {
|
|
46802
|
+
const next = await reader.read();
|
|
46803
|
+
if (next.done)
|
|
46804
|
+
break;
|
|
46805
|
+
size += next.value.byteLength;
|
|
46806
|
+
if (size > maximum)
|
|
46807
|
+
throw new Error("Artifact exceeds its declared size limit");
|
|
46808
|
+
chunks.push(next.value);
|
|
46809
|
+
}
|
|
46810
|
+
} catch (error61) {
|
|
46811
|
+
await reader.cancel().catch(() => {});
|
|
46812
|
+
throw error61;
|
|
46813
|
+
} finally {
|
|
46814
|
+
reader.releaseLock();
|
|
46815
|
+
}
|
|
46816
|
+
const bytes = new Uint8Array(size);
|
|
46817
|
+
let offset = 0;
|
|
46818
|
+
for (const chunk of chunks) {
|
|
46819
|
+
bytes.set(chunk, offset);
|
|
46820
|
+
offset += chunk.byteLength;
|
|
46821
|
+
}
|
|
46822
|
+
return bytes;
|
|
46823
|
+
}
|
|
46824
|
+
function decodeRemoteFiles(files) {
|
|
46825
|
+
let total = 0;
|
|
46826
|
+
const decoded = files.map((file2) => {
|
|
46827
|
+
if (file2.base64.length > 1398104 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(file2.base64))
|
|
46828
|
+
throw new Error("Invalid inline input encoding");
|
|
46829
|
+
const bytes = Buffer.from(file2.base64, "base64");
|
|
46830
|
+
total += bytes.byteLength;
|
|
46831
|
+
if (total > 1024 * 1024 || bytes.toString("base64") !== file2.base64)
|
|
46832
|
+
throw new Error("Inline inputs must total at most 1 MiB");
|
|
46833
|
+
return { name: file2.name, bytes, contentType: file2.contentType };
|
|
46834
|
+
});
|
|
46835
|
+
describeRemoteFiles(decoded);
|
|
46836
|
+
return decoded;
|
|
46837
|
+
}
|
|
46838
|
+
var MAX_REMOTE_FILE_BYTES;
|
|
46839
|
+
var init_remote_files = __esm(() => {
|
|
46840
|
+
MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
46841
|
+
});
|
|
46842
|
+
|
|
46843
|
+
// src/lib/profile-client.ts
|
|
46844
|
+
function object6(value) {
|
|
46845
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
46846
|
+
}
|
|
46847
|
+
function identifier(value) {
|
|
46848
|
+
return typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(value) && !value.includes("..");
|
|
46849
|
+
}
|
|
46850
|
+
function invalid() {
|
|
46851
|
+
throw new Error("The Skills API returned an invalid profile response");
|
|
46852
|
+
}
|
|
46853
|
+
function validateSelection2(value) {
|
|
46854
|
+
if (!object6(value) || !identifier(value.slug) || typeof value.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(value.version) || value.version.includes("..") || typeof value.bundleDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.bundleDigest))
|
|
46855
|
+
invalid();
|
|
46856
|
+
if (selectionAliasError([value]))
|
|
46857
|
+
invalid();
|
|
46858
|
+
if (value.triggers !== undefined) {
|
|
46859
|
+
if (!object6(value.triggers) || Object.keys(value.triggers).some((key) => !["keywords", "paths", "always"].includes(key)))
|
|
46860
|
+
invalid();
|
|
46861
|
+
if (value.triggers.always !== undefined && typeof value.triggers.always !== "boolean")
|
|
46862
|
+
invalid();
|
|
46863
|
+
for (const key of ["keywords", "paths"]) {
|
|
46864
|
+
const values = value.triggers[key];
|
|
46865
|
+
if (values !== undefined && (!Array.isArray(values) || values.length > 32 || values.some((term) => typeof term !== "string" || !term.trim() || term.length > 256)))
|
|
46866
|
+
invalid();
|
|
46867
|
+
}
|
|
46868
|
+
}
|
|
46869
|
+
}
|
|
46870
|
+
|
|
46871
|
+
class HttpProfileClient {
|
|
46872
|
+
key;
|
|
46873
|
+
authority;
|
|
46874
|
+
origin;
|
|
46875
|
+
constructor(key, origin) {
|
|
46876
|
+
this.key = key;
|
|
46877
|
+
if (!key.trim())
|
|
46878
|
+
throw new Error("A Skills credential is required");
|
|
46879
|
+
this.origin = normalizeSkillsApiOrigin(origin);
|
|
46880
|
+
this.authority = skillsApiRequestUrl(this.origin, "/api/v1/").replace(/\/+$/, "");
|
|
46881
|
+
}
|
|
46882
|
+
async requestRoute(route, init = {}) {
|
|
46883
|
+
const requestUrl = skillsApiRequestUrl(this.origin, route);
|
|
46884
|
+
let response;
|
|
46885
|
+
try {
|
|
46886
|
+
response = await fetch(requestUrl, {
|
|
46887
|
+
...init,
|
|
46888
|
+
redirect: "error",
|
|
46889
|
+
credentials: "omit",
|
|
46890
|
+
signal: AbortSignal.timeout(15000),
|
|
46891
|
+
headers: {
|
|
46892
|
+
"Content-Type": "application/json",
|
|
46893
|
+
"User-Agent": `hasna-skills/${package_default.version}`,
|
|
46894
|
+
Authorization: `Bearer ${this.key}`,
|
|
46895
|
+
...init.headers
|
|
46896
|
+
}
|
|
46897
|
+
});
|
|
46898
|
+
} catch {
|
|
46899
|
+
throw new SkillSelectionError("SKILLS_API_UNAVAILABLE", "Unable to reach the configured Skills API");
|
|
46900
|
+
}
|
|
46901
|
+
if (!response.ok) {
|
|
46902
|
+
response.body?.cancel().catch(() => {});
|
|
46903
|
+
const code = response.status === 401 ? "SKILLS_API_UNAUTHORIZED" : response.status === 403 ? "SKILLS_API_FORBIDDEN" : [404, 410].includes(response.status) ? "SKILLS_API_RESOURCE_UNAVAILABLE" : response.status === 429 || response.status >= 500 ? "SKILLS_API_UNAVAILABLE" : undefined;
|
|
46904
|
+
if (code)
|
|
46905
|
+
throw new SkillSelectionError(code, `Skills API request failed (HTTP ${response.status})`);
|
|
46906
|
+
throw new Error(`Skills API request failed (HTTP ${response.status})`);
|
|
46907
|
+
}
|
|
46908
|
+
return response;
|
|
46909
|
+
}
|
|
46910
|
+
request(path, init = {}) {
|
|
46911
|
+
return this.requestRoute(`/api/v1${path}`, init);
|
|
46912
|
+
}
|
|
46913
|
+
async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
|
|
46914
|
+
try {
|
|
46915
|
+
return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit)));
|
|
46916
|
+
} catch {
|
|
46917
|
+
invalid();
|
|
46918
|
+
}
|
|
46919
|
+
}
|
|
46920
|
+
async resolveProfile(id) {
|
|
46921
|
+
if (!identifier(id))
|
|
46922
|
+
throw new Error("Invalid selection profile id");
|
|
46923
|
+
const result = await this.read(await this.request(`/profiles/${encodeURIComponent(id)}/resolve`), MAX_RESOLVED_PROFILE_BYTES);
|
|
46924
|
+
if (!object6(result) || result.profileId !== id || result.authority !== this.authority || !identifier(result.workspaceId) || !identifier(result.profileRevision) || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
|
|
46925
|
+
invalid();
|
|
46926
|
+
const seen = new Set;
|
|
46927
|
+
for (const entry of result.selections) {
|
|
46928
|
+
validateSelection2(entry);
|
|
46929
|
+
const item = entry;
|
|
46930
|
+
if (seen.has(item.slug) || item.authority !== this.authority || item.workspaceId !== result.workspaceId || item.profileRevision !== result.profileRevision)
|
|
46931
|
+
invalid();
|
|
46932
|
+
seen.add(item.slug);
|
|
46933
|
+
}
|
|
46934
|
+
if (selectionAliasError(result.selections))
|
|
46935
|
+
invalid();
|
|
46936
|
+
return result;
|
|
46937
|
+
}
|
|
46938
|
+
async resolvePrincipal() {
|
|
46939
|
+
const result = await this.read(await this.requestRoute("/api/auth/whoami"), 16 * 1024);
|
|
46940
|
+
if (!object6(result) || Object.keys(result).sort().join(",") !== "organization,user" || !object6(result.user) || Object.keys(result.user).sort().join(",") !== "email,id,role" || !object6(result.organization) || Object.keys(result.organization).sort().join(",") !== "id,name,slug" || !identifier(result.user.id) || typeof result.user.email !== "string" || !result.user.email.trim() || !["owner", "admin", "member", "viewer"].includes(String(result.user.role)) || !identifier(result.organization.id) || !identifier(result.organization.slug) || typeof result.organization.name !== "string" || !result.organization.name.trim())
|
|
46941
|
+
invalid();
|
|
46942
|
+
return { userId: result.user.id, accountId: result.organization.id, role: result.user.role };
|
|
46943
|
+
}
|
|
46944
|
+
async recordStation(id, input2) {
|
|
46945
|
+
if (!identifier(id) || !identifier(input2.profileId) || !identifier(input2.profileRevision) || !Array.isArray(input2.selections) || input2.selections.length > MAX_PROFILE_SELECTIONS)
|
|
46946
|
+
throw new Error("Invalid station state");
|
|
46947
|
+
input2.selections.forEach(validateSelection2);
|
|
46948
|
+
if (selectionAliasError(input2.selections))
|
|
46949
|
+
throw new Error("Invalid station selection aliases");
|
|
46950
|
+
const snapshot = { ...input2, selections: profileSelectionSnapshot(input2.selections) };
|
|
46951
|
+
const bodyBytes = profileDocumentBytes(snapshot);
|
|
46952
|
+
if (bodyBytes > MAX_PROFILE_DOCUMENT_BYTES)
|
|
46953
|
+
throw new Error("Station state exceeds the profile document size limit");
|
|
46954
|
+
if (requiresProfileCapacity(input2.selections, bodyBytes)) {
|
|
46955
|
+
const capabilities = await this.read(await this.request("/capabilities"));
|
|
46956
|
+
assertAdvertisedProfileCapacity(capabilities, input2.selections.length, bodyBytes);
|
|
46957
|
+
}
|
|
46958
|
+
const result = await this.read(await this.request(`/stations/${encodeURIComponent(id)}/state`, {
|
|
46959
|
+
method: "PUT",
|
|
46960
|
+
body: JSON.stringify(snapshot)
|
|
46961
|
+
}));
|
|
46962
|
+
if (!object6(result) || result.stationId !== id || result.profileId !== input2.profileId || result.profileRevision !== input2.profileRevision || !identifier(result.workspaceId) || !identifier(result.actorId) || typeof result.appliedAt !== "string" || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
|
|
46963
|
+
invalid();
|
|
46964
|
+
result.selections.forEach(validateSelection2);
|
|
46965
|
+
if (selectionAliasError(result.selections))
|
|
46966
|
+
invalid();
|
|
46967
|
+
if (!selectionSnapshotsEqual(input2.selections, result.selections))
|
|
46968
|
+
invalid();
|
|
46969
|
+
return result;
|
|
46970
|
+
}
|
|
46971
|
+
async getBundle(slug, version2) {
|
|
46972
|
+
if (!identifier(slug) || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2) || version2.includes(".."))
|
|
46973
|
+
throw new Error("Invalid immutable skill reference");
|
|
46974
|
+
return this.request(`/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version2)}/bundle`);
|
|
46975
|
+
}
|
|
46976
|
+
}
|
|
46977
|
+
async function createProfileClient() {
|
|
46978
|
+
let connection;
|
|
46979
|
+
try {
|
|
46980
|
+
connection = await resolveSkillsConnection();
|
|
46981
|
+
} catch (error61) {
|
|
46982
|
+
if (isSkillsFleetCredentialError(error61) && error61.code === "MISSING_API_CREDENTIAL") {
|
|
46983
|
+
throw new SkillSelectionError("SKILLS_API_CREDENTIAL_UNAVAILABLE", "The configured Skills authority has no usable credential. Sign in through the configured credential resolver; no payload was delivered.");
|
|
46984
|
+
}
|
|
46985
|
+
throw error61;
|
|
46986
|
+
}
|
|
46987
|
+
if (!connection)
|
|
46988
|
+
throw new Error("Selection profiles require a configured Skills API");
|
|
46989
|
+
return new HttpProfileClient(connection.apiKey, connection.apiOrigin);
|
|
46990
|
+
}
|
|
46991
|
+
var init_profile_client = __esm(() => {
|
|
46992
|
+
init_selection_cache();
|
|
46993
|
+
init_package();
|
|
46994
|
+
init_fleet_credentials();
|
|
46995
|
+
init_remote_files();
|
|
46996
|
+
init_profile_limits();
|
|
46997
|
+
});
|
|
46998
|
+
|
|
46966
46999
|
// src/lib/preparation-process.ts
|
|
46967
47000
|
function capturePreparationProcess(child, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
|
|
46968
47001
|
let spawnedPid;
|
|
@@ -47167,116 +47200,9 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
|
|
|
47167
47200
|
var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation", RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.", DEFAULT_PREPARATION_TIMEOUT_MS = 60000;
|
|
47168
47201
|
var init_dependency_preparation = () => {};
|
|
47169
47202
|
|
|
47170
|
-
// src/lib/managed-policy.ts
|
|
47171
|
-
import { closeSync as closeSync8, constants as constants5, fstatSync as fstatSync8, lstatSync as lstatSync10, openSync as openSync8, readSync as readSync4 } from "fs";
|
|
47172
|
-
import { join as join16 } from "path";
|
|
47173
|
-
function refusal(reason = "is unreadable") {
|
|
47174
|
-
return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason}; refusing legacy fallback.`);
|
|
47175
|
-
}
|
|
47176
|
-
function parseManagedSkillPolicy(text2) {
|
|
47177
|
-
if (Buffer.byteLength(text2) > AGENT_POLICY_LIMITS.bytes)
|
|
47178
|
-
throw refusal("exceeds its size limit");
|
|
47179
|
-
let value;
|
|
47180
|
-
try {
|
|
47181
|
-
value = JSON.parse(text2);
|
|
47182
|
-
} catch {
|
|
47183
|
-
throw refusal();
|
|
47184
|
-
}
|
|
47185
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || value.loading !== "cli" || value.version !== undefined && value.version !== 1)
|
|
47186
|
-
throw refusal();
|
|
47187
|
-
if (value.profileId !== undefined && (typeof value.profileId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value.profileId) || value.profileId.includes("..")))
|
|
47188
|
-
throw refusal();
|
|
47189
|
-
try {
|
|
47190
|
-
assertAgentPolicyCollections(value);
|
|
47191
|
-
} catch {
|
|
47192
|
-
throw refusal("has invalid collection bounds");
|
|
47193
|
-
}
|
|
47194
|
-
return value;
|
|
47195
|
-
}
|
|
47196
|
-
function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
|
|
47197
|
-
const path = join16(dataDir2, "agent-policy.json");
|
|
47198
|
-
let descriptor, observed = false;
|
|
47199
|
-
try {
|
|
47200
|
-
const stat = lstatSync10(path);
|
|
47201
|
-
observed = true;
|
|
47202
|
-
if (!stat.isFile() || stat.isSymbolicLink())
|
|
47203
|
-
throw refusal();
|
|
47204
|
-
if (stat.size > AGENT_POLICY_LIMITS.bytes)
|
|
47205
|
-
throw refusal("exceeds its size limit");
|
|
47206
|
-
descriptor = openSync8(path, constants5.O_RDONLY | (constants5.O_NOFOLLOW ?? 0) | constants5.O_NONBLOCK);
|
|
47207
|
-
const opened = fstatSync8(descriptor);
|
|
47208
|
-
if (!opened.isFile())
|
|
47209
|
-
throw refusal();
|
|
47210
|
-
if (opened.size > AGENT_POLICY_LIMITS.bytes)
|
|
47211
|
-
throw refusal("exceeds its size limit");
|
|
47212
|
-
const bytes = Buffer.allocUnsafe(AGENT_POLICY_LIMITS.bytes + 1);
|
|
47213
|
-
let length = 0;
|
|
47214
|
-
while (length < bytes.length) {
|
|
47215
|
-
const count = readSync4(descriptor, bytes, length, bytes.length - length, null);
|
|
47216
|
-
if (count === 0)
|
|
47217
|
-
break;
|
|
47218
|
-
length += count;
|
|
47219
|
-
}
|
|
47220
|
-
if (length > AGENT_POLICY_LIMITS.bytes)
|
|
47221
|
-
throw refusal("exceeds its size limit");
|
|
47222
|
-
const text2 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
|
|
47223
|
-
return { text: text2, value: parseManagedSkillPolicy(text2) };
|
|
47224
|
-
} catch (error61) {
|
|
47225
|
-
if (error61.code === "ENOENT" && !observed) {
|
|
47226
|
-
if (lstatSync10(path, { throwIfNoEntry: false }) === undefined)
|
|
47227
|
-
return null;
|
|
47228
|
-
}
|
|
47229
|
-
if (error61 instanceof SkillSelectionError)
|
|
47230
|
-
throw error61;
|
|
47231
|
-
throw refusal();
|
|
47232
|
-
} finally {
|
|
47233
|
-
if (descriptor !== undefined)
|
|
47234
|
-
closeSync8(descriptor);
|
|
47235
|
-
}
|
|
47236
|
-
}
|
|
47237
|
-
function readManagedSkillPolicy(dataDir2 = getDataDirReadOnly()) {
|
|
47238
|
-
return readManagedSkillPolicySnapshot(dataDir2)?.value ?? null;
|
|
47239
|
-
}
|
|
47240
|
-
function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
|
|
47241
|
-
return readManagedSkillPolicy(dataDir2)?.loading === "cli";
|
|
47242
|
-
}
|
|
47243
|
-
var init_managed_policy = __esm(() => {
|
|
47244
|
-
init_config();
|
|
47245
|
-
init_selection_cache();
|
|
47246
|
-
init_agent_policy_limits();
|
|
47247
|
-
});
|
|
47248
|
-
|
|
47249
|
-
// src/lib/agent-bridge.ts
|
|
47250
|
-
import { createHash as createHash8 } from "crypto";
|
|
47251
|
-
var CLI_BRIDGE_VERSION = 1, markdown = `---
|
|
47252
|
-
name: skills-cli
|
|
47253
|
-
description: Discover, load, author, and run versioned skills through the Skills CLI when a task needs reusable instructions or an executable skill.
|
|
47254
|
-
---
|
|
47255
|
-
|
|
47256
|
-
Use the Skills CLI as the skill authority for this station.
|
|
47257
|
-
|
|
47258
|
-
- Discover available skills with \`skills list --json\` or \`skills search <query> --json\`.
|
|
47259
|
-
- Read selected instructions with \`skills load <slug>\`. Use \`skills sync\` to refresh the station's shared selection profile.
|
|
47260
|
-
- Create local drafts with \`skills new <name> --kind instruction\` or \`--kind executable\` in the Skills-owned source directory. Validate with \`skills validate <name>\`; use \`skills push --help\` for explicit versioned publication.
|
|
47261
|
-
- Run a skill only when the user's task calls for execution. Use \`skills run --help\` and select the intended local or cloud target explicitly.
|
|
47262
|
-
|
|
47263
|
-
This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
|
|
47264
|
-
`, CLI_BRIDGE_DIGEST, CLI_BRIDGE_FILES;
|
|
47265
|
-
var init_agent_bridge = __esm(() => {
|
|
47266
|
-
CLI_BRIDGE_DIGEST = createHash8("sha256").update(markdown).digest("hex");
|
|
47267
|
-
CLI_BRIDGE_FILES = Object.freeze({
|
|
47268
|
-
"SKILL.md": markdown,
|
|
47269
|
-
".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
|
|
47270
|
-
`
|
|
47271
|
-
});
|
|
47272
|
-
});
|
|
47273
|
-
|
|
47274
|
-
// src/lib/agent-codex.ts
|
|
47275
|
-
var init_agent_codex = () => {};
|
|
47276
|
-
|
|
47277
47203
|
// src/lib/claude-settings-witness.ts
|
|
47278
|
-
import { createHash as
|
|
47279
|
-
import { closeSync as
|
|
47204
|
+
import { createHash as createHash8 } from "crypto";
|
|
47205
|
+
import { closeSync as closeSync8, constants as constants5, fstatSync as fstatSync8, lstatSync as lstatSync10, openSync as openSync8, readSync as readSync4 } from "fs";
|
|
47280
47206
|
import { basename as basename4, dirname as dirname9, isAbsolute as isAbsolute8, resolve as resolve8 } from "path";
|
|
47281
47207
|
function need2(value, reason) {
|
|
47282
47208
|
if (!value)
|
|
@@ -47288,36 +47214,36 @@ function absolutePath(value) {
|
|
|
47288
47214
|
function parents(path) {
|
|
47289
47215
|
const result = [];
|
|
47290
47216
|
for (let at = dirname9(path);; at = dirname9(at)) {
|
|
47291
|
-
const stat =
|
|
47217
|
+
const stat = lstatSync10(at, { bigint: true });
|
|
47292
47218
|
need2(stat.isDirectory() && !stat.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
47293
47219
|
result.push([at, stat]);
|
|
47294
47220
|
if (at === dirname9(at))
|
|
47295
47221
|
return result;
|
|
47296
47222
|
}
|
|
47297
47223
|
}
|
|
47298
|
-
function
|
|
47224
|
+
function readNativeSettingsWitnessFile(path, budget, filename = "settings.json") {
|
|
47299
47225
|
absolutePath(path);
|
|
47300
|
-
need2(basename4(path) ===
|
|
47226
|
+
need2(basename4(path) === filename, `requires ${filename}`);
|
|
47301
47227
|
need2(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
|
|
47302
|
-
const ancestors = parents(path), initial =
|
|
47228
|
+
const ancestors = parents(path), initial = lstatSync10(path, { bigint: true });
|
|
47303
47229
|
need2(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_SETTINGS_WITNESS_LIMITS.bytes), "is not a bounded regular file");
|
|
47304
47230
|
need2(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
|
|
47305
|
-
const fd =
|
|
47231
|
+
const fd = openSync8(path, constants5.O_RDONLY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK);
|
|
47306
47232
|
try {
|
|
47307
|
-
const opened =
|
|
47233
|
+
const opened = fstatSync8(fd, { bigint: true });
|
|
47308
47234
|
need2(opened.isFile() && sameFile(initial, opened), "changed during open");
|
|
47309
47235
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
47310
47236
|
let length = 0;
|
|
47311
47237
|
while (length < bytes.length) {
|
|
47312
|
-
const count =
|
|
47238
|
+
const count = readSync4(fd, bytes, length, bytes.length - length, null);
|
|
47313
47239
|
if (!count)
|
|
47314
47240
|
break;
|
|
47315
47241
|
length += count;
|
|
47316
47242
|
}
|
|
47317
|
-
const after =
|
|
47243
|
+
const after = fstatSync8(fd, { bigint: true }), current = lstatSync10(path, { bigint: true });
|
|
47318
47244
|
need2(current.isFile() && sameFile(opened, after) && sameFile(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
47319
47245
|
for (const [ancestor, before] of ancestors) {
|
|
47320
|
-
const now =
|
|
47246
|
+
const now = lstatSync10(ancestor, { bigint: true });
|
|
47321
47247
|
need2(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
47322
47248
|
}
|
|
47323
47249
|
budget.remaining -= length;
|
|
@@ -47327,7 +47253,7 @@ function readSettings(path, budget) {
|
|
|
47327
47253
|
throw new Error("Claude settings witness is not strict UTF-8");
|
|
47328
47254
|
}
|
|
47329
47255
|
} finally {
|
|
47330
|
-
|
|
47256
|
+
closeSync8(fd);
|
|
47331
47257
|
}
|
|
47332
47258
|
}
|
|
47333
47259
|
function parse8(text2) {
|
|
@@ -47451,7 +47377,7 @@ function settingsDigest(text2, version2 = 1) {
|
|
|
47451
47377
|
}
|
|
47452
47378
|
return true;
|
|
47453
47379
|
});
|
|
47454
|
-
return
|
|
47380
|
+
return createHash8("sha256").update(`hasna.skills.claude-settings.v${version2}\x00`).update(JSON.stringify(canonical2(value))).digest("hex");
|
|
47455
47381
|
}
|
|
47456
47382
|
function hashClaudeSettingsReplacement(text2, budget) {
|
|
47457
47383
|
need2(typeof text2 === "string", "requires settings text");
|
|
@@ -47461,10 +47387,10 @@ function hashClaudeSettingsReplacement(text2, budget) {
|
|
|
47461
47387
|
return settingsDigest(text2);
|
|
47462
47388
|
}
|
|
47463
47389
|
function captureClaudeSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
47464
|
-
return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(
|
|
47390
|
+
return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget)) };
|
|
47465
47391
|
}
|
|
47466
47392
|
function captureClaudeSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
47467
|
-
return { path, hashMode: "claude-settings-v2", sha256: settingsDigest(
|
|
47393
|
+
return { path, hashMode: "claude-settings-v2", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 2) };
|
|
47468
47394
|
}
|
|
47469
47395
|
function hashClaudeSettingsReplacementV2(text2, budget) {
|
|
47470
47396
|
need2(typeof text2 === "string", "requires settings text");
|
|
@@ -47474,7 +47400,7 @@ function hashClaudeSettingsReplacementV2(text2, budget) {
|
|
|
47474
47400
|
return settingsDigest(text2, 2);
|
|
47475
47401
|
}
|
|
47476
47402
|
function captureClaudeSettingsV3(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
47477
|
-
return { path, hashMode: "claude-settings-v3", sha256: settingsDigest(
|
|
47403
|
+
return { path, hashMode: "claude-settings-v3", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 3) };
|
|
47478
47404
|
}
|
|
47479
47405
|
function hashClaudeSettingsReplacementV3(text2, budget) {
|
|
47480
47406
|
need2(typeof text2 === "string", "requires settings text");
|
|
@@ -47540,8 +47466,233 @@ var init_claude_settings_witness = __esm(() => {
|
|
|
47540
47466
|
PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
|
|
47541
47467
|
});
|
|
47542
47468
|
|
|
47469
|
+
// src/lib/codex-hook-rpc.ts
|
|
47470
|
+
var init_codex_hook_rpc = () => {};
|
|
47471
|
+
|
|
47472
|
+
// src/lib/codex-native-skill-catalog.ts
|
|
47473
|
+
var SUPPORTED_NAME_CONTROL_VERSIONS;
|
|
47474
|
+
var init_codex_native_skill_catalog = __esm(() => {
|
|
47475
|
+
init_codex_hook_rpc();
|
|
47476
|
+
SUPPORTED_NAME_CONTROL_VERSIONS = new Set(["codex-cli 0.159.2"]);
|
|
47477
|
+
});
|
|
47478
|
+
|
|
47479
|
+
// src/lib/codex-plugin-skill-controls.ts
|
|
47480
|
+
var init_codex_plugin_skill_controls = __esm(() => {
|
|
47481
|
+
init_claude_settings_witness();
|
|
47482
|
+
init_codex_native_skill_catalog();
|
|
47483
|
+
});
|
|
47484
|
+
|
|
47485
|
+
// src/lib/codex-settings-witness.ts
|
|
47486
|
+
import { createHash as createHash9 } from "crypto";
|
|
47487
|
+
function need3(value) {
|
|
47488
|
+
if (!value)
|
|
47489
|
+
throw new Error("Invalid Codex settings witness");
|
|
47490
|
+
}
|
|
47491
|
+
function numericTokens(text2) {
|
|
47492
|
+
const result = [];
|
|
47493
|
+
for (let at = 0;at < text2.length; ) {
|
|
47494
|
+
const char = text2[at];
|
|
47495
|
+
if (char === "#") {
|
|
47496
|
+
while (at < text2.length && text2[at] !== `
|
|
47497
|
+
`)
|
|
47498
|
+
at++;
|
|
47499
|
+
continue;
|
|
47500
|
+
}
|
|
47501
|
+
if (char === '"' || char === "'") {
|
|
47502
|
+
const quote = char, triple = text2.slice(at, at + 3) === quote.repeat(3);
|
|
47503
|
+
at += triple ? 3 : 1;
|
|
47504
|
+
while (at < text2.length) {
|
|
47505
|
+
if (quote === '"' && text2[at] === "\\") {
|
|
47506
|
+
at += 2;
|
|
47507
|
+
continue;
|
|
47508
|
+
}
|
|
47509
|
+
if (text2.slice(at, at + (triple ? 3 : 1)) === quote.repeat(triple ? 3 : 1)) {
|
|
47510
|
+
at += triple ? 3 : 1;
|
|
47511
|
+
break;
|
|
47512
|
+
}
|
|
47513
|
+
at++;
|
|
47514
|
+
}
|
|
47515
|
+
continue;
|
|
47516
|
+
}
|
|
47517
|
+
if (/[0-9+-]/.test(char)) {
|
|
47518
|
+
const start = at;
|
|
47519
|
+
while (at < text2.length && !/[\s,\]{}#=]/.test(text2[at]))
|
|
47520
|
+
at++;
|
|
47521
|
+
if (at > start) {
|
|
47522
|
+
result.push(text2.slice(start, at));
|
|
47523
|
+
continue;
|
|
47524
|
+
}
|
|
47525
|
+
}
|
|
47526
|
+
at++;
|
|
47527
|
+
}
|
|
47528
|
+
return result;
|
|
47529
|
+
}
|
|
47530
|
+
function canonical3(value, depth = 0, budget = { remaining: 65536 }) {
|
|
47531
|
+
need3(depth <= 32 && --budget.remaining >= 0);
|
|
47532
|
+
if (value === null)
|
|
47533
|
+
return ["null"];
|
|
47534
|
+
if (typeof value === "string")
|
|
47535
|
+
return ["string", value];
|
|
47536
|
+
if (typeof value === "boolean")
|
|
47537
|
+
return ["boolean", value];
|
|
47538
|
+
if (typeof value === "number") {
|
|
47539
|
+
need3(Number.isFinite(value));
|
|
47540
|
+
return ["number", String(value)];
|
|
47541
|
+
}
|
|
47542
|
+
if (value instanceof Date) {
|
|
47543
|
+
need3(Number.isFinite(value.getTime()));
|
|
47544
|
+
return ["date", value.toISOString()];
|
|
47545
|
+
}
|
|
47546
|
+
if (Array.isArray(value))
|
|
47547
|
+
return ["array", value.map((child) => canonical3(child, depth + 1, budget))];
|
|
47548
|
+
need3(value && typeof value === "object");
|
|
47549
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical3(child, depth + 1, budget)])];
|
|
47550
|
+
}
|
|
47551
|
+
function hashCodexSettingsReplacement(text2, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
47552
|
+
need3(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
|
|
47553
|
+
budget.remaining -= Buffer.byteLength(text2);
|
|
47554
|
+
let config2;
|
|
47555
|
+
try {
|
|
47556
|
+
config2 = Bun.TOML.parse(text2);
|
|
47557
|
+
} catch {
|
|
47558
|
+
throw new Error("Invalid Codex settings witness");
|
|
47559
|
+
}
|
|
47560
|
+
need3(config2 && typeof config2 === "object" && !Array.isArray(config2));
|
|
47561
|
+
if (Object.hasOwn(config2, "model")) {
|
|
47562
|
+
need3(typeof config2.model === "string" && /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(config2.model));
|
|
47563
|
+
delete config2.model;
|
|
47564
|
+
}
|
|
47565
|
+
for (const [key, values] of [["model_reasoning_effort", EFFORT], ["model_verbosity", VERBOSITY]])
|
|
47566
|
+
if (Object.hasOwn(config2, key)) {
|
|
47567
|
+
need3(typeof config2[key] === "string" && values.has(config2[key]));
|
|
47568
|
+
delete config2[key];
|
|
47569
|
+
}
|
|
47570
|
+
return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical3(config2), numericTokens(text2)])}`);
|
|
47571
|
+
}
|
|
47572
|
+
function captureCodexSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
47573
|
+
const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
|
|
47574
|
+
return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
|
|
47575
|
+
}
|
|
47576
|
+
var sha = (text2) => createHash9("sha256").update(text2).digest("hex"), EFFORT, VERBOSITY;
|
|
47577
|
+
var init_codex_settings_witness = __esm(() => {
|
|
47578
|
+
init_claude_settings_witness();
|
|
47579
|
+
EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
47580
|
+
VERBOSITY = new Set(["low", "medium", "high"]);
|
|
47581
|
+
});
|
|
47582
|
+
|
|
47583
|
+
// src/lib/managed-policy.ts
|
|
47584
|
+
import { closeSync as closeSync9, constants as constants6, fstatSync as fstatSync9, lstatSync as lstatSync11, openSync as openSync9, readSync as readSync5 } from "fs";
|
|
47585
|
+
import { join as join16 } from "path";
|
|
47586
|
+
function refusal(reason = "is unreadable") {
|
|
47587
|
+
return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason}; refusing legacy fallback.`);
|
|
47588
|
+
}
|
|
47589
|
+
function parseManagedSkillPolicy(text2) {
|
|
47590
|
+
if (Buffer.byteLength(text2) > AGENT_POLICY_LIMITS.bytes)
|
|
47591
|
+
throw refusal("exceeds its size limit");
|
|
47592
|
+
let value;
|
|
47593
|
+
try {
|
|
47594
|
+
value = JSON.parse(text2);
|
|
47595
|
+
} catch {
|
|
47596
|
+
throw refusal();
|
|
47597
|
+
}
|
|
47598
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || value.loading !== "cli" || value.version !== undefined && value.version !== 1)
|
|
47599
|
+
throw refusal();
|
|
47600
|
+
if (value.profileId !== undefined && (typeof value.profileId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value.profileId) || value.profileId.includes("..")))
|
|
47601
|
+
throw refusal();
|
|
47602
|
+
try {
|
|
47603
|
+
assertAgentPolicyCollections(value);
|
|
47604
|
+
} catch {
|
|
47605
|
+
throw refusal("has invalid collection bounds");
|
|
47606
|
+
}
|
|
47607
|
+
return value;
|
|
47608
|
+
}
|
|
47609
|
+
function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
|
|
47610
|
+
const path = join16(dataDir2, "agent-policy.json");
|
|
47611
|
+
let descriptor, observed = false;
|
|
47612
|
+
try {
|
|
47613
|
+
const stat = lstatSync11(path);
|
|
47614
|
+
observed = true;
|
|
47615
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
47616
|
+
throw refusal();
|
|
47617
|
+
if (stat.size > AGENT_POLICY_LIMITS.bytes)
|
|
47618
|
+
throw refusal("exceeds its size limit");
|
|
47619
|
+
descriptor = openSync9(path, constants6.O_RDONLY | (constants6.O_NOFOLLOW ?? 0) | constants6.O_NONBLOCK);
|
|
47620
|
+
const opened = fstatSync9(descriptor);
|
|
47621
|
+
if (!opened.isFile())
|
|
47622
|
+
throw refusal();
|
|
47623
|
+
if (opened.size > AGENT_POLICY_LIMITS.bytes)
|
|
47624
|
+
throw refusal("exceeds its size limit");
|
|
47625
|
+
const bytes = Buffer.allocUnsafe(AGENT_POLICY_LIMITS.bytes + 1);
|
|
47626
|
+
let length = 0;
|
|
47627
|
+
while (length < bytes.length) {
|
|
47628
|
+
const count = readSync5(descriptor, bytes, length, bytes.length - length, null);
|
|
47629
|
+
if (count === 0)
|
|
47630
|
+
break;
|
|
47631
|
+
length += count;
|
|
47632
|
+
}
|
|
47633
|
+
if (length > AGENT_POLICY_LIMITS.bytes)
|
|
47634
|
+
throw refusal("exceeds its size limit");
|
|
47635
|
+
const text2 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
|
|
47636
|
+
return { text: text2, value: parseManagedSkillPolicy(text2) };
|
|
47637
|
+
} catch (error61) {
|
|
47638
|
+
if (error61.code === "ENOENT" && !observed) {
|
|
47639
|
+
if (lstatSync11(path, { throwIfNoEntry: false }) === undefined)
|
|
47640
|
+
return null;
|
|
47641
|
+
}
|
|
47642
|
+
if (error61 instanceof SkillSelectionError)
|
|
47643
|
+
throw error61;
|
|
47644
|
+
throw refusal();
|
|
47645
|
+
} finally {
|
|
47646
|
+
if (descriptor !== undefined)
|
|
47647
|
+
closeSync9(descriptor);
|
|
47648
|
+
}
|
|
47649
|
+
}
|
|
47650
|
+
function readManagedSkillPolicy(dataDir2 = getDataDirReadOnly()) {
|
|
47651
|
+
return readManagedSkillPolicySnapshot(dataDir2)?.value ?? null;
|
|
47652
|
+
}
|
|
47653
|
+
function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
|
|
47654
|
+
return readManagedSkillPolicy(dataDir2)?.loading === "cli";
|
|
47655
|
+
}
|
|
47656
|
+
var init_managed_policy = __esm(() => {
|
|
47657
|
+
init_config();
|
|
47658
|
+
init_selection_cache();
|
|
47659
|
+
init_agent_policy_limits();
|
|
47660
|
+
});
|
|
47661
|
+
|
|
47662
|
+
// src/lib/agent-bridge.ts
|
|
47663
|
+
import { createHash as createHash10 } from "crypto";
|
|
47664
|
+
var CLI_BRIDGE_VERSION = 1, markdown = `---
|
|
47665
|
+
name: skills-cli
|
|
47666
|
+
description: Discover, load, author, and run versioned skills through the Skills CLI when a task needs reusable instructions or an executable skill.
|
|
47667
|
+
---
|
|
47668
|
+
|
|
47669
|
+
Use the Skills CLI as the skill authority for this station.
|
|
47670
|
+
|
|
47671
|
+
- Discover available skills with \`skills list --json\` or \`skills search <query> --json\`.
|
|
47672
|
+
- Read selected instructions with \`skills load <slug>\`. Use \`skills sync\` to refresh the station's shared selection profile.
|
|
47673
|
+
- Create local drafts with \`skills new <name> --kind instruction\` or \`--kind executable\` in the Skills-owned source directory. Validate with \`skills validate <name>\`; use \`skills push --help\` for explicit versioned publication.
|
|
47674
|
+
- Run a skill only when the user's task calls for execution. Use \`skills run --help\` and select the intended local or cloud target explicitly.
|
|
47675
|
+
|
|
47676
|
+
This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
|
|
47677
|
+
`, CLI_BRIDGE_DIGEST, CLI_BRIDGE_FILES;
|
|
47678
|
+
var init_agent_bridge = __esm(() => {
|
|
47679
|
+
CLI_BRIDGE_DIGEST = createHash10("sha256").update(markdown).digest("hex");
|
|
47680
|
+
CLI_BRIDGE_FILES = Object.freeze({
|
|
47681
|
+
"SKILL.md": markdown,
|
|
47682
|
+
".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
|
|
47683
|
+
`
|
|
47684
|
+
});
|
|
47685
|
+
});
|
|
47686
|
+
|
|
47687
|
+
// src/lib/agent-codex.ts
|
|
47688
|
+
var init_agent_codex = () => {};
|
|
47689
|
+
|
|
47543
47690
|
// src/lib/agent-integration.ts
|
|
47544
47691
|
var init_agent_integration = __esm(() => {
|
|
47692
|
+
init_codex_plugin_skill_controls();
|
|
47693
|
+
init_codex_native_skill_catalog();
|
|
47694
|
+
init_codex_settings_witness();
|
|
47695
|
+
init_claude_settings_witness();
|
|
47545
47696
|
init_native_discovery_roots();
|
|
47546
47697
|
init_config();
|
|
47547
47698
|
init_managed_policy();
|
|
@@ -48219,7 +48370,7 @@ var init_selection_resolver = __esm(() => {
|
|
|
48219
48370
|
});
|
|
48220
48371
|
|
|
48221
48372
|
// src/lib/plugin-projection.ts
|
|
48222
|
-
import { createHash as
|
|
48373
|
+
import { createHash as createHash11 } from "crypto";
|
|
48223
48374
|
function pluginRefusal(message) {
|
|
48224
48375
|
throw new SkillSelectionError("PLUGIN_ADMISSION_REFUSED", message);
|
|
48225
48376
|
}
|
|
@@ -48282,7 +48433,7 @@ function validatePluginManifest(value) {
|
|
|
48282
48433
|
pluginNeed(item.kind === "skill" || item.kind === "command", "Unknown plugin payload kind");
|
|
48283
48434
|
pluginDigest(item.sourceDigest);
|
|
48284
48435
|
pluginKeys(item.target, ["slug", "version", "bundleDigest"]);
|
|
48285
|
-
|
|
48436
|
+
validateSelection({ ...item.target, authority: "https://example.com/skills/v1", workspaceId: "validation", profileRevision: "validation" });
|
|
48286
48437
|
}
|
|
48287
48438
|
}
|
|
48288
48439
|
function pluginFileWitnesses(entries) {
|
|
@@ -48297,7 +48448,7 @@ function pluginFileWitnesses(entries) {
|
|
|
48297
48448
|
return { path: item.path, mode: item.mode, size: item.bytes.byteLength, sha256: pluginHash(item.bytes) };
|
|
48298
48449
|
});
|
|
48299
48450
|
}
|
|
48300
|
-
var PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) =>
|
|
48451
|
+
var PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) => createHash11("sha256").update(bytes).digest("hex");
|
|
48301
48452
|
var init_plugin_projection = __esm(() => {
|
|
48302
48453
|
init_selection_cache();
|
|
48303
48454
|
PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -48315,7 +48466,7 @@ function stat(path) {
|
|
|
48315
48466
|
return pluginRefusal("Cannot inspect plugin filesystem entry");
|
|
48316
48467
|
}
|
|
48317
48468
|
}
|
|
48318
|
-
function
|
|
48469
|
+
function canonical4(path) {
|
|
48319
48470
|
pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute9(path) && resolve9(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
|
|
48320
48471
|
}
|
|
48321
48472
|
function owner(stats, system = false) {
|
|
@@ -48326,7 +48477,7 @@ function same3(left, right, identityOnly = false) {
|
|
|
48326
48477
|
pluginNeed(right && left.dev === right.dev && left.ino === right.ino && left.mode === right.mode && left.uid === right.uid && left.gid === right.gid && (identityOnly || left.size === right.size && left.nlink === right.nlink && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs), "Plugin filesystem entry changed during access");
|
|
48327
48478
|
}
|
|
48328
48479
|
function ancestors(path, create = false) {
|
|
48329
|
-
|
|
48480
|
+
canonical4(path);
|
|
48330
48481
|
const result = [], parent = dirname10(path), filesystemRoot = parse9(parent).root;
|
|
48331
48482
|
let current = filesystemRoot;
|
|
48332
48483
|
for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
|
|
@@ -48617,11 +48768,11 @@ function validateBinding(value) {
|
|
|
48617
48768
|
for (const key of ["authority", "workspaceId", "profileId", "bundleSlug"])
|
|
48618
48769
|
pluginText(value[key]);
|
|
48619
48770
|
pluginNeed(value.principal.accountId === value.workspaceId, "Plugin principal account differs from its workspace");
|
|
48620
|
-
|
|
48771
|
+
validateSelection({ slug: value.bundleSlug, version: "1.0.0", bundleDigest: `sha256:${"0".repeat(64)}`, authority: value.authority, workspaceId: value.workspaceId, profileRevision: "validation" });
|
|
48621
48772
|
}
|
|
48622
48773
|
function validateObservedSelection(value) {
|
|
48623
48774
|
pluginKeys(value, ["slug", "version", "bundleDigest", "aliases", "triggers", "authority", "workspaceId", "profileRevision"]);
|
|
48624
|
-
|
|
48775
|
+
validateSelection(value);
|
|
48625
48776
|
if (value.triggers !== undefined) {
|
|
48626
48777
|
pluginKeys(value.triggers, ["keywords", "paths", "always"]);
|
|
48627
48778
|
pluginNeed(value.triggers.always === undefined || typeof value.triggers.always === "boolean", "Invalid plugin selection trigger");
|
|
@@ -48904,21 +49055,21 @@ var init_plugin_discovery = __esm(() => {
|
|
|
48904
49055
|
});
|
|
48905
49056
|
|
|
48906
49057
|
// src/lib/claude-marketplace-registry.ts
|
|
48907
|
-
import { createHash as
|
|
49058
|
+
import { createHash as createHash12 } from "crypto";
|
|
48908
49059
|
import { closeSync as closeSync12, constants as constants9, fstatSync as fstatSync12, lstatSync as lstatSync14, openSync as openSync12, readSync as readSync8 } from "fs";
|
|
48909
49060
|
import { basename as basename6, dirname as dirname12, isAbsolute as isAbsolute12, resolve as resolve12 } from "path";
|
|
48910
|
-
function
|
|
49061
|
+
function need4(value, reason) {
|
|
48911
49062
|
if (!value)
|
|
48912
49063
|
throw new Error(`Claude marketplace registry ${reason}`);
|
|
48913
49064
|
}
|
|
48914
49065
|
function absolutePath2(value) {
|
|
48915
|
-
|
|
49066
|
+
need4(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute12(value) && resolve12(value) === value, "requires a normalized absolute path");
|
|
48916
49067
|
}
|
|
48917
49068
|
function parents2(path) {
|
|
48918
49069
|
const result = [];
|
|
48919
49070
|
for (let at = dirname12(path);; at = dirname12(at)) {
|
|
48920
49071
|
const stat2 = lstatSync14(at, { bigint: true });
|
|
48921
|
-
|
|
49072
|
+
need4(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
48922
49073
|
result.push([at, stat2]);
|
|
48923
49074
|
if (at === dirname12(at))
|
|
48924
49075
|
return result;
|
|
@@ -48926,15 +49077,15 @@ function parents2(path) {
|
|
|
48926
49077
|
}
|
|
48927
49078
|
function readRegistry2(path, budget) {
|
|
48928
49079
|
absolutePath2(path);
|
|
48929
|
-
|
|
48930
|
-
|
|
49080
|
+
need4(basename6(path) === "known_marketplaces.json", "requires known_marketplaces.json");
|
|
49081
|
+
need4(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
|
|
48931
49082
|
const ancestors2 = parents2(path), initial = lstatSync14(path, { bigint: true });
|
|
48932
|
-
|
|
48933
|
-
|
|
49083
|
+
need4(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
|
|
49084
|
+
need4(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
|
|
48934
49085
|
const fd = openSync12(path, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
|
|
48935
49086
|
try {
|
|
48936
49087
|
const opened = fstatSync12(fd, { bigint: true });
|
|
48937
|
-
|
|
49088
|
+
need4(opened.isFile() && sameFile2(initial, opened), "changed during open");
|
|
48938
49089
|
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
48939
49090
|
let length = 0;
|
|
48940
49091
|
while (length < bytes.length) {
|
|
@@ -48944,10 +49095,10 @@ function readRegistry2(path, budget) {
|
|
|
48944
49095
|
length += count;
|
|
48945
49096
|
}
|
|
48946
49097
|
const after = fstatSync12(fd, { bigint: true }), current = lstatSync14(path, { bigint: true });
|
|
48947
|
-
|
|
49098
|
+
need4(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
48948
49099
|
for (const [ancestor, before] of ancestors2) {
|
|
48949
49100
|
const now = lstatSync14(ancestor, { bigint: true });
|
|
48950
|
-
|
|
49101
|
+
need4(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
48951
49102
|
}
|
|
48952
49103
|
budget.remaining -= length;
|
|
48953
49104
|
try {
|
|
@@ -48967,7 +49118,7 @@ function parse10(text2) {
|
|
|
48967
49118
|
at++;
|
|
48968
49119
|
};
|
|
48969
49120
|
function string5() {
|
|
48970
|
-
|
|
49121
|
+
need4(text2[at] === '"', "contains invalid JSON");
|
|
48971
49122
|
const start = at++;
|
|
48972
49123
|
while (at < text2.length) {
|
|
48973
49124
|
const character = text2[at++];
|
|
@@ -48978,7 +49129,7 @@ function parse10(text2) {
|
|
|
48978
49129
|
} catch {
|
|
48979
49130
|
throw new Error("Claude marketplace registry contains invalid JSON string");
|
|
48980
49131
|
}
|
|
48981
|
-
|
|
49132
|
+
need4(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
|
|
48982
49133
|
return value2;
|
|
48983
49134
|
}
|
|
48984
49135
|
if (character === "\\")
|
|
@@ -48987,7 +49138,7 @@ function parse10(text2) {
|
|
|
48987
49138
|
throw new Error("Claude marketplace registry contains an unterminated JSON string");
|
|
48988
49139
|
}
|
|
48989
49140
|
function value(depth) {
|
|
48990
|
-
|
|
49141
|
+
need4(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
48991
49142
|
space();
|
|
48992
49143
|
const character = text2[at];
|
|
48993
49144
|
if (character === '"')
|
|
@@ -49001,17 +49152,17 @@ function parse10(text2) {
|
|
|
49001
49152
|
if (object7) {
|
|
49002
49153
|
space();
|
|
49003
49154
|
const key = string5();
|
|
49004
|
-
|
|
49155
|
+
need4(!keys.has(key), "contains a duplicate JSON key");
|
|
49005
49156
|
keys.add(key);
|
|
49006
49157
|
space();
|
|
49007
|
-
|
|
49158
|
+
need4(text2[at++] === ":", "contains invalid JSON");
|
|
49008
49159
|
entries.push([key, value(depth + 1)]);
|
|
49009
49160
|
} else
|
|
49010
49161
|
items.push(value(depth + 1));
|
|
49011
49162
|
space();
|
|
49012
49163
|
if (text2[at] === end)
|
|
49013
49164
|
break;
|
|
49014
|
-
|
|
49165
|
+
need4(text2[at++] === ",", "contains invalid JSON");
|
|
49015
49166
|
}
|
|
49016
49167
|
at++;
|
|
49017
49168
|
return object7 ? { kind: "object", entries } : { kind: "array", items };
|
|
@@ -49023,21 +49174,21 @@ function parse10(text2) {
|
|
|
49023
49174
|
}
|
|
49024
49175
|
numberPattern.lastIndex = at;
|
|
49025
49176
|
const number5 = numberPattern.exec(text2);
|
|
49026
|
-
|
|
49177
|
+
need4(number5, "contains invalid JSON");
|
|
49027
49178
|
at += number5[0].length;
|
|
49028
49179
|
return { kind: "number", value: number5[0] };
|
|
49029
49180
|
}
|
|
49030
49181
|
const root = value(0);
|
|
49031
49182
|
space();
|
|
49032
|
-
|
|
49033
|
-
|
|
49183
|
+
need4(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
|
|
49184
|
+
need4(root.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
|
|
49034
49185
|
return root;
|
|
49035
49186
|
}
|
|
49036
49187
|
function exactKeys(value, keys) {
|
|
49037
49188
|
return value?.kind === "object" && value.entries.length === keys.length && value.entries.every(([key]) => keys.includes(key));
|
|
49038
49189
|
}
|
|
49039
49190
|
function textField(value) {
|
|
49040
|
-
|
|
49191
|
+
need4(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
|
|
49041
49192
|
return value.value;
|
|
49042
49193
|
}
|
|
49043
49194
|
function normalizeRow(row, version2 = 1) {
|
|
@@ -49045,7 +49196,7 @@ function normalizeRow(row, version2 = 1) {
|
|
|
49045
49196
|
if (version2 !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
|
|
49046
49197
|
return;
|
|
49047
49198
|
const autoUpdate = field(row, "autoUpdate");
|
|
49048
|
-
|
|
49199
|
+
need4(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
|
|
49049
49200
|
}
|
|
49050
49201
|
const source = field(row, "source");
|
|
49051
49202
|
if (source?.kind !== "object")
|
|
@@ -49061,23 +49212,23 @@ function normalizeRow(row, version2 = 1) {
|
|
|
49061
49212
|
absolutePath2(sourceLocation);
|
|
49062
49213
|
absolutePath2(textField(field(row, "installLocation")));
|
|
49063
49214
|
const updated = textField(field(row, "lastUpdated"));
|
|
49064
|
-
|
|
49215
|
+
need4(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
|
|
49065
49216
|
const millis = Date.parse(updated);
|
|
49066
|
-
|
|
49217
|
+
need4(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
49067
49218
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
49068
49219
|
}
|
|
49069
49220
|
function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
49070
49221
|
const value = parse10(readRegistry2(path, budget));
|
|
49071
49222
|
for (const [, row] of value.entries)
|
|
49072
49223
|
normalizeRow(row);
|
|
49073
|
-
const sha2562 =
|
|
49224
|
+
const sha2562 = createHash12("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
49074
49225
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
49075
49226
|
}
|
|
49076
49227
|
function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
49077
49228
|
const value = parse10(readRegistry2(path, budget));
|
|
49078
49229
|
for (const [, row] of value.entries)
|
|
49079
49230
|
normalizeRow(row, 2);
|
|
49080
|
-
const sha2562 =
|
|
49231
|
+
const sha2562 = createHash12("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
49081
49232
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
49082
49233
|
}
|
|
49083
49234
|
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, field = (object7, key) => object7.entries.find(([name]) => name === key)?.[1];
|
|
@@ -49087,17 +49238,17 @@ var init_claude_marketplace_registry = __esm(() => {
|
|
|
49087
49238
|
|
|
49088
49239
|
// src/lib/codex-hook-trust-files.ts
|
|
49089
49240
|
import { closeSync as closeSync13, constants as constants10, fstatSync as fstatSync13, fsyncSync as fsyncSync3, lstatSync as lstatSync15, openSync as openSync13, readSync as readSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
49090
|
-
import { createHash as
|
|
49241
|
+
import { createHash as createHash13 } from "crypto";
|
|
49091
49242
|
import { dirname as dirname13 } from "path";
|
|
49092
|
-
function
|
|
49243
|
+
function need5(value, code) {
|
|
49093
49244
|
if (!value)
|
|
49094
49245
|
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
49095
49246
|
}
|
|
49096
49247
|
function safeParents(file2) {
|
|
49097
49248
|
for (let p = dirname13(file2);; p = dirname13(p)) {
|
|
49098
49249
|
const s = lstatSync15(p);
|
|
49099
|
-
|
|
49100
|
-
|
|
49250
|
+
need5(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
|
|
49251
|
+
need5((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
49101
49252
|
if (dirname13(p) === p)
|
|
49102
49253
|
break;
|
|
49103
49254
|
}
|
|
@@ -49106,12 +49257,12 @@ function snapshot(file2, privateFile = false, options = {}) {
|
|
|
49106
49257
|
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
49107
49258
|
safeParents(file2);
|
|
49108
49259
|
const initial = lstatSync15(file2, { bigint: true });
|
|
49109
|
-
|
|
49110
|
-
|
|
49260
|
+
need5(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
|
|
49261
|
+
need5(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
|
|
49111
49262
|
const fd = openSync13(file2, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
|
|
49112
49263
|
try {
|
|
49113
49264
|
const before = fstatSync13(fd, { bigint: true });
|
|
49114
|
-
|
|
49265
|
+
need5(sameStat(initial, before), "INPUT_CHANGED");
|
|
49115
49266
|
const bytes = Buffer.alloc(Number(before.size) + 1);
|
|
49116
49267
|
let length = 0;
|
|
49117
49268
|
while (length < bytes.length) {
|
|
@@ -49120,16 +49271,16 @@ function snapshot(file2, privateFile = false, options = {}) {
|
|
|
49120
49271
|
break;
|
|
49121
49272
|
length += n2;
|
|
49122
49273
|
}
|
|
49123
|
-
|
|
49274
|
+
need5(length === Number(before.size) && sameStat(before, fstatSync13(fd, { bigint: true })) && sameStat(before, lstatSync15(file2, { bigint: true })), "INPUT_CHANGED");
|
|
49124
49275
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
49125
|
-
return { file: file2, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256:
|
|
49276
|
+
return { file: file2, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash13("sha256").update(content).digest("hex") };
|
|
49126
49277
|
} finally {
|
|
49127
49278
|
closeSync13(fd);
|
|
49128
49279
|
}
|
|
49129
49280
|
}
|
|
49130
49281
|
function unchanged2(before) {
|
|
49131
49282
|
const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
49132
|
-
|
|
49283
|
+
need5(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
49133
49284
|
}
|
|
49134
49285
|
var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
|
|
49135
49286
|
var init_codex_hook_trust_files = () => {};
|
|
@@ -49148,10 +49299,10 @@ var init_codex_hook_additions_review = __esm(() => {
|
|
|
49148
49299
|
});
|
|
49149
49300
|
|
|
49150
49301
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
49151
|
-
import { createHash as
|
|
49302
|
+
import { createHash as createHash14 } from "crypto";
|
|
49152
49303
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
49153
49304
|
const proof = proofs2.get(witness);
|
|
49154
|
-
|
|
49305
|
+
need5(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha2(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
49155
49306
|
unchanged2(proof.before);
|
|
49156
49307
|
unchanged2(proof.current);
|
|
49157
49308
|
proof.recheckAdditions();
|
|
@@ -49160,10 +49311,10 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
49160
49311
|
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
49161
49312
|
return false;
|
|
49162
49313
|
assertCodexHookDiscoveryRecovery(binding, witness);
|
|
49163
|
-
|
|
49314
|
+
need5(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
49164
49315
|
return true;
|
|
49165
49316
|
}
|
|
49166
|
-
var
|
|
49317
|
+
var sha2 = (text2) => createHash14("sha256").update(text2).digest("hex"), proofs2;
|
|
49167
49318
|
var init_codex_hook_discovery_recovery = __esm(() => {
|
|
49168
49319
|
init_codex_hook_trust_files();
|
|
49169
49320
|
init_codex_hook_trust_layout();
|
|
@@ -49172,7 +49323,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
|
|
|
49172
49323
|
});
|
|
49173
49324
|
|
|
49174
49325
|
// src/lib/agent-discovery.ts
|
|
49175
|
-
import { createHash as
|
|
49326
|
+
import { createHash as createHash15 } from "crypto";
|
|
49176
49327
|
import { lstatSync as lstatSync16, readFileSync as readFileSync15, readdirSync as readdirSync6, realpathSync as realpathSync3 } from "fs";
|
|
49177
49328
|
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute13, join as join23, resolve as resolve13, sep as sep4 } from "path";
|
|
49178
49329
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
@@ -49208,6 +49359,14 @@ function read(path, changes) {
|
|
|
49208
49359
|
return readFileSync15(path, "utf8");
|
|
49209
49360
|
}
|
|
49210
49361
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
49362
|
+
if (source.hashMode === "codex-settings-v1") {
|
|
49363
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
49364
|
+
throw new Error("Codex settings witnesses require exact typed metadata");
|
|
49365
|
+
const current = captureCodexSettings(source.path, budget).sha256;
|
|
49366
|
+
if (current !== source.sha256)
|
|
49367
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
49368
|
+
return changes?.has(source.path) ? hashCodexSettingsReplacement(changes.get(source.path), budget) : current;
|
|
49369
|
+
}
|
|
49211
49370
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
49212
49371
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
49213
49372
|
throw new Error("Claude settings witnesses require exact typed metadata");
|
|
@@ -49276,6 +49435,8 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
49276
49435
|
safe4(root);
|
|
49277
49436
|
}
|
|
49278
49437
|
function assertMarketplaceBinding(binding, source) {
|
|
49438
|
+
if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" || basename7(source.path) !== "config.toml"))
|
|
49439
|
+
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
49279
49440
|
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename7(source.path) !== "settings.json"))
|
|
49280
49441
|
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
49281
49442
|
if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
@@ -49285,10 +49446,10 @@ function agentDiscoveryConfigPath(home, agent) {
|
|
|
49285
49446
|
return join23(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
49286
49447
|
}
|
|
49287
49448
|
function resolveAgentDiscovery(options) {
|
|
49288
|
-
const
|
|
49449
|
+
const canonical5 = options.canonical ?? resolve13, home = resolve13(options.home), agent = options.agent;
|
|
49289
49450
|
const sources = [], roots = new Set, builtinNames = [];
|
|
49290
49451
|
const witness = (path, format, fields) => {
|
|
49291
|
-
const source = { path:
|
|
49452
|
+
const source = { path: canonical5(path), sha256: null, ...format ? { format, fields } : {} };
|
|
49292
49453
|
source.sha256 = projected(source);
|
|
49293
49454
|
sources.push(source);
|
|
49294
49455
|
return read(source.path);
|
|
@@ -49355,9 +49516,9 @@ function resolveAgentDiscovery(options) {
|
|
|
49355
49516
|
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
49356
49517
|
unresolved("invalid retained review");
|
|
49357
49518
|
verifyAgentDiscovery(retained);
|
|
49358
|
-
if (!retained.sources.some((source) => source.path ===
|
|
49519
|
+
if (!retained.sources.some((source) => source.path === canonical5(configPath) && source.format === undefined && source.fields === undefined))
|
|
49359
49520
|
unresolved("retained review is missing the full agent configuration source");
|
|
49360
|
-
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && source.path !==
|
|
49521
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "codex-settings-v1")) && source.path !== canonical5(configPath)))
|
|
49361
49522
|
unresolved("retained settings witness names another configuration source");
|
|
49362
49523
|
if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual2(saved, source))) || [...roots].some((root) => !retained.roots.includes(root)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
49363
49524
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
@@ -49373,14 +49534,14 @@ function resolveAgentDiscovery(options) {
|
|
|
49373
49534
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
49374
49535
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
49375
49536
|
verifyAgentDiscovery(supplied);
|
|
49376
|
-
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && source.path !==
|
|
49537
|
+
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "codex-settings-v1")) && source.path !== canonical5(configPath)))
|
|
49377
49538
|
throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
|
|
49378
|
-
if (!review.sources.some((source) => source.path ===
|
|
49539
|
+
if (!review.sources.some((source) => source.path === canonical5(configPath)))
|
|
49379
49540
|
throw new Error("Discovery review must include the agent configuration source");
|
|
49380
49541
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...sources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
|
|
49381
49542
|
}
|
|
49382
49543
|
function plugin(root) {
|
|
49383
|
-
root =
|
|
49544
|
+
root = canonical5(root);
|
|
49384
49545
|
safe4(root);
|
|
49385
49546
|
const manifestPath = join23(root, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
49386
49547
|
const raw = witness(manifestPath);
|
|
@@ -49412,7 +49573,7 @@ function resolveAgentDiscovery(options) {
|
|
|
49412
49573
|
if (agent === "claude") {
|
|
49413
49574
|
const registrations = witness(join23(home, ".claude/plugins/installed_plugins.json"));
|
|
49414
49575
|
const installed = registrations === null ? {} : parseConfig(registrations, join23(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
49415
|
-
const commands =
|
|
49576
|
+
const commands = canonical5(join23(home, ".claude/commands"));
|
|
49416
49577
|
safe4(commands);
|
|
49417
49578
|
if (lstatSync16(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync6(commands).length)
|
|
49418
49579
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
@@ -49486,9 +49647,9 @@ function resolveAgentDiscovery(options) {
|
|
|
49486
49647
|
roots.add(join23(root, "skills"));
|
|
49487
49648
|
}
|
|
49488
49649
|
}
|
|
49489
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join23(home, ".hermes/plugins"), join23(home, ".hermes/hermes-agent")].map((path) =>
|
|
49650
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join23(home, ".hermes/plugins"), join23(home, ".hermes/hermes-agent")].map((path) => canonical5(path))) } : {} };
|
|
49490
49651
|
}
|
|
49491
|
-
var digest = (text2) =>
|
|
49652
|
+
var digest = (text2) => createHash15("sha256").update(text2).digest("hex");
|
|
49492
49653
|
var init_agent_discovery = __esm(() => {
|
|
49493
49654
|
init_agent_policy_limits();
|
|
49494
49655
|
init_agent_hermes();
|
|
@@ -49500,6 +49661,7 @@ var init_agent_discovery = __esm(() => {
|
|
|
49500
49661
|
init_claude_marketplace_registry();
|
|
49501
49662
|
init_claude_settings_witness();
|
|
49502
49663
|
init_codex_hook_discovery_recovery();
|
|
49664
|
+
init_codex_settings_witness();
|
|
49503
49665
|
init_agent_discovery_directories();
|
|
49504
49666
|
});
|
|
49505
49667
|
|
|
@@ -50564,7 +50726,7 @@ var init_remote_customer_operations = __esm(() => {
|
|
|
50564
50726
|
});
|
|
50565
50727
|
|
|
50566
50728
|
// src/lib/canonical-json.ts
|
|
50567
|
-
import { createHash as
|
|
50729
|
+
import { createHash as createHash16 } from "crypto";
|
|
50568
50730
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
50569
50731
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
50570
50732
|
}
|
|
@@ -50608,7 +50770,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
50608
50770
|
return visit3(value, 0);
|
|
50609
50771
|
}
|
|
50610
50772
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
50611
|
-
return
|
|
50773
|
+
return createHash16("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
50612
50774
|
}
|
|
50613
50775
|
var init_canonical_json = () => {};
|
|
50614
50776
|
|
|
@@ -52834,7 +52996,7 @@ var init_remote_invitation_recovery = __esm(() => {
|
|
|
52834
52996
|
});
|
|
52835
52997
|
|
|
52836
52998
|
// src/lib/remote-private-publications.ts
|
|
52837
|
-
import { createHash as
|
|
52999
|
+
import { createHash as createHash17 } from "crypto";
|
|
52838
53000
|
function checkedPublicationDeclaration(value) {
|
|
52839
53001
|
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)
|
|
52840
53002
|
return bad();
|
|
@@ -53095,7 +53257,7 @@ var PRIVATE_PUBLICATION_MAX_BYTES, failures2, PrivatePublicationError, bad = ()
|
|
|
53095
53257
|
throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
|
|
53096
53258
|
}, invalid4 = () => {
|
|
53097
53259
|
throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
|
|
53098
|
-
}, 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, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(","), date6 = (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) =>
|
|
53260
|
+
}, 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, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(","), date6 = (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) => createHash17("sha256").update(bytes).digest("hex");
|
|
53099
53261
|
var init_remote_private_publications = __esm(() => {
|
|
53100
53262
|
init_fleet_credentials();
|
|
53101
53263
|
init_remote_workspace_selection();
|
|
@@ -53873,7 +54035,7 @@ async function continueRecurringRecovery(customer, directory, options) {
|
|
|
53873
54035
|
throw error61;
|
|
53874
54036
|
}
|
|
53875
54037
|
}
|
|
53876
|
-
var uuid12,
|
|
54038
|
+
var uuid12, sha3, common, intentSchema, stateSchema, invalid5 = () => {
|
|
53877
54039
|
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.");
|
|
53878
54040
|
};
|
|
53879
54041
|
var init_recurring_recovery = __esm(() => {
|
|
@@ -53883,7 +54045,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
53883
54045
|
init_remote_recurring();
|
|
53884
54046
|
init_recurring_customer();
|
|
53885
54047
|
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}$/);
|
|
53886
|
-
|
|
54048
|
+
sha3 = exports_external.string().regex(/^[a-f0-9]{64}$/);
|
|
53887
54049
|
common = exports_external.object({
|
|
53888
54050
|
contractVersion: exports_external.literal(1),
|
|
53889
54051
|
apiOrigin: exports_external.string().min(1).max(4096),
|
|
@@ -53896,7 +54058,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
53896
54058
|
common.extend({
|
|
53897
54059
|
operation: exports_external.literal("activate"),
|
|
53898
54060
|
draftId: uuid12,
|
|
53899
|
-
termsSha256:
|
|
54061
|
+
termsSha256: sha3,
|
|
53900
54062
|
approvalDeadline: exports_external.string().datetime(),
|
|
53901
54063
|
approval: recurringActivationSchema
|
|
53902
54064
|
}).strict(),
|
|
@@ -53904,7 +54066,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
53904
54066
|
]);
|
|
53905
54067
|
stateSchema = exports_external.object({
|
|
53906
54068
|
contractVersion: exports_external.literal(1),
|
|
53907
|
-
intentSha256:
|
|
54069
|
+
intentSha256: sha3,
|
|
53908
54070
|
phase: exports_external.enum(["prepared", "pending", "observed"]),
|
|
53909
54071
|
consentId: uuid12.nullable()
|
|
53910
54072
|
}).strict();
|
|
@@ -55845,7 +56007,7 @@ var init_credential_state = __esm(() => {
|
|
|
55845
56007
|
});
|
|
55846
56008
|
|
|
55847
56009
|
// src/lib/run-state.ts
|
|
55848
|
-
import { createHash as
|
|
56010
|
+
import { createHash as createHash18, randomBytes } from "crypto";
|
|
55849
56011
|
import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync19, readdirSync as readdirSync9, statSync as statSync9, writeFileSync as writeFileSync10 } from "fs";
|
|
55850
56012
|
import { extname, join as join29, relative as relative5 } from "path";
|
|
55851
56013
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
@@ -55960,7 +56122,7 @@ function collectRunArtifacts(context2) {
|
|
|
55960
56122
|
artifacts.push({
|
|
55961
56123
|
path: toProjectRelative(context2.targetDir, path),
|
|
55962
56124
|
mime: mimeForPath(path),
|
|
55963
|
-
sha256:
|
|
56125
|
+
sha256: createHash18("sha256").update(bytes).digest("hex"),
|
|
55964
56126
|
sizeBytes: stat3.size
|
|
55965
56127
|
});
|
|
55966
56128
|
}
|
|
@@ -57774,7 +57936,7 @@ __export(exports_selected_run, {
|
|
|
57774
57936
|
prepareSelectedSecretBindings: () => prepareSelectedSecretBindings,
|
|
57775
57937
|
executeSelectedLocal: () => executeSelectedLocal
|
|
57776
57938
|
});
|
|
57777
|
-
import { createHash as
|
|
57939
|
+
import { createHash as createHash19 } from "crypto";
|
|
57778
57940
|
import { spawn } from "child_process";
|
|
57779
57941
|
import { mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync11, realpathSync as realpathSync8 } from "fs";
|
|
57780
57942
|
import { dirname as dirname19, join as join31, resolve as resolve17 } from "path";
|
|
@@ -57900,7 +58062,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
57900
58062
|
selection: selection2,
|
|
57901
58063
|
target: "local",
|
|
57902
58064
|
runtime: runtimeName,
|
|
57903
|
-
inputDigest: `sha256:${
|
|
58065
|
+
inputDigest: `sha256:${createHash19("sha256").update(input2).digest("hex")}`,
|
|
57904
58066
|
runDirectory,
|
|
57905
58067
|
...executionGrant ? { executionGrant } : {},
|
|
57906
58068
|
...bindings ? { secretBinding: { schema: bindings.schema, selection: bindings.selection, consumer: bindings.consumer, secretsAuthority: bindings.secretsAuthority, bindings: bindings.bindings } } : {},
|
|
@@ -59128,7 +59290,7 @@ var init_schedule_tools = __esm(() => {
|
|
|
59128
59290
|
});
|
|
59129
59291
|
|
|
59130
59292
|
// src/lib/native-storage.ts
|
|
59131
|
-
import { createHash as
|
|
59293
|
+
import { createHash as createHash20, createHmac } from "crypto";
|
|
59132
59294
|
import {
|
|
59133
59295
|
existsSync as existsSync18,
|
|
59134
59296
|
mkdirSync as mkdirSync15,
|
|
@@ -59205,7 +59367,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
59205
59367
|
files.push({
|
|
59206
59368
|
path: relativePath,
|
|
59207
59369
|
sizeBytes: bytes.byteLength,
|
|
59208
|
-
sha256:
|
|
59370
|
+
sha256: createHash20("sha256").update(bytes).digest("hex"),
|
|
59209
59371
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
59210
59372
|
});
|
|
59211
59373
|
}
|