@hasna/skills 0.8.11 → 0.9.1
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 +21 -4
- package/bin/index.js +853 -446
- package/bin/mcp.js +309 -155
- package/bin/migrate.js +100 -10
- package/bin/server.js +596 -51
- package/bin/worker.js +283 -20
- package/dist/cli/commands/registry.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +498 -184
- package/dist/lib/agent-discovery.d.ts +1 -1
- package/dist/lib/claude-settings-witness.d.ts +19 -0
- package/dist/lib/cloud-executions.d.ts +20 -0
- package/dist/lib/registry-types.d.ts +3 -0
- package/dist/lib/remote-client.d.ts +11 -0
- package/dist/lib/revision.d.ts +3 -0
- package/dist/sdk/execution/admission.d.ts +2 -1
- package/dist/sdk/execution/types.d.ts +9 -0
- package/dist/sdk/index.d.ts +1 -0
- package/dist/sdk/index.js +1755 -752
- package/dist/server/runtime-policy.d.ts +4 -2
- package/dist/server/runtime-pure-contract.d.ts +51 -0
- package/dist/server/runtime-store.d.ts +4 -0
- package/dist/server/runtime-worker.d.ts +2 -1
- package/dist/server/selection-store.d.ts +9 -1
- package/dist/server/skills-api.d.ts +2 -1
- package/dist/server/sqlite-store.d.ts +4 -1
- package/dist/server/store.d.ts +6 -1
- package/dist/server/types.d.ts +27 -0
- package/docs/plugin-admission.md +67 -0
- package/migrations/postgres/0010_skill_archive_lifecycle.sql +5 -0
- package/migrations/sqlite/0010_skill_archive_lifecycle.sql +5 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8323,6 +8323,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
|
|
|
8323
8323
|
const version = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
8324
8324
|
const kind = parseSkillKind(stringField(jsonManifest, "kind") ?? frontmatter?.kind);
|
|
8325
8325
|
const commands = parseManifestCommands(jsonManifest) ?? (kind === "instruction" ? [] : inferPackageCommands(pkg, name)) ?? [];
|
|
8326
|
+
const runtime = parseManifestRuntime(jsonManifest) ?? inferRuntimeFromCommands(commands);
|
|
8326
8327
|
return {
|
|
8327
8328
|
$schema: stringField(jsonManifest, "$schema") ?? PORTABLE_SKILL_SCHEMA,
|
|
8328
8329
|
standard: stringField(jsonManifest, "standard") ?? PORTABLE_SKILL_STANDARD,
|
|
@@ -8335,7 +8336,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
|
|
|
8335
8336
|
...kind ? { kind } : {},
|
|
8336
8337
|
inputs: kind === "instruction" ? parseManifestInputs(jsonManifest) ?? [] : parseManifestInputs(jsonManifest) ?? DEFAULT_INPUTS,
|
|
8337
8338
|
commands,
|
|
8338
|
-
...
|
|
8339
|
+
...runtime ? { runtime } : {},
|
|
8339
8340
|
...parseManifestProvenance(jsonManifest) ? { provenance: parseManifestProvenance(jsonManifest) } : {}
|
|
8340
8341
|
};
|
|
8341
8342
|
}
|
|
@@ -8854,6 +8855,12 @@ function inferPackageCommands(pkg, fallbackName) {
|
|
|
8854
8855
|
}
|
|
8855
8856
|
return;
|
|
8856
8857
|
}
|
|
8858
|
+
function inferRuntimeFromCommands(commands) {
|
|
8859
|
+
const entrypoint = commands.find((command) => command.entry)?.entry;
|
|
8860
|
+
if (!entrypoint || !entrypoint.toLowerCase().endsWith(".py"))
|
|
8861
|
+
return;
|
|
8862
|
+
return { ...defaultRuntimeContract(entrypoint), runtime: "python3" };
|
|
8863
|
+
}
|
|
8857
8864
|
function readJsonObject(path) {
|
|
8858
8865
|
const parsed = JSON.parse(readFileSync5(path, "utf-8"));
|
|
8859
8866
|
if (!isRecord2(parsed))
|
|
@@ -9650,7 +9657,7 @@ import { homedir as homedir5 } from "os";
|
|
|
9650
9657
|
import {
|
|
9651
9658
|
cpSync as cpSync2,
|
|
9652
9659
|
existsSync as existsSync12,
|
|
9653
|
-
lstatSync as
|
|
9660
|
+
lstatSync as lstatSync19,
|
|
9654
9661
|
mkdirSync as mkdirSync8,
|
|
9655
9662
|
mkdtempSync as mkdtempSync2,
|
|
9656
9663
|
readFileSync as readFileSync15,
|
|
@@ -9661,12 +9668,12 @@ import {
|
|
|
9661
9668
|
writeFileSync as writeFileSync6
|
|
9662
9669
|
} from "fs";
|
|
9663
9670
|
import { homedir as homedir4 } from "os";
|
|
9664
|
-
import { basename as
|
|
9671
|
+
import { basename as basename9, dirname as dirname16, join as join26 } from "path";
|
|
9665
9672
|
|
|
9666
9673
|
// src/lib/agent-integration.ts
|
|
9667
|
-
import { createHash as
|
|
9668
|
-
import { existsSync as existsSync11, lstatSync as
|
|
9669
|
-
import { dirname as
|
|
9674
|
+
import { createHash as createHash14, randomUUID as randomUUID3 } from "crypto";
|
|
9675
|
+
import { existsSync as existsSync11, lstatSync as lstatSync18, statSync as statSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync14, readdirSync as readdirSync10, opendirSync as opendirSync5, readlinkSync as readlinkSync2, realpathSync as realpathSync5, renameSync as renameSync4, rmdirSync as rmdirSync3, writeFileSync as writeFileSync5, unlinkSync as unlinkSync4, chmodSync, openSync as openSync11, closeSync as closeSync11, fsyncSync as fsyncSync3, fstatSync as fstatSync11, readSync as readSync9, constants as constants10, linkSync as linkSync2 } from "fs";
|
|
9676
|
+
import { dirname as dirname15, join as join25, resolve as resolve15, relative as relative5, isAbsolute as isAbsolute15, sep as sep6 } from "path";
|
|
9670
9677
|
import { homedir as homedir3 } from "os";
|
|
9671
9678
|
|
|
9672
9679
|
// src/lib/managed-policy.ts
|
|
@@ -10807,7 +10814,9 @@ function assertAgentPolicyCollections(policy) {
|
|
|
10807
10814
|
text(source.path);
|
|
10808
10815
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
10809
10816
|
if (source.hashMode !== undefined)
|
|
10810
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
10817
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
10818
|
+
if (source.hashMode === "claude-settings-v1")
|
|
10819
|
+
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve4(source.path) === source.path && basename3(source.path) === "settings.json");
|
|
10811
10820
|
if (source.hashMode === "claude-marketplace-registry")
|
|
10812
10821
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve4(source.path) === source.path && basename3(source.path) === "known_marketplaces.json");
|
|
10813
10822
|
if (source.hashMode === "claude-plugin-registry") {
|
|
@@ -10961,9 +10970,9 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
10961
10970
|
}
|
|
10962
10971
|
|
|
10963
10972
|
// src/lib/agent-discovery.ts
|
|
10964
|
-
import { createHash as
|
|
10965
|
-
import { lstatSync as
|
|
10966
|
-
import { dirname as
|
|
10973
|
+
import { createHash as createHash13 } from "crypto";
|
|
10974
|
+
import { lstatSync as lstatSync17, readFileSync as readFileSync13, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
|
|
10975
|
+
import { basename as basename8, dirname as dirname14, isAbsolute as isAbsolute14, join as join23, resolve as resolve14, sep as sep5 } from "path";
|
|
10967
10976
|
|
|
10968
10977
|
// node_modules/yaml/dist/index.js
|
|
10969
10978
|
var composer = require_composer();
|
|
@@ -11439,7 +11448,7 @@ import { isAbsolute as isAbsolute10, join as join20, resolve as resolve10 } from
|
|
|
11439
11448
|
// package.json
|
|
11440
11449
|
var package_default = {
|
|
11441
11450
|
name: "@hasna/skills",
|
|
11442
|
-
version: "0.
|
|
11451
|
+
version: "0.9.1",
|
|
11443
11452
|
description: "Skills library for AI coding agents",
|
|
11444
11453
|
type: "module",
|
|
11445
11454
|
bin: {
|
|
@@ -14931,8 +14940,228 @@ function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024
|
|
|
14931
14940
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
14932
14941
|
}
|
|
14933
14942
|
|
|
14943
|
+
// src/lib/claude-settings-witness.ts
|
|
14944
|
+
import { createHash as createHash12 } from "crypto";
|
|
14945
|
+
import { closeSync as closeSync10, constants as constants9, fstatSync as fstatSync10, lstatSync as lstatSync16, openSync as openSync10, readSync as readSync8 } from "fs";
|
|
14946
|
+
import { basename as basename7, dirname as dirname13, isAbsolute as isAbsolute13, resolve as resolve13 } from "path";
|
|
14947
|
+
var CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
14948
|
+
function need3(value, reason) {
|
|
14949
|
+
if (!value)
|
|
14950
|
+
throw new Error(`Claude settings witness ${reason}`);
|
|
14951
|
+
}
|
|
14952
|
+
function absolutePath2(value) {
|
|
14953
|
+
need3(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute13(value) && resolve13(value) === value, "requires a normalized absolute path");
|
|
14954
|
+
}
|
|
14955
|
+
var sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
|
|
14956
|
+
function parents2(path) {
|
|
14957
|
+
const result = [];
|
|
14958
|
+
for (let at = dirname13(path);; at = dirname13(at)) {
|
|
14959
|
+
const stat2 = lstatSync16(at, { bigint: true });
|
|
14960
|
+
need3(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
14961
|
+
result.push([at, stat2]);
|
|
14962
|
+
if (at === dirname13(at))
|
|
14963
|
+
return result;
|
|
14964
|
+
}
|
|
14965
|
+
}
|
|
14966
|
+
function readSettings(path, budget) {
|
|
14967
|
+
absolutePath2(path);
|
|
14968
|
+
need3(basename7(path) === "settings.json", "requires settings.json");
|
|
14969
|
+
need3(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
|
|
14970
|
+
const ancestors2 = parents2(path), initial = lstatSync16(path, { bigint: true });
|
|
14971
|
+
need3(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_SETTINGS_WITNESS_LIMITS.bytes), "is not a bounded regular file");
|
|
14972
|
+
need3(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
|
|
14973
|
+
const fd = openSync10(path, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
|
|
14974
|
+
try {
|
|
14975
|
+
const opened = fstatSync10(fd, { bigint: true });
|
|
14976
|
+
need3(opened.isFile() && sameFile2(initial, opened), "changed during open");
|
|
14977
|
+
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
14978
|
+
let length = 0;
|
|
14979
|
+
while (length < bytes.length) {
|
|
14980
|
+
const count = readSync8(fd, bytes, length, bytes.length - length, null);
|
|
14981
|
+
if (!count)
|
|
14982
|
+
break;
|
|
14983
|
+
length += count;
|
|
14984
|
+
}
|
|
14985
|
+
const after = fstatSync10(fd, { bigint: true }), current = lstatSync16(path, { bigint: true });
|
|
14986
|
+
need3(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
14987
|
+
for (const [ancestor, before] of ancestors2) {
|
|
14988
|
+
const now = lstatSync16(ancestor, { bigint: true });
|
|
14989
|
+
need3(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
14990
|
+
}
|
|
14991
|
+
budget.remaining -= length;
|
|
14992
|
+
try {
|
|
14993
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
|
|
14994
|
+
} catch {
|
|
14995
|
+
throw new Error("Claude settings witness is not strict UTF-8");
|
|
14996
|
+
}
|
|
14997
|
+
} finally {
|
|
14998
|
+
closeSync10(fd);
|
|
14999
|
+
}
|
|
15000
|
+
}
|
|
15001
|
+
function parse6(text2) {
|
|
15002
|
+
let at = 0, nodes = 0;
|
|
15003
|
+
const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
|
|
15004
|
+
const space = () => {
|
|
15005
|
+
while (at < text2.length && /[\x20\t\r\n]/.test(text2[at]))
|
|
15006
|
+
at++;
|
|
15007
|
+
};
|
|
15008
|
+
function string() {
|
|
15009
|
+
need3(text2[at] === '"', "contains invalid JSON");
|
|
15010
|
+
const start = at++;
|
|
15011
|
+
while (at < text2.length) {
|
|
15012
|
+
const character = text2[at++];
|
|
15013
|
+
if (character === '"') {
|
|
15014
|
+
let value2;
|
|
15015
|
+
try {
|
|
15016
|
+
value2 = JSON.parse(text2.slice(start, at));
|
|
15017
|
+
} catch {
|
|
15018
|
+
throw new Error("Claude settings witness contains invalid JSON string");
|
|
15019
|
+
}
|
|
15020
|
+
need3(value2.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.stringCharacters, "exceeds its string limit");
|
|
15021
|
+
return value2;
|
|
15022
|
+
}
|
|
15023
|
+
if (character === "\\")
|
|
15024
|
+
at++;
|
|
15025
|
+
}
|
|
15026
|
+
throw new Error("Claude settings witness contains an unterminated JSON string");
|
|
15027
|
+
}
|
|
15028
|
+
function value(depth) {
|
|
15029
|
+
need3(depth <= CLAUDE_SETTINGS_WITNESS_LIMITS.depth && ++nodes <= CLAUDE_SETTINGS_WITNESS_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
15030
|
+
space();
|
|
15031
|
+
const character = text2[at];
|
|
15032
|
+
if (character === '"')
|
|
15033
|
+
return { kind: "string", value: string() };
|
|
15034
|
+
if (character === "{" || character === "[") {
|
|
15035
|
+
at++;
|
|
15036
|
+
space();
|
|
15037
|
+
const object4 = character === "{", end = object4 ? "}" : "]", entries = [], items = [], keys = new Set;
|
|
15038
|
+
if (text2[at] !== end)
|
|
15039
|
+
while (true) {
|
|
15040
|
+
if (object4) {
|
|
15041
|
+
space();
|
|
15042
|
+
const key = string();
|
|
15043
|
+
need3(!keys.has(key), "contains a duplicate JSON key");
|
|
15044
|
+
keys.add(key);
|
|
15045
|
+
space();
|
|
15046
|
+
need3(text2[at++] === ":", "contains invalid JSON");
|
|
15047
|
+
entries.push([key, value(depth + 1)]);
|
|
15048
|
+
} else
|
|
15049
|
+
items.push(value(depth + 1));
|
|
15050
|
+
space();
|
|
15051
|
+
if (text2[at] === end)
|
|
15052
|
+
break;
|
|
15053
|
+
need3(text2[at++] === ",", "contains invalid JSON");
|
|
15054
|
+
}
|
|
15055
|
+
at++;
|
|
15056
|
+
return object4 ? { kind: "object", entries } : { kind: "array", items };
|
|
15057
|
+
}
|
|
15058
|
+
for (const literal of ["true", "false", "null"])
|
|
15059
|
+
if (text2.startsWith(literal, at)) {
|
|
15060
|
+
at += literal.length;
|
|
15061
|
+
return { kind: "literal", value: literal };
|
|
15062
|
+
}
|
|
15063
|
+
numberPattern.lastIndex = at;
|
|
15064
|
+
const number = numberPattern.exec(text2);
|
|
15065
|
+
need3(number, "contains invalid JSON");
|
|
15066
|
+
at += number[0].length;
|
|
15067
|
+
return { kind: "number", value: number[0] };
|
|
15068
|
+
}
|
|
15069
|
+
const root2 = value(0);
|
|
15070
|
+
space();
|
|
15071
|
+
need3(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
|
|
15072
|
+
need3(root2.entries.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.rows, "exceeds its settings field limit");
|
|
15073
|
+
return root2;
|
|
15074
|
+
}
|
|
15075
|
+
var BOOLEAN_PREFERENCES = new Set([
|
|
15076
|
+
"autoScrollEnabled",
|
|
15077
|
+
"axScreenReader",
|
|
15078
|
+
"emojiCompletionEnabled",
|
|
15079
|
+
"prefersReducedMotion",
|
|
15080
|
+
"showTurnDuration",
|
|
15081
|
+
"spinnerTipsEnabled",
|
|
15082
|
+
"syntaxHighlightingDisabled",
|
|
15083
|
+
"terminalProgressBarEnabled",
|
|
15084
|
+
"terminalTitleFromRename",
|
|
15085
|
+
"verbose",
|
|
15086
|
+
"wheelScrollAccelerationEnabled"
|
|
15087
|
+
]);
|
|
15088
|
+
var ENUM_PREFERENCES = Object.freeze({
|
|
15089
|
+
editorMode: ["normal", "vim"],
|
|
15090
|
+
tui: ["default", "fullscreen"],
|
|
15091
|
+
viewMode: ["default", "verbose", "focus"]
|
|
15092
|
+
});
|
|
15093
|
+
var BUILTIN_MODEL_SELECTIONS = new Set([
|
|
15094
|
+
"default",
|
|
15095
|
+
"best",
|
|
15096
|
+
"fable",
|
|
15097
|
+
"fable[1m]",
|
|
15098
|
+
"sonnet",
|
|
15099
|
+
"sonnet[1m]",
|
|
15100
|
+
"opus",
|
|
15101
|
+
"opus[1m]",
|
|
15102
|
+
"haiku",
|
|
15103
|
+
"opusplan",
|
|
15104
|
+
"claude-fable-5-1",
|
|
15105
|
+
"claude-fable-5",
|
|
15106
|
+
"claude-fable-5[1m]",
|
|
15107
|
+
"claude-opus-5",
|
|
15108
|
+
"claude-sonnet-5",
|
|
15109
|
+
"claude-haiku-4-5-20251001",
|
|
15110
|
+
"claude-opus-4-6",
|
|
15111
|
+
"claude-sonnet-4-5",
|
|
15112
|
+
"claude-sonnet-4-5-20250929",
|
|
15113
|
+
"claude-opus-4-8",
|
|
15114
|
+
"claude-opus-4-8[1m]",
|
|
15115
|
+
"claude-opus-4-7",
|
|
15116
|
+
"claude-sonnet-4-6",
|
|
15117
|
+
"claude-opus-4-5-20251101",
|
|
15118
|
+
"claude-opus-4-5",
|
|
15119
|
+
"claude-haiku-4-5",
|
|
15120
|
+
"claude-fable-5-1[1m]",
|
|
15121
|
+
"claude-opus-5[1m]",
|
|
15122
|
+
"claude-opus-4-7[1m]",
|
|
15123
|
+
"claude-opus-4-6[1m]",
|
|
15124
|
+
"claude-sonnet-4-6[1m]"
|
|
15125
|
+
]);
|
|
15126
|
+
function canonical3(value) {
|
|
15127
|
+
if (value.kind === "object")
|
|
15128
|
+
return { kind: "object", entries: value.entries.map(([key, child]) => [key, canonical3(child)]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0) };
|
|
15129
|
+
if (value.kind === "array")
|
|
15130
|
+
return { kind: "array", items: value.items.map(canonical3) };
|
|
15131
|
+
return value;
|
|
15132
|
+
}
|
|
15133
|
+
function settingsDigest(text2) {
|
|
15134
|
+
const value = parse6(text2);
|
|
15135
|
+
value.entries = value.entries.filter(([key, child]) => {
|
|
15136
|
+
if (key === "model") {
|
|
15137
|
+
need3(child.kind === "string", "contains an invalid model selection");
|
|
15138
|
+
return !BUILTIN_MODEL_SELECTIONS.has(child.value);
|
|
15139
|
+
}
|
|
15140
|
+
if (BOOLEAN_PREFERENCES.has(key)) {
|
|
15141
|
+
need3(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid display preference");
|
|
15142
|
+
return false;
|
|
15143
|
+
}
|
|
15144
|
+
if (Object.hasOwn(ENUM_PREFERENCES, key)) {
|
|
15145
|
+
need3(child.kind === "string" && ENUM_PREFERENCES[key].includes(child.value), "contains an invalid display preference");
|
|
15146
|
+
return false;
|
|
15147
|
+
}
|
|
15148
|
+
return true;
|
|
15149
|
+
});
|
|
15150
|
+
return createHash12("sha256").update("hasna.skills.claude-settings.v1\x00").update(JSON.stringify(canonical3(value))).digest("hex");
|
|
15151
|
+
}
|
|
15152
|
+
function hashClaudeSettingsReplacement(text2, budget) {
|
|
15153
|
+
need3(typeof text2 === "string", "requires settings text");
|
|
15154
|
+
const bytes = Buffer.byteLength(text2);
|
|
15155
|
+
need3(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
15156
|
+
budget.remaining -= bytes;
|
|
15157
|
+
return settingsDigest(text2);
|
|
15158
|
+
}
|
|
15159
|
+
function captureClaudeSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
15160
|
+
return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readSettings(path, budget)) };
|
|
15161
|
+
}
|
|
15162
|
+
|
|
14934
15163
|
// src/lib/agent-discovery.ts
|
|
14935
|
-
var digest = (text2) =>
|
|
15164
|
+
var digest = (text2) => createHash13("sha256").update(text2).digest("hex");
|
|
14936
15165
|
function parseConfig(text2, path, toml = false) {
|
|
14937
15166
|
try {
|
|
14938
15167
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -14944,12 +15173,12 @@ function parseConfig(text2, path, toml = false) {
|
|
|
14944
15173
|
}
|
|
14945
15174
|
}
|
|
14946
15175
|
function safe4(path) {
|
|
14947
|
-
if (!
|
|
15176
|
+
if (!isAbsolute14(path) || path.includes("\x00"))
|
|
14948
15177
|
throw new Error("Expected an absolute native discovery path");
|
|
14949
|
-
for (let cursor = path;; cursor =
|
|
14950
|
-
if (
|
|
15178
|
+
for (let cursor = path;; cursor = dirname14(cursor)) {
|
|
15179
|
+
if (lstatSync17(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
14951
15180
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
14952
|
-
if (
|
|
15181
|
+
if (dirname14(cursor) === cursor)
|
|
14953
15182
|
break;
|
|
14954
15183
|
}
|
|
14955
15184
|
}
|
|
@@ -14957,7 +15186,7 @@ function read(path, changes) {
|
|
|
14957
15186
|
safe4(path);
|
|
14958
15187
|
if (changes?.has(path))
|
|
14959
15188
|
return changes.get(path);
|
|
14960
|
-
const stat2 =
|
|
15189
|
+
const stat2 = lstatSync17(path, { throwIfNoEntry: false });
|
|
14961
15190
|
if (!stat2)
|
|
14962
15191
|
return null;
|
|
14963
15192
|
if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
|
|
@@ -14965,6 +15194,14 @@ function read(path, changes) {
|
|
|
14965
15194
|
return readFileSync13(path, "utf8");
|
|
14966
15195
|
}
|
|
14967
15196
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
15197
|
+
if (source.hashMode === "claude-settings-v1") {
|
|
15198
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
15199
|
+
throw new Error("Claude settings witnesses require exact typed metadata");
|
|
15200
|
+
const current = captureClaudeSettings(source.path, budget).sha256;
|
|
15201
|
+
if (current !== source.sha256)
|
|
15202
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
15203
|
+
return changes?.has(source.path) ? hashClaudeSettingsReplacement(changes.get(source.path), budget) : current;
|
|
15204
|
+
}
|
|
14968
15205
|
if (source.hashMode === "claude-marketplace-registry") {
|
|
14969
15206
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
|
|
14970
15207
|
throw new Error("Claude marketplace witnesses require an exact reviewed registry without synthetic changes");
|
|
@@ -15022,6 +15259,8 @@ function verifyAgentDiscovery(binding) {
|
|
|
15022
15259
|
safe4(root2);
|
|
15023
15260
|
}
|
|
15024
15261
|
function assertMarketplaceBinding(binding, source) {
|
|
15262
|
+
if (source.hashMode === "claude-settings-v1" && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
|
|
15263
|
+
throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
|
|
15025
15264
|
if (source.hashMode === "claude-marketplace-registry" && (binding.agent !== "claude" || binding.method !== "reviewed"))
|
|
15026
15265
|
throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
|
|
15027
15266
|
}
|
|
@@ -15045,10 +15284,10 @@ function captureDiscoveryPathSources(paths) {
|
|
|
15045
15284
|
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
|
|
15046
15285
|
}
|
|
15047
15286
|
function resolveAgentDiscovery(options) {
|
|
15048
|
-
const
|
|
15287
|
+
const canonical4 = options.canonical ?? resolve14, home = resolve14(options.home), agent = options.agent;
|
|
15049
15288
|
const sources = [], roots = new Set, builtinNames = [];
|
|
15050
15289
|
const witness = (path, format, fields) => {
|
|
15051
|
-
const source = { path:
|
|
15290
|
+
const source = { path: canonical4(path), sha256: null, ...format ? { format, fields } : {} };
|
|
15052
15291
|
source.sha256 = projected(source);
|
|
15053
15292
|
sources.push(source);
|
|
15054
15293
|
return read(source.path);
|
|
@@ -15076,7 +15315,7 @@ function resolveAgentDiscovery(options) {
|
|
|
15076
15315
|
continue;
|
|
15077
15316
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
15078
15317
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
15079
|
-
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join23(home, normalized.slice(2)) :
|
|
15318
|
+
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join23(home, normalized.slice(2)) : resolve14(home, ".hermes", normalized);
|
|
15080
15319
|
safe4(path);
|
|
15081
15320
|
roots.add(path);
|
|
15082
15321
|
}
|
|
@@ -15085,21 +15324,21 @@ function resolveAgentDiscovery(options) {
|
|
|
15085
15324
|
const executable = Bun.which("gemini");
|
|
15086
15325
|
if (executable) {
|
|
15087
15326
|
let packageRoot;
|
|
15088
|
-
for (let directory =
|
|
15327
|
+
for (let directory = dirname14(realpathSync4(executable)), depth = 0;depth < 8; directory = dirname14(directory), depth++) {
|
|
15089
15328
|
const path = join23(directory, "package.json"), raw = read(path);
|
|
15090
15329
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
15091
15330
|
packageRoot = directory;
|
|
15092
15331
|
witness(path);
|
|
15093
15332
|
break;
|
|
15094
15333
|
}
|
|
15095
|
-
if (
|
|
15334
|
+
if (dirname14(directory) === directory)
|
|
15096
15335
|
break;
|
|
15097
15336
|
}
|
|
15098
15337
|
if (!packageRoot)
|
|
15099
15338
|
unresolved("installed Gemini package layout is not recognized");
|
|
15100
15339
|
const builtinRoot = join23(packageRoot, "bundle/builtin");
|
|
15101
15340
|
safe4(builtinRoot);
|
|
15102
|
-
if (!
|
|
15341
|
+
if (!lstatSync17(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
15103
15342
|
unresolved("installed Gemini builtin root is missing");
|
|
15104
15343
|
for (const name of readdirSync9(builtinRoot).sort()) {
|
|
15105
15344
|
const text2 = witness(join23(builtinRoot, name, "SKILL.md"));
|
|
@@ -15120,12 +15359,14 @@ function resolveAgentDiscovery(options) {
|
|
|
15120
15359
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
15121
15360
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
15122
15361
|
verifyAgentDiscovery(supplied);
|
|
15123
|
-
if (
|
|
15362
|
+
if (review.sources.some((source) => source.hashMode === "claude-settings-v1" && source.path !== canonical4(configPath)))
|
|
15363
|
+
throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
|
|
15364
|
+
if (!review.sources.some((source) => source.path === canonical4(configPath)))
|
|
15124
15365
|
throw new Error("Discovery review must include the agent configuration source");
|
|
15125
15366
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...sources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
|
|
15126
15367
|
}
|
|
15127
15368
|
function plugin(root2) {
|
|
15128
|
-
root2 =
|
|
15369
|
+
root2 = canonical4(root2);
|
|
15129
15370
|
safe4(root2);
|
|
15130
15371
|
const manifestPath = join23(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
15131
15372
|
const raw = witness(manifestPath);
|
|
@@ -15137,7 +15378,7 @@ function resolveAgentDiscovery(options) {
|
|
|
15137
15378
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
15138
15379
|
const commands = join23(root2, "commands");
|
|
15139
15380
|
safe4(commands);
|
|
15140
|
-
if (agent === "claude" && (manifest.commands !== undefined ||
|
|
15381
|
+
if (agent === "claude" && (manifest.commands !== undefined || lstatSync17(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length))
|
|
15141
15382
|
unresolved("legacy command discovery requires retirement or a dedicated format adapter");
|
|
15142
15383
|
const rootSkill = witness(join23(root2, "SKILL.md"));
|
|
15143
15384
|
if (rootSkill !== null)
|
|
@@ -15148,18 +15389,18 @@ function resolveAgentDiscovery(options) {
|
|
|
15148
15389
|
if (agent === "claude")
|
|
15149
15390
|
roots.add(join23(root2, "skills"));
|
|
15150
15391
|
for (const value of declared) {
|
|
15151
|
-
const path =
|
|
15392
|
+
const path = resolve14(root2, value);
|
|
15152
15393
|
if (path !== root2 && !path.startsWith(root2 + sep5))
|
|
15153
15394
|
unresolved("plugin skill path escapes its root");
|
|
15154
|
-
roots.add(path.endsWith(`${sep5}SKILL.md`) ?
|
|
15395
|
+
roots.add(path.endsWith(`${sep5}SKILL.md`) ? dirname14(path) : path);
|
|
15155
15396
|
}
|
|
15156
15397
|
}
|
|
15157
15398
|
if (agent === "claude") {
|
|
15158
15399
|
const registrations = witness(join23(home, ".claude/plugins/installed_plugins.json"));
|
|
15159
15400
|
const installed = registrations === null ? {} : parseConfig(registrations, join23(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
15160
|
-
const commands =
|
|
15401
|
+
const commands = canonical4(join23(home, ".claude/commands"));
|
|
15161
15402
|
safe4(commands);
|
|
15162
|
-
if (
|
|
15403
|
+
if (lstatSync17(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length)
|
|
15163
15404
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
15164
15405
|
for (const [id, enabled] of Object.entries(config.enabledPlugins ?? {})) {
|
|
15165
15406
|
if (enabled === false)
|
|
@@ -15179,7 +15420,7 @@ function resolveAgentDiscovery(options) {
|
|
|
15179
15420
|
unresolved("unsupported plugin enablement");
|
|
15180
15421
|
const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
|
|
15181
15422
|
const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
15182
|
-
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !
|
|
15423
|
+
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute14(market.source))
|
|
15183
15424
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
15184
15425
|
const catalogText = witness(join23(market.source, ".agents/plugins/marketplace.json"));
|
|
15185
15426
|
if (catalogText === null)
|
|
@@ -15187,7 +15428,7 @@ function resolveAgentDiscovery(options) {
|
|
|
15187
15428
|
const catalog = parseConfig(catalogText, join23(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
15188
15429
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
15189
15430
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
15190
|
-
plugin(
|
|
15431
|
+
plugin(resolve14(market.source, matches[0].source.path));
|
|
15191
15432
|
}
|
|
15192
15433
|
} else if (agent === "opencode") {
|
|
15193
15434
|
if (witness(join23(home, ".config/opencode/opencode.jsonc")) !== null || witness(join23(home, ".config/opencode/config.json")) !== null)
|
|
@@ -15195,18 +15436,18 @@ function resolveAgentDiscovery(options) {
|
|
|
15195
15436
|
if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
|
|
15196
15437
|
unresolved("external plugin or remote skill source");
|
|
15197
15438
|
for (const path of config.skills?.paths ?? []) {
|
|
15198
|
-
if (typeof path !== "string" || !
|
|
15439
|
+
if (typeof path !== "string" || !isAbsolute14(path))
|
|
15199
15440
|
unresolved("relative or malformed added skill root");
|
|
15200
15441
|
roots.add(path);
|
|
15201
15442
|
}
|
|
15202
15443
|
const localPlugins = join23(home, ".config/opencode/plugins");
|
|
15203
15444
|
safe4(localPlugins);
|
|
15204
|
-
if (
|
|
15445
|
+
if (lstatSync17(localPlugins, { throwIfNoEntry: false }) && readdirSync9(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
15205
15446
|
unresolved("additional local plugin hooks require review");
|
|
15206
15447
|
} else if (agent === "hermes") {
|
|
15207
15448
|
for (const directory of [join23(home, ".hermes/plugins"), join23(home, ".hermes/hermes-agent")]) {
|
|
15208
15449
|
safe4(directory);
|
|
15209
|
-
if (
|
|
15450
|
+
if (lstatSync17(directory, { throwIfNoEntry: false }))
|
|
15210
15451
|
unresolved("installed runtime/plugin discovery requires reviewed source bindings");
|
|
15211
15452
|
}
|
|
15212
15453
|
if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
|
|
@@ -15216,11 +15457,11 @@ function resolveAgentDiscovery(options) {
|
|
|
15216
15457
|
unresolved("custom extension or skill paths");
|
|
15217
15458
|
const directory = join23(home, ".gemini/extensions");
|
|
15218
15459
|
safe4(directory);
|
|
15219
|
-
if (
|
|
15460
|
+
if (lstatSync17(directory, { throwIfNoEntry: false }))
|
|
15220
15461
|
for (const name of readdirSync9(directory)) {
|
|
15221
15462
|
const root2 = join23(directory, name);
|
|
15222
15463
|
safe4(root2);
|
|
15223
|
-
if (!
|
|
15464
|
+
if (!lstatSync17(root2)?.isDirectory())
|
|
15224
15465
|
continue;
|
|
15225
15466
|
const raw = witness(join23(root2, "gemini-extension.json"));
|
|
15226
15467
|
if (raw === null)
|
|
@@ -15231,7 +15472,7 @@ function resolveAgentDiscovery(options) {
|
|
|
15231
15472
|
roots.add(join23(root2, "skills"));
|
|
15232
15473
|
}
|
|
15233
15474
|
}
|
|
15234
|
-
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) =>
|
|
15475
|
+
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) => canonical4(path))) } : {} };
|
|
15235
15476
|
}
|
|
15236
15477
|
|
|
15237
15478
|
// src/lib/agent-adapters.ts
|
|
@@ -15375,7 +15616,7 @@ function disableCodexBundledSkills(text2) {
|
|
|
15375
15616
|
}
|
|
15376
15617
|
|
|
15377
15618
|
// src/lib/agent-integration.ts
|
|
15378
|
-
var sha = (data) =>
|
|
15619
|
+
var sha = (data) => createHash14("sha256").update(data).digest("hex");
|
|
15379
15620
|
var ROOTS = [
|
|
15380
15621
|
["claude", ".claude/skills"],
|
|
15381
15622
|
["codex", ".codex/skills"],
|
|
@@ -15394,11 +15635,11 @@ var ROOTS = [
|
|
|
15394
15635
|
["copilot", ".github/skills"]
|
|
15395
15636
|
];
|
|
15396
15637
|
function assertSafePath(path) {
|
|
15397
|
-
let cursor =
|
|
15638
|
+
let cursor = resolve15(path);
|
|
15398
15639
|
while (true) {
|
|
15399
|
-
if (
|
|
15640
|
+
if (lstatSync18(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
15400
15641
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
15401
|
-
const parent =
|
|
15642
|
+
const parent = dirname15(cursor);
|
|
15402
15643
|
if (parent === cursor)
|
|
15403
15644
|
return;
|
|
15404
15645
|
cursor = parent;
|
|
@@ -15407,16 +15648,16 @@ function assertSafePath(path) {
|
|
|
15407
15648
|
function readRootAlias(home, agent) {
|
|
15408
15649
|
assertSafePath(home);
|
|
15409
15650
|
const realHome = realpathSync5(home), alias = join25(realHome, `.${agent}`);
|
|
15410
|
-
const stat2 =
|
|
15651
|
+
const stat2 = lstatSync18(alias, { throwIfNoEntry: false, bigint: true });
|
|
15411
15652
|
if (!stat2?.isSymbolicLink())
|
|
15412
15653
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
15413
|
-
const link = readlinkSync2(alias), targetPath =
|
|
15654
|
+
const link = readlinkSync2(alias), targetPath = resolve15(dirname15(alias), link);
|
|
15414
15655
|
assertSafePath(targetPath);
|
|
15415
|
-
const targetStat =
|
|
15656
|
+
const targetStat = lstatSync18(targetPath, { throwIfNoEntry: false });
|
|
15416
15657
|
if (!targetStat?.isDirectory())
|
|
15417
15658
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
15418
15659
|
const target = realpathSync5(targetPath), inside = relative5(realHome, target);
|
|
15419
|
-
if (!inside || inside === ".." || inside.startsWith(`..${sep6}`) ||
|
|
15660
|
+
if (!inside || inside === ".." || inside.startsWith(`..${sep6}`) || isAbsolute15(inside))
|
|
15420
15661
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
15421
15662
|
return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
15422
15663
|
}
|
|
@@ -15425,14 +15666,14 @@ function rootAliases(home, allowed = false) {
|
|
|
15425
15666
|
return [];
|
|
15426
15667
|
const result = [];
|
|
15427
15668
|
for (const agent of ["claude", "codex"])
|
|
15428
|
-
if (
|
|
15669
|
+
if (lstatSync18(join25(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
|
|
15429
15670
|
result.push(readRootAlias(home, agent));
|
|
15430
15671
|
if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep6) || other.target.startsWith(item.target + sep6))))
|
|
15431
15672
|
throw new Error("Agent root alias targets must not overlap");
|
|
15432
15673
|
return result;
|
|
15433
15674
|
}
|
|
15434
15675
|
function canonicalAgentPath(path, aliases) {
|
|
15435
|
-
const absolute =
|
|
15676
|
+
const absolute = resolve15(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep6));
|
|
15436
15677
|
return binding ? join25(binding.target, relative5(binding.alias, absolute)) : absolute;
|
|
15437
15678
|
}
|
|
15438
15679
|
function recheckRootAliases(aliases) {
|
|
@@ -15446,35 +15687,35 @@ function recheckRootAliases(aliases) {
|
|
|
15446
15687
|
}
|
|
15447
15688
|
function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
15448
15689
|
assertSafePath(path);
|
|
15449
|
-
const before =
|
|
15690
|
+
const before = lstatSync18(path);
|
|
15450
15691
|
if (!before.isFile() || before.size > maximum)
|
|
15451
15692
|
throw new Error("Unsupported or oversized native skill file");
|
|
15452
|
-
const descriptor =
|
|
15693
|
+
const descriptor = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
|
|
15453
15694
|
try {
|
|
15454
|
-
const opened =
|
|
15695
|
+
const opened = fstatSync11(descriptor);
|
|
15455
15696
|
if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size)
|
|
15456
15697
|
throw new Error("Native skill file changed while reading");
|
|
15457
15698
|
const bytes = Buffer.allocUnsafe(before.size + 1);
|
|
15458
15699
|
let length = 0;
|
|
15459
15700
|
while (length < bytes.length) {
|
|
15460
|
-
const count =
|
|
15701
|
+
const count = readSync9(descriptor, bytes, length, bytes.length - length, null);
|
|
15461
15702
|
if (!count)
|
|
15462
15703
|
break;
|
|
15463
15704
|
length += count;
|
|
15464
15705
|
}
|
|
15465
|
-
const after =
|
|
15706
|
+
const after = fstatSync11(descriptor);
|
|
15466
15707
|
if (length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
|
|
15467
15708
|
throw new Error("Native skill file changed while reading");
|
|
15468
15709
|
return bytes.subarray(0, length);
|
|
15469
15710
|
} finally {
|
|
15470
|
-
|
|
15711
|
+
closeSync11(descriptor);
|
|
15471
15712
|
}
|
|
15472
15713
|
}
|
|
15473
15714
|
function treeHash(root2) {
|
|
15474
|
-
const hash2 =
|
|
15715
|
+
const hash2 = createHash14("sha256");
|
|
15475
15716
|
let bytes = 0, files = 0;
|
|
15476
15717
|
function visit2(path) {
|
|
15477
|
-
const stat2 =
|
|
15718
|
+
const stat2 = lstatSync18(path);
|
|
15478
15719
|
if (stat2.isSymbolicLink())
|
|
15479
15720
|
throw new Error(`Refusing symlink in skill: ${path}`);
|
|
15480
15721
|
const rel = relative5(root2, path);
|
|
@@ -15500,11 +15741,11 @@ function treeHash(root2) {
|
|
|
15500
15741
|
function projectAncestorDirectories(projects) {
|
|
15501
15742
|
const directories = new Set;
|
|
15502
15743
|
for (const project of projects) {
|
|
15503
|
-
for (let path =
|
|
15744
|
+
for (let path = resolve15(project), depth = 0;; depth++) {
|
|
15504
15745
|
if (depth >= 100)
|
|
15505
15746
|
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
15506
15747
|
directories.add(path);
|
|
15507
|
-
const parent =
|
|
15748
|
+
const parent = dirname15(path);
|
|
15508
15749
|
if (parent === path)
|
|
15509
15750
|
break;
|
|
15510
15751
|
path = parent;
|
|
@@ -15531,10 +15772,10 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
15531
15772
|
throw new Error("Native skill discovery metadata limit exceeded");
|
|
15532
15773
|
}
|
|
15533
15774
|
function emptySiblingCacheAlias(path, parent) {
|
|
15534
|
-
const link = readlinkSync2(path), target =
|
|
15535
|
-
if (
|
|
15775
|
+
const link = readlinkSync2(path), target = resolve15(parent, link);
|
|
15776
|
+
if (dirname15(target) !== parent || (isAbsolute15(link) ? link !== target : dirname15(link) !== "."))
|
|
15536
15777
|
return false;
|
|
15537
|
-
if (!
|
|
15778
|
+
if (!lstatSync18(target, { throwIfNoEntry: false })?.isDirectory())
|
|
15538
15779
|
return false;
|
|
15539
15780
|
const scan = cacheScans.get(target);
|
|
15540
15781
|
return Boolean(scan?.complete && !scan.hasSkills && scan.entries <= maxAliasProofEntries);
|
|
@@ -15546,7 +15787,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
15546
15787
|
assertSafePath(path);
|
|
15547
15788
|
if (!existsSync11(path))
|
|
15548
15789
|
return scan;
|
|
15549
|
-
const stat2 =
|
|
15790
|
+
const stat2 = lstatSync18(path);
|
|
15550
15791
|
if (stat2.isFile())
|
|
15551
15792
|
return scan;
|
|
15552
15793
|
if (!stat2.isDirectory())
|
|
@@ -15565,7 +15806,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
15565
15806
|
}
|
|
15566
15807
|
const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
|
|
15567
15808
|
const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep6));
|
|
15568
|
-
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join25(home, ".codex", "skills", ".system"), aliases) + sep6) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome:
|
|
15809
|
+
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join25(home, ".codex", "skills", ".system"), aliases) + sep6) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve15(home) } : {}, ...rootAlias ? { rootAlias } : {} });
|
|
15569
15810
|
return scan;
|
|
15570
15811
|
}
|
|
15571
15812
|
if (depth > (vendor ? 32 : 3))
|
|
@@ -15592,7 +15833,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
15592
15833
|
if (isVendor && !options.includeVendor)
|
|
15593
15834
|
continue;
|
|
15594
15835
|
const child = join25(path, name);
|
|
15595
|
-
if (isVendor &&
|
|
15836
|
+
if (isVendor && lstatSync18(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
15596
15837
|
const target = statSync8(child, { throwIfNoEntry: false });
|
|
15597
15838
|
if (target?.isFile() || pluginCache && target?.isDirectory() && emptySiblingCacheAlias(child, path)) {
|
|
15598
15839
|
scan.entries++;
|
|
@@ -15714,7 +15955,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
15714
15955
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
15715
15956
|
const bundled = disableCodexBundledSkills(text2);
|
|
15716
15957
|
let result = bundled;
|
|
15717
|
-
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [
|
|
15958
|
+
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve15(bridgePath), resolve15(join25(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
|
|
15718
15959
|
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
15719
15960
|
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
15720
15961
|
throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
|
|
@@ -15726,7 +15967,7 @@ function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
|
15726
15967
|
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
15727
15968
|
const parsed = Bun.TOML.parse(section);
|
|
15728
15969
|
const declared = parsed.skills?.config?.[0];
|
|
15729
|
-
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [
|
|
15970
|
+
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve15(path), resolve15(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
|
|
15730
15971
|
return section;
|
|
15731
15972
|
found = true;
|
|
15732
15973
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -15844,7 +16085,7 @@ function planAgentIntegration(options) {
|
|
|
15844
16085
|
version: CLI_BRIDGE_VERSION,
|
|
15845
16086
|
digest: CLI_BRIDGE_DIGEST,
|
|
15846
16087
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
15847
|
-
home:
|
|
16088
|
+
home: resolve15(home),
|
|
15848
16089
|
includeVendor: true,
|
|
15849
16090
|
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join25(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
|
|
15850
16091
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
@@ -15861,7 +16102,7 @@ function planAgentIntegration(options) {
|
|
|
15861
16102
|
}
|
|
15862
16103
|
function atomicWrite2(path, content) {
|
|
15863
16104
|
assertSafePath(path);
|
|
15864
|
-
mkdirSync7(
|
|
16105
|
+
mkdirSync7(dirname15(path), { recursive: true, mode: 448 });
|
|
15865
16106
|
const temporary = `${path}.skills-${randomUUID3()}`;
|
|
15866
16107
|
try {
|
|
15867
16108
|
writeFileSync5(temporary, content, { mode: 384, flag: "wx" });
|
|
@@ -15874,10 +16115,10 @@ function atomicWrite2(path, content) {
|
|
|
15874
16115
|
function applyAgentIntegration(plan) {
|
|
15875
16116
|
const policyPath = join25(plan.dataDir, "agent-policy.json");
|
|
15876
16117
|
assertSafePath(policyPath);
|
|
15877
|
-
const currentText = (path) =>
|
|
16118
|
+
const currentText = (path) => resolve15(path) === resolve15(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
|
|
15878
16119
|
readManagedSkillPolicySnapshot(plan.dataDir);
|
|
15879
16120
|
for (const change of plan.changes)
|
|
15880
|
-
if (
|
|
16121
|
+
if (resolve15(change.path) === resolve15(policyPath))
|
|
15881
16122
|
parseManagedSkillPolicy(change.after);
|
|
15882
16123
|
const aliases = plan.rootAliases ?? [];
|
|
15883
16124
|
recheckRootAliases(aliases);
|
|
@@ -15907,7 +16148,7 @@ function applyAgentIntegration(plan) {
|
|
|
15907
16148
|
recheckRootAliases(aliases);
|
|
15908
16149
|
if (currentText(change.path) !== change.before)
|
|
15909
16150
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
15910
|
-
for (let directory =
|
|
16151
|
+
for (let directory = dirname15(change.path);!existsSync11(directory); directory = dirname15(directory))
|
|
15911
16152
|
createdDirectories.add(directory);
|
|
15912
16153
|
const after = change.after;
|
|
15913
16154
|
atomicWrite2(change.path, after);
|
|
@@ -15947,11 +16188,11 @@ function applyAgentIntegration(plan) {
|
|
|
15947
16188
|
}
|
|
15948
16189
|
function syncArchiveDirectory(path) {
|
|
15949
16190
|
assertSafePath(path);
|
|
15950
|
-
const descriptor =
|
|
16191
|
+
const descriptor = openSync11(path, constants10.O_RDONLY | constants10.O_DIRECTORY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
|
|
15951
16192
|
try {
|
|
15952
16193
|
fsyncSync3(descriptor);
|
|
15953
16194
|
} finally {
|
|
15954
|
-
|
|
16195
|
+
closeSync11(descriptor);
|
|
15955
16196
|
}
|
|
15956
16197
|
}
|
|
15957
16198
|
function writeArchiveJournal(path, value) {
|
|
@@ -15959,18 +16200,18 @@ function writeArchiveJournal(path, value) {
|
|
|
15959
16200
|
const temporary = `${path}.skills-${randomUUID3()}`;
|
|
15960
16201
|
let descriptor;
|
|
15961
16202
|
try {
|
|
15962
|
-
descriptor =
|
|
16203
|
+
descriptor = openSync11(temporary, constants10.O_WRONLY | constants10.O_CREAT | constants10.O_EXCL | constants10.O_NOFOLLOW, 384);
|
|
15963
16204
|
writeFileSync5(descriptor, `${JSON.stringify(value)}
|
|
15964
16205
|
`);
|
|
15965
16206
|
fsyncSync3(descriptor);
|
|
15966
|
-
|
|
16207
|
+
closeSync11(descriptor);
|
|
15967
16208
|
descriptor = undefined;
|
|
15968
16209
|
renameSync4(temporary, path);
|
|
15969
|
-
syncArchiveDirectory(
|
|
16210
|
+
syncArchiveDirectory(dirname15(path));
|
|
15970
16211
|
} finally {
|
|
15971
16212
|
if (descriptor !== undefined)
|
|
15972
|
-
|
|
15973
|
-
if (
|
|
16213
|
+
closeSync11(descriptor);
|
|
16214
|
+
if (lstatSync18(temporary, { throwIfNoEntry: false }))
|
|
15974
16215
|
unlinkSync4(temporary);
|
|
15975
16216
|
}
|
|
15976
16217
|
}
|
|
@@ -16003,7 +16244,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16003
16244
|
const moved = [];
|
|
16004
16245
|
const verifyArchive = (entry) => {
|
|
16005
16246
|
assertSafePath(entry.archive);
|
|
16006
|
-
const stat2 =
|
|
16247
|
+
const stat2 = lstatSync18(entry.archive);
|
|
16007
16248
|
if (entry.discoveryOnly ? !stat2.isFile() : !stat2.isDirectory())
|
|
16008
16249
|
throw new Error("Archive type changed");
|
|
16009
16250
|
if ((entry.discoveryOnly ? sha(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
@@ -16011,14 +16252,14 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16011
16252
|
};
|
|
16012
16253
|
assertSafePath(archiveRoot);
|
|
16013
16254
|
const created = [];
|
|
16014
|
-
for (let path = archiveRoot;!existsSync11(path); path =
|
|
16255
|
+
for (let path = archiveRoot;!existsSync11(path); path = dirname15(path))
|
|
16015
16256
|
created.push(path);
|
|
16016
16257
|
mkdirSync7(archiveRoot, { recursive: true, mode: 448 });
|
|
16017
16258
|
try {
|
|
16018
16259
|
for (const directory of created)
|
|
16019
16260
|
syncArchiveDirectory(directory);
|
|
16020
16261
|
if (created.length)
|
|
16021
|
-
syncArchiveDirectory(
|
|
16262
|
+
syncArchiveDirectory(dirname15(created.at(-1)));
|
|
16022
16263
|
writeArchiveJournal(receiptPath, journal);
|
|
16023
16264
|
for (const [index, entry] of selected.entries()) {
|
|
16024
16265
|
recheckRootAliases(aliases);
|
|
@@ -16030,7 +16271,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16030
16271
|
writeArchiveJournal(receiptPath, journal);
|
|
16031
16272
|
renameSync4(move.source, move.archive);
|
|
16032
16273
|
moved.push(move);
|
|
16033
|
-
syncArchiveDirectory(
|
|
16274
|
+
syncArchiveDirectory(dirname15(move.source));
|
|
16034
16275
|
syncArchiveDirectory(archiveRoot);
|
|
16035
16276
|
verifyArchive(move);
|
|
16036
16277
|
move.status = "archived";
|
|
@@ -16051,7 +16292,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16051
16292
|
recheckRootAliases(aliases);
|
|
16052
16293
|
assertSafePath(entry.source);
|
|
16053
16294
|
assertSafePath(entry.archive);
|
|
16054
|
-
if (
|
|
16295
|
+
if (lstatSync18(entry.source, { throwIfNoEntry: false })) {
|
|
16055
16296
|
entry.status = "recovery-required";
|
|
16056
16297
|
entry.conflict = "source-occupied";
|
|
16057
16298
|
continue;
|
|
@@ -16065,10 +16306,10 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16065
16306
|
}
|
|
16066
16307
|
if (entry.discoveryOnly) {
|
|
16067
16308
|
linkSync2(entry.archive, entry.source);
|
|
16068
|
-
syncArchiveDirectory(
|
|
16309
|
+
syncArchiveDirectory(dirname15(entry.source));
|
|
16069
16310
|
unlinkSync4(entry.archive);
|
|
16070
16311
|
} else {
|
|
16071
|
-
if (
|
|
16312
|
+
if (lstatSync18(entry.source, { throwIfNoEntry: false })) {
|
|
16072
16313
|
entry.status = "recovery-required";
|
|
16073
16314
|
entry.conflict = "source-occupied";
|
|
16074
16315
|
continue;
|
|
@@ -16076,7 +16317,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16076
16317
|
renameSync4(entry.archive, entry.source);
|
|
16077
16318
|
}
|
|
16078
16319
|
entry.status = "restored";
|
|
16079
|
-
syncArchiveDirectory(
|
|
16320
|
+
syncArchiveDirectory(dirname15(entry.source));
|
|
16080
16321
|
syncArchiveDirectory(archiveRoot);
|
|
16081
16322
|
} catch {
|
|
16082
16323
|
entry.status = "recovery-required";
|
|
@@ -16115,7 +16356,7 @@ function isSkillsOwnershipMarker(marker) {
|
|
|
16115
16356
|
function hasSkillsOwnershipMarker(dir) {
|
|
16116
16357
|
const path = join26(dir, SYNC_MARKER_FILE);
|
|
16117
16358
|
try {
|
|
16118
|
-
if (!
|
|
16359
|
+
if (!lstatSync19(path).isFile())
|
|
16119
16360
|
return false;
|
|
16120
16361
|
const marker = JSON.parse(readFileSync15(path, "utf8"));
|
|
16121
16362
|
return isSkillsOwnershipMarker(marker);
|
|
@@ -16341,9 +16582,9 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
16341
16582
|
const action = dirExists ? "update" : "create";
|
|
16342
16583
|
if (options.dryRun)
|
|
16343
16584
|
return { action, path: skillMdPath };
|
|
16344
|
-
const parentDir =
|
|
16585
|
+
const parentDir = dirname16(dir);
|
|
16345
16586
|
mkdirSync8(parentDir, { recursive: true });
|
|
16346
|
-
const transactionDir = mkdtempSync2(join26(parentDir, `.hasna-skills-write-${
|
|
16587
|
+
const transactionDir = mkdtempSync2(join26(parentDir, `.hasna-skills-write-${basename9(dir)}-`));
|
|
16347
16588
|
const candidateDir = join26(transactionDir, "candidate");
|
|
16348
16589
|
const backupDir = join26(transactionDir, "backup");
|
|
16349
16590
|
const candidateSkillMdPath = join26(candidateDir, "SKILL.md");
|
|
@@ -16832,7 +17073,7 @@ function loadProjectConfigCompat(targetDir) {
|
|
|
16832
17073
|
return loadProjectConfig(targetDir);
|
|
16833
17074
|
}
|
|
16834
17075
|
// src/lib/run-state.ts
|
|
16835
|
-
import { createHash as
|
|
17076
|
+
import { createHash as createHash15, randomBytes } from "crypto";
|
|
16836
17077
|
import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync18, readdirSync as readdirSync12, statSync as statSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
16837
17078
|
import { extname, join as join29, relative as relative6 } from "path";
|
|
16838
17079
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
@@ -16959,7 +17200,7 @@ function collectRunArtifacts(context) {
|
|
|
16959
17200
|
artifacts.push({
|
|
16960
17201
|
path: toProjectRelative(context.targetDir, path),
|
|
16961
17202
|
mime: mimeForPath(path),
|
|
16962
|
-
sha256:
|
|
17203
|
+
sha256: createHash15("sha256").update(bytes).digest("hex"),
|
|
16963
17204
|
sizeBytes: stat2.size
|
|
16964
17205
|
});
|
|
16965
17206
|
}
|
|
@@ -17087,7 +17328,7 @@ __export(exports_external, {
|
|
|
17087
17328
|
pipe: () => pipe,
|
|
17088
17329
|
partialRecord: () => partialRecord,
|
|
17089
17330
|
parseAsync: () => parseAsync2,
|
|
17090
|
-
parse: () =>
|
|
17331
|
+
parse: () => parse9,
|
|
17091
17332
|
overwrite: () => _overwrite,
|
|
17092
17333
|
output: () => output,
|
|
17093
17334
|
optional: () => optional,
|
|
@@ -17307,7 +17548,7 @@ __export(exports_core2, {
|
|
|
17307
17548
|
prettifyError: () => prettifyError,
|
|
17308
17549
|
parseURLObject: () => parseURLObject,
|
|
17309
17550
|
parseAsync: () => parseAsync,
|
|
17310
|
-
parse: () =>
|
|
17551
|
+
parse: () => parse7,
|
|
17311
17552
|
meta: () => meta,
|
|
17312
17553
|
mergeValues: () => mergeValues,
|
|
17313
17554
|
memoizer: () => memoizer,
|
|
@@ -18761,7 +19002,7 @@ var _parse = (_Err) => {
|
|
|
18761
19002
|
};
|
|
18762
19003
|
return fn;
|
|
18763
19004
|
};
|
|
18764
|
-
var
|
|
19005
|
+
var parse7 = /* @__PURE__ */ _parse($ZodRealError);
|
|
18765
19006
|
var _parseAsync = (_Err) => {
|
|
18766
19007
|
const fn = async (schema, value, _ctx, params) => {
|
|
18767
19008
|
const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
|
|
@@ -18832,18 +19073,18 @@ var validateAsync = async (schema, value, _ctx) => {
|
|
|
18832
19073
|
return result.issues.length === 0;
|
|
18833
19074
|
};
|
|
18834
19075
|
var _encode = (_Err) => {
|
|
18835
|
-
const
|
|
19076
|
+
const parse8 = _parse(_Err);
|
|
18836
19077
|
const fn = (schema, value, _ctx, _params) => {
|
|
18837
19078
|
const ctx = _ctx ? { ..._ctx, direction: "backward" } : { direction: "backward" };
|
|
18838
|
-
return
|
|
19079
|
+
return parse8(schema, value, ctx, finalizeParams(fn, _params));
|
|
18839
19080
|
};
|
|
18840
19081
|
return fn;
|
|
18841
19082
|
};
|
|
18842
19083
|
var encode = /* @__PURE__ */ _encode($ZodRealError);
|
|
18843
19084
|
var _decode = (_Err) => {
|
|
18844
|
-
const
|
|
19085
|
+
const parse8 = _parse(_Err);
|
|
18845
19086
|
const fn = (schema, value, _ctx, _params) => {
|
|
18846
|
-
return
|
|
19087
|
+
return parse8(schema, value, _ctx, finalizeParams(fn, _params));
|
|
18847
19088
|
};
|
|
18848
19089
|
return fn;
|
|
18849
19090
|
};
|
|
@@ -21766,10 +22007,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
|
|
|
21766
22007
|
throw new Error("implement() must be called with a function");
|
|
21767
22008
|
}
|
|
21768
22009
|
return Object.defineProperty(function(...args) {
|
|
21769
|
-
const parsedArgs = inst._def.input ?
|
|
22010
|
+
const parsedArgs = inst._def.input ? parse7(inst._def.input, args) : args;
|
|
21770
22011
|
const result = Reflect.apply(func, this, parsedArgs);
|
|
21771
22012
|
if (inst._def.output) {
|
|
21772
|
-
return
|
|
22013
|
+
return parse7(inst._def.output, result);
|
|
21773
22014
|
}
|
|
21774
22015
|
return result;
|
|
21775
22016
|
}, "_zod", { value: inst._zod, enumerable: false });
|
|
@@ -33407,7 +33648,7 @@ var ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer2, undefi
|
|
|
33407
33648
|
});
|
|
33408
33649
|
|
|
33409
33650
|
// node_modules/zod/v4/classic/parse.js
|
|
33410
|
-
var
|
|
33651
|
+
var parse9 = /* @__PURE__ */ _parse(ZodRealError);
|
|
33411
33652
|
var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
33412
33653
|
var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
33413
33654
|
var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
|
|
@@ -33543,7 +33784,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
|
|
|
33543
33784
|
exports_util.own(this, "~standard", value);
|
|
33544
33785
|
},
|
|
33545
33786
|
parse: function _parse2(data, params) {
|
|
33546
|
-
return
|
|
33787
|
+
return parse9(this, data, params, { callee: _parse2 });
|
|
33547
33788
|
},
|
|
33548
33789
|
parseAsync: async function _parseAsync2(data, params) {
|
|
33549
33790
|
return await parseAsync2(this, data, params, { callee: _parseAsync2 });
|
|
@@ -35720,6 +35961,8 @@ var remoteSkillSchema = exports_external.object({
|
|
|
35720
35961
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
35721
35962
|
dependencies: exports_external.array(exports_external.string()).optional(),
|
|
35722
35963
|
version: exports_external.string().optional(),
|
|
35964
|
+
revisionId: exports_external.union([exports_external.string().regex(/^[\x21-\x7e]+$/), exports_external.null()]).optional(),
|
|
35965
|
+
lifecycle: exports_external.enum(["active", "archived"]).optional(),
|
|
35723
35966
|
availability: remoteAvailabilitySchema.optional()
|
|
35724
35967
|
}).passthrough().refine((skill) => skill.name || skill.slug, {
|
|
35725
35968
|
message: "Remote skill requires name or slug"
|
|
@@ -35782,6 +36025,8 @@ function normalizeRemoteSkill(skill) {
|
|
|
35782
36025
|
tags: skill.tags || ["remote"],
|
|
35783
36026
|
dependencies: skill.dependencies,
|
|
35784
36027
|
...skill.version ? { version: skill.version } : {},
|
|
36028
|
+
...skill.revisionId !== undefined ? { revisionId: skill.revisionId } : {},
|
|
36029
|
+
...skill.lifecycle ? { lifecycle: skill.lifecycle } : {},
|
|
35785
36030
|
availability: normalizeRemoteAvailability(skill.availability),
|
|
35786
36031
|
source: "remote"
|
|
35787
36032
|
};
|
|
@@ -36374,7 +36619,7 @@ function clone2(value) {
|
|
|
36374
36619
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
36375
36620
|
}
|
|
36376
36621
|
// src/lib/canonical-json.ts
|
|
36377
|
-
import { createHash as
|
|
36622
|
+
import { createHash as createHash16 } from "crypto";
|
|
36378
36623
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
36379
36624
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
36380
36625
|
}
|
|
@@ -36418,7 +36663,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
36418
36663
|
return visit3(value, 0);
|
|
36419
36664
|
}
|
|
36420
36665
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
36421
|
-
return
|
|
36666
|
+
return createHash16("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
36422
36667
|
}
|
|
36423
36668
|
|
|
36424
36669
|
// src/lib/remote-recurring-contract.ts
|
|
@@ -37379,10 +37624,10 @@ async function readQuoteUnavailableCode(response) {
|
|
|
37379
37624
|
let timer;
|
|
37380
37625
|
let deadlineExceeded = false;
|
|
37381
37626
|
const expired = Symbol("quote body deadline");
|
|
37382
|
-
const deadline = new Promise((
|
|
37627
|
+
const deadline = new Promise((resolve16) => {
|
|
37383
37628
|
timer = setTimeout(() => {
|
|
37384
37629
|
deadlineExceeded = true;
|
|
37385
|
-
|
|
37630
|
+
resolve16(expired);
|
|
37386
37631
|
reader.cancel().catch(() => {});
|
|
37387
37632
|
}, 1500);
|
|
37388
37633
|
});
|
|
@@ -37498,6 +37743,18 @@ class RemoteRequestError extends Error {
|
|
|
37498
37743
|
this.name = "RemoteRequestError";
|
|
37499
37744
|
}
|
|
37500
37745
|
}
|
|
37746
|
+
var KNOWN_SKILL_LIFECYCLE_CODES = new Set(["SKILL_ARCHIVE_PROFILE_CONFLICT", "LIFECYCLE_ROLE_REQUIRED"]);
|
|
37747
|
+
|
|
37748
|
+
class RemoteSkillLifecycleError extends RemoteRequestError {
|
|
37749
|
+
code;
|
|
37750
|
+
constructor(path, status, code) {
|
|
37751
|
+
super(path, status);
|
|
37752
|
+
this.name = "RemoteSkillLifecycleError";
|
|
37753
|
+
const safeCode = code !== undefined && KNOWN_SKILL_LIFECYCLE_CODES.has(code) ? code : undefined;
|
|
37754
|
+
this.code = safeCode;
|
|
37755
|
+
this.message = `Skill lifecycle update was refused (HTTP ${status}${safeCode ? `, code ${safeCode}` : ""})`;
|
|
37756
|
+
}
|
|
37757
|
+
}
|
|
37501
37758
|
|
|
37502
37759
|
class RemoteCreditCheckoutError extends RemoteRequestError {
|
|
37503
37760
|
code;
|
|
@@ -37678,6 +37935,39 @@ class RemoteSkillsClient {
|
|
|
37678
37935
|
})();
|
|
37679
37936
|
return this.capabilities;
|
|
37680
37937
|
}
|
|
37938
|
+
async addSkillPublishScope(keyId, expectedScopes, expectedOrgId) {
|
|
37939
|
+
if (!/^[A-Za-z0-9_-]+$/.test(keyId))
|
|
37940
|
+
throw new Error("Invalid API key id");
|
|
37941
|
+
if (!/^[A-Za-z0-9_-]{1,256}$/.test(expectedOrgId))
|
|
37942
|
+
throw new Error("Invalid expected organization id");
|
|
37943
|
+
if (!Array.isArray(expectedScopes) || expectedScopes.length > 32 || expectedScopes.some((scope) => typeof scope !== "string" || scope.length > 128 || !/^(?:\*|[a-z][a-z0-9_-]*:(?:\*|[a-z][a-z0-9_-]*))$/.test(scope)) || new Set(expectedScopes).size !== expectedScopes.length)
|
|
37944
|
+
throw new Error("Invalid expected API key scopes");
|
|
37945
|
+
const path = `/api/v1/admin/keys/${encodeURIComponent(keyId)}/scopes`;
|
|
37946
|
+
const response = await this.request(path, { method: "PATCH", body: JSON.stringify({ expected_scopes: expectedScopes, add_scopes: ["skills:publish"] }) });
|
|
37947
|
+
if (!response.ok)
|
|
37948
|
+
throw new RemoteRequestError(path, response.status);
|
|
37949
|
+
let value;
|
|
37950
|
+
try {
|
|
37951
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readBoundedResponse(response, 64 * 1024)));
|
|
37952
|
+
} catch {
|
|
37953
|
+
throw new Error("Invalid API key scope update response");
|
|
37954
|
+
}
|
|
37955
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
37956
|
+
throw new Error("Invalid API key scope update response");
|
|
37957
|
+
const body = value;
|
|
37958
|
+
const returnedId = body.keyId;
|
|
37959
|
+
const orgId = body.orgId;
|
|
37960
|
+
const scopes = body.scopes;
|
|
37961
|
+
if (returnedId !== keyId || typeof orgId !== "string" || !orgId || expectedOrgId !== undefined && orgId !== expectedOrgId || !Array.isArray(scopes) || scopes.some((scope) => typeof scope !== "string") || body.updated !== true) {
|
|
37962
|
+
throw new Error("Invalid API key scope update response");
|
|
37963
|
+
}
|
|
37964
|
+
const returnedScopes = scopes;
|
|
37965
|
+
const expectedResult = expectedScopes.includes("skills:publish") ? expectedScopes : [...expectedScopes, "skills:publish"];
|
|
37966
|
+
if (returnedScopes.length !== expectedResult.length || expectedResult.some((scope, index) => returnedScopes[index] !== scope)) {
|
|
37967
|
+
throw new Error("API key scope update response did not preserve the expected scopes");
|
|
37968
|
+
}
|
|
37969
|
+
return { keyId, orgId, scopes: [...returnedScopes], updated: true };
|
|
37970
|
+
}
|
|
37681
37971
|
async submitQuotedRun(slug2, input2 = {}, args = [], approval = {}) {
|
|
37682
37972
|
runQuoteReceipt(approval.quoteReceipt);
|
|
37683
37973
|
({ input: input2, args, approval } = JSON.parse(JSON.stringify({ input: input2, args, approval })));
|
|
@@ -38195,6 +38485,19 @@ class RemoteSkillsClient {
|
|
|
38195
38485
|
async deleteSkill(slug2) {
|
|
38196
38486
|
return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}`, { method: "DELETE" });
|
|
38197
38487
|
}
|
|
38488
|
+
async setSkillLifecycle(slug2, lifecycle, options) {
|
|
38489
|
+
const path = `/api/v1/skills/${encodeURIComponent(slug2)}/lifecycle`;
|
|
38490
|
+
const response = await this.request(path, {
|
|
38491
|
+
method: "PATCH",
|
|
38492
|
+
headers: { "If-Match": options.expectedRevisionId },
|
|
38493
|
+
body: JSON.stringify({ lifecycle, ...options.reason ? { reason: options.reason } : {}, ...options.replacementSlug ? { replacementSlug: options.replacementSlug } : {} })
|
|
38494
|
+
});
|
|
38495
|
+
if (!response.ok) {
|
|
38496
|
+
const code = await readResponseCode(response);
|
|
38497
|
+
throw new RemoteSkillLifecycleError(path, response.status, code);
|
|
38498
|
+
}
|
|
38499
|
+
return response.json();
|
|
38500
|
+
}
|
|
38198
38501
|
async downloadSkillBundle(slug2) {
|
|
38199
38502
|
return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}/bundle`, { method: "GET" });
|
|
38200
38503
|
}
|
|
@@ -38347,9 +38650,13 @@ function normalizeSkillSummaryList(payload) {
|
|
|
38347
38650
|
return payload.map(normalizeSkillSummary);
|
|
38348
38651
|
}
|
|
38349
38652
|
async function responseBodyCarriesCode(response, codes) {
|
|
38653
|
+
const code = await readResponseCode(response);
|
|
38654
|
+
return code !== undefined && codes.includes(code);
|
|
38655
|
+
}
|
|
38656
|
+
async function readResponseCode(response) {
|
|
38350
38657
|
const reader = response.body?.getReader();
|
|
38351
38658
|
if (!reader)
|
|
38352
|
-
return
|
|
38659
|
+
return;
|
|
38353
38660
|
const maximum = 8 * 1024;
|
|
38354
38661
|
let deadline;
|
|
38355
38662
|
const expired = new Promise((_, reject) => {
|
|
@@ -38364,7 +38671,7 @@ async function responseBodyCarriesCode(response, codes) {
|
|
|
38364
38671
|
break;
|
|
38365
38672
|
size += next.value.byteLength;
|
|
38366
38673
|
if (size > maximum)
|
|
38367
|
-
return
|
|
38674
|
+
return;
|
|
38368
38675
|
chunks.push(next.value);
|
|
38369
38676
|
}
|
|
38370
38677
|
const bytes = new Uint8Array(size);
|
|
@@ -38375,11 +38682,11 @@ async function responseBodyCarriesCode(response, codes) {
|
|
|
38375
38682
|
}
|
|
38376
38683
|
const payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
38377
38684
|
if (!payload || typeof payload !== "object" || Array.isArray(payload) || !Object.hasOwn(payload, "code"))
|
|
38378
|
-
return
|
|
38685
|
+
return;
|
|
38379
38686
|
const code = payload.code;
|
|
38380
|
-
return typeof code === "string" &&
|
|
38687
|
+
return typeof code === "string" && /^[A-Z][A-Z0-9_:-]{0,127}$/.test(code) ? code : undefined;
|
|
38381
38688
|
} catch {
|
|
38382
|
-
return
|
|
38689
|
+
return;
|
|
38383
38690
|
} finally {
|
|
38384
38691
|
clearTimeout(deadline);
|
|
38385
38692
|
reader.cancel().catch(() => {});
|
|
@@ -38610,10 +38917,10 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
38610
38917
|
saveSchedules(data, targetDir);
|
|
38611
38918
|
}
|
|
38612
38919
|
// src/lib/revision.ts
|
|
38613
|
-
import { createHash as
|
|
38920
|
+
import { createHash as createHash17 } from "crypto";
|
|
38614
38921
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
38615
38922
|
function revisionIdOf(content) {
|
|
38616
|
-
const
|
|
38923
|
+
const canonicalContent = {
|
|
38617
38924
|
slug: content.slug,
|
|
38618
38925
|
displayName: content.displayName,
|
|
38619
38926
|
description: content.description,
|
|
@@ -38625,15 +38932,21 @@ function revisionIdOf(content) {
|
|
|
38625
38932
|
skillMd: content.skillMd ?? null,
|
|
38626
38933
|
bundleSha256: content.bundleSha256 ?? null,
|
|
38627
38934
|
bundleByteSize: content.bundleByteSize ?? null
|
|
38628
|
-
}
|
|
38629
|
-
|
|
38935
|
+
};
|
|
38936
|
+
if (content.lifecycle === "archived" || content.archiveReason !== undefined || content.replacementSlug !== undefined) {
|
|
38937
|
+
canonicalContent.lifecycle = content.lifecycle ?? "active";
|
|
38938
|
+
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
38939
|
+
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
38940
|
+
}
|
|
38941
|
+
const canonical4 = JSON.stringify(canonicalContent);
|
|
38942
|
+
return createHash17("sha256").update(canonical4).digest("hex");
|
|
38630
38943
|
}
|
|
38631
38944
|
function revisionIdOfRecord(record7) {
|
|
38632
38945
|
return revisionIdOf(record7);
|
|
38633
38946
|
}
|
|
38634
38947
|
// src/lib/pull.ts
|
|
38635
38948
|
import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync20, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync10 } from "fs";
|
|
38636
|
-
import { dirname as
|
|
38949
|
+
import { dirname as dirname17, join as join31 } from "path";
|
|
38637
38950
|
|
|
38638
38951
|
// src/lib/skill-bundles.ts
|
|
38639
38952
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -38975,7 +39288,7 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
38975
39288
|
try {
|
|
38976
39289
|
for (const entry of entries) {
|
|
38977
39290
|
const destination = join31(staging, entry.path);
|
|
38978
|
-
mkdirSync12(
|
|
39291
|
+
mkdirSync12(dirname17(destination), { recursive: true });
|
|
38979
39292
|
writeFileSync10(destination, entry.bytes, { mode: entry.mode });
|
|
38980
39293
|
}
|
|
38981
39294
|
writePullMarker(staging, {
|
|
@@ -39069,7 +39382,7 @@ function splitNameVersion(raw) {
|
|
|
39069
39382
|
return version2 ? { name, version: version2 } : { name };
|
|
39070
39383
|
}
|
|
39071
39384
|
// src/lib/recurring-surface.ts
|
|
39072
|
-
import { isAbsolute as
|
|
39385
|
+
import { isAbsolute as isAbsolute16 } from "path";
|
|
39073
39386
|
|
|
39074
39387
|
// src/lib/remote-invitation-recovery.ts
|
|
39075
39388
|
class InvitationEmailInputError extends Error {
|
|
@@ -39167,7 +39480,7 @@ async function requestInvitationEmail(origin, action, input2) {
|
|
|
39167
39480
|
}
|
|
39168
39481
|
|
|
39169
39482
|
// src/lib/remote-private-publications.ts
|
|
39170
|
-
import { createHash as
|
|
39483
|
+
import { createHash as createHash18 } from "crypto";
|
|
39171
39484
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
39172
39485
|
var failures2 = {
|
|
39173
39486
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -39214,7 +39527,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
|
39214
39527
|
var record8 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
39215
39528
|
var exact = (v, keys) => record8(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
|
|
39216
39529
|
var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
|
|
39217
|
-
var publicationSha256 = (bytes) =>
|
|
39530
|
+
var publicationSha256 = (bytes) => createHash18("sha256").update(bytes).digest("hex");
|
|
39218
39531
|
function checkedPublicationDeclaration(value) {
|
|
39219
39532
|
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)
|
|
39220
39533
|
return bad();
|
|
@@ -39459,12 +39772,12 @@ class RemotePrivatePublicationsClient {
|
|
|
39459
39772
|
previous = view;
|
|
39460
39773
|
if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
|
|
39461
39774
|
return view;
|
|
39462
|
-
await new Promise((
|
|
39775
|
+
await new Promise((resolve16) => {
|
|
39463
39776
|
const timer = setTimeout(done, Math.min(1000, until - Date.now()));
|
|
39464
39777
|
function done() {
|
|
39465
39778
|
clearTimeout(timer);
|
|
39466
39779
|
options.signal?.removeEventListener("abort", done);
|
|
39467
|
-
|
|
39780
|
+
resolve16();
|
|
39468
39781
|
}
|
|
39469
39782
|
options.signal?.addEventListener("abort", done, { once: true });
|
|
39470
39783
|
});
|
|
@@ -39728,7 +40041,7 @@ var stateSchema = exports_external.object({
|
|
|
39728
40041
|
var uuid13 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
39729
40042
|
var context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
|
|
39730
40043
|
var page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
|
|
39731
|
-
var directory = exports_external.string().max(4096).refine(
|
|
40044
|
+
var directory = exports_external.string().max(4096).refine(isAbsolute16, "Use an absolute recovery directory");
|
|
39732
40045
|
var email4 = exports_external.string().trim().toLowerCase().email().max(254);
|
|
39733
40046
|
var code = exports_external.string().regex(/^\d{6}$/);
|
|
39734
40047
|
var recurringSurfaceSchemas = {
|
|
@@ -39879,7 +40192,7 @@ function findSkillsParityForMcpTool(tool) {
|
|
|
39879
40192
|
}
|
|
39880
40193
|
// src/lib/registry-sync.ts
|
|
39881
40194
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
39882
|
-
import { dirname as
|
|
40195
|
+
import { dirname as dirname18, relative as relative7 } from "path";
|
|
39883
40196
|
function createRegistrySyncArtifact(options = {}) {
|
|
39884
40197
|
const profile = options.profile ?? "all";
|
|
39885
40198
|
const includeDocs = options.includeDocs ?? true;
|
|
@@ -39936,7 +40249,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
39936
40249
|
};
|
|
39937
40250
|
}
|
|
39938
40251
|
function writeRegistrySyncArtifact(path, artifact) {
|
|
39939
|
-
mkdirSync13(
|
|
40252
|
+
mkdirSync13(dirname18(path), { recursive: true });
|
|
39940
40253
|
writeFileSync11(path, `${JSON.stringify(artifact, null, 2)}
|
|
39941
40254
|
`);
|
|
39942
40255
|
}
|
|
@@ -41021,14 +41334,14 @@ function clone3(value) {
|
|
|
41021
41334
|
}
|
|
41022
41335
|
// src/lib/feedback.ts
|
|
41023
41336
|
import { appendFileSync, existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
|
|
41024
|
-
import { dirname as
|
|
41337
|
+
import { dirname as dirname19, join as join32 } from "path";
|
|
41025
41338
|
import { Database } from "bun:sqlite";
|
|
41026
41339
|
function getFeedbackDbPath() {
|
|
41027
41340
|
return join32(getDataDir(), "skills.db");
|
|
41028
41341
|
}
|
|
41029
41342
|
function getFeedbackDb() {
|
|
41030
41343
|
const dbPath = getFeedbackDbPath();
|
|
41031
|
-
const dir =
|
|
41344
|
+
const dir = dirname19(dbPath);
|
|
41032
41345
|
if (!existsSync18(dir))
|
|
41033
41346
|
mkdirSync14(dir, { recursive: true });
|
|
41034
41347
|
const db = new Database(dbPath);
|
|
@@ -41057,7 +41370,7 @@ function saveFeedback(input2) {
|
|
|
41057
41370
|
const category = input2.category ?? "general";
|
|
41058
41371
|
if (isApiMode()) {
|
|
41059
41372
|
const path = join32(getDataDir(), "feedback.jsonl");
|
|
41060
|
-
const dir =
|
|
41373
|
+
const dir = dirname19(path);
|
|
41061
41374
|
if (!existsSync18(dir))
|
|
41062
41375
|
mkdirSync14(dir, { recursive: true });
|
|
41063
41376
|
appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
@@ -41080,7 +41393,7 @@ function isApiMode(env = process.env) {
|
|
|
41080
41393
|
}
|
|
41081
41394
|
}
|
|
41082
41395
|
// src/lib/native-storage.ts
|
|
41083
|
-
import { createHash as
|
|
41396
|
+
import { createHash as createHash19, createHmac as createHmac2 } from "crypto";
|
|
41084
41397
|
import {
|
|
41085
41398
|
existsSync as existsSync19,
|
|
41086
41399
|
mkdirSync as mkdirSync15,
|
|
@@ -41089,7 +41402,7 @@ import {
|
|
|
41089
41402
|
statSync as statSync11,
|
|
41090
41403
|
writeFileSync as writeFileSync12
|
|
41091
41404
|
} from "fs";
|
|
41092
|
-
import { dirname as
|
|
41405
|
+
import { dirname as dirname20, join as join33, normalize as normalize3, relative as relative8, sep as sep7 } from "path";
|
|
41093
41406
|
var SKILLS_STORAGE_TABLES = [
|
|
41094
41407
|
"skills_sync_records",
|
|
41095
41408
|
"skills_sync_cursors"
|
|
@@ -41256,7 +41569,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
41256
41569
|
files.push({
|
|
41257
41570
|
path: relativePath,
|
|
41258
41571
|
sizeBytes: bytes.byteLength,
|
|
41259
|
-
sha256:
|
|
41572
|
+
sha256: createHash19("sha256").update(bytes).digest("hex"),
|
|
41260
41573
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
41261
41574
|
});
|
|
41262
41575
|
}
|
|
@@ -41275,18 +41588,18 @@ function importSkillsLocalSnapshot(snapshot, targetDir = process.cwd(), options
|
|
|
41275
41588
|
skipped += 1;
|
|
41276
41589
|
continue;
|
|
41277
41590
|
}
|
|
41278
|
-
const
|
|
41279
|
-
if (existsSync19(
|
|
41591
|
+
const absolutePath3 = resolveSnapshotPath(targetDir, file2.path);
|
|
41592
|
+
if (existsSync19(absolutePath3) && !options.overwrite) {
|
|
41280
41593
|
skipped += 1;
|
|
41281
41594
|
continue;
|
|
41282
41595
|
}
|
|
41283
41596
|
const bytes = Buffer.from(file2.contentBase64, "base64");
|
|
41284
|
-
const hash4 =
|
|
41597
|
+
const hash4 = createHash19("sha256").update(bytes).digest("hex");
|
|
41285
41598
|
if (hash4 !== file2.sha256) {
|
|
41286
41599
|
throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
|
|
41287
41600
|
}
|
|
41288
|
-
mkdirSync15(
|
|
41289
|
-
writeFileSync12(
|
|
41601
|
+
mkdirSync15(dirname20(absolutePath3), { recursive: true });
|
|
41602
|
+
writeFileSync12(absolutePath3, bytes);
|
|
41290
41603
|
written += 1;
|
|
41291
41604
|
}
|
|
41292
41605
|
return { written, skipped };
|
|
@@ -41613,7 +41926,7 @@ function toIsoString(value) {
|
|
|
41613
41926
|
return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
|
|
41614
41927
|
}
|
|
41615
41928
|
function sha256Hex2(value) {
|
|
41616
|
-
return
|
|
41929
|
+
return createHash19("sha256").update(value).digest("hex");
|
|
41617
41930
|
}
|
|
41618
41931
|
function normalizeHeaders(headers) {
|
|
41619
41932
|
const result = {};
|
|
@@ -41665,7 +41978,7 @@ function toArrayBuffer(bytes) {
|
|
|
41665
41978
|
return buffer;
|
|
41666
41979
|
}
|
|
41667
41980
|
// src/lib/station-snapshot.ts
|
|
41668
|
-
import { createHash as
|
|
41981
|
+
import { createHash as createHash20 } from "crypto";
|
|
41669
41982
|
import {
|
|
41670
41983
|
copyFileSync,
|
|
41671
41984
|
mkdirSync as mkdirSync16,
|
|
@@ -41673,7 +41986,7 @@ import {
|
|
|
41673
41986
|
statSync as statSync13,
|
|
41674
41987
|
writeFileSync as writeFileSync13
|
|
41675
41988
|
} from "fs";
|
|
41676
|
-
import { dirname as
|
|
41989
|
+
import { dirname as dirname21, isAbsolute as isAbsolute17, relative as relative9, resolve as resolve16, sep as sep9 } from "path";
|
|
41677
41990
|
|
|
41678
41991
|
// src/lib/portable-snapshot-filter.ts
|
|
41679
41992
|
import { readdirSync as readdirSync14, statSync as statSync12 } from "fs";
|
|
@@ -41840,7 +42153,7 @@ function validateStationId(stationId) {
|
|
|
41840
42153
|
}
|
|
41841
42154
|
}
|
|
41842
42155
|
function sha256File(filePath) {
|
|
41843
|
-
return
|
|
42156
|
+
return createHash20("sha256").update(readFileSync23(filePath)).digest("hex");
|
|
41844
42157
|
}
|
|
41845
42158
|
function scanHome(definition, homesRoot) {
|
|
41846
42159
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -41911,7 +42224,7 @@ function humanHomes(scanned) {
|
|
|
41911
42224
|
}));
|
|
41912
42225
|
}
|
|
41913
42226
|
function writeStationSnapshot(options) {
|
|
41914
|
-
const repoRoot =
|
|
42227
|
+
const repoRoot = resolve16(options.repoRoot ?? process.cwd());
|
|
41915
42228
|
const { scanned, plans, totalBytes } = planStationSnapshot(options);
|
|
41916
42229
|
const manifestFiles = plans.map((plan) => ({
|
|
41917
42230
|
relativePath: plan.source.relativePath,
|
|
@@ -41937,9 +42250,9 @@ function writeStationSnapshot(options) {
|
|
|
41937
42250
|
const conflicts = [];
|
|
41938
42251
|
const untouched = [];
|
|
41939
42252
|
for (const plan of plans) {
|
|
41940
|
-
const destination =
|
|
42253
|
+
const destination = resolve16(repoRoot, plan.destination);
|
|
41941
42254
|
const destinationRelative = relative9(repoRoot, destination);
|
|
41942
|
-
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep9) ||
|
|
42255
|
+
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep9) || isAbsolute17(destinationRelative)) {
|
|
41943
42256
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
41944
42257
|
}
|
|
41945
42258
|
let existingDigest = null;
|
|
@@ -41960,8 +42273,8 @@ function writeStationSnapshot(options) {
|
|
|
41960
42273
|
}
|
|
41961
42274
|
let written = 0;
|
|
41962
42275
|
for (const plan of untouched) {
|
|
41963
|
-
const destination =
|
|
41964
|
-
mkdirSync16(
|
|
42276
|
+
const destination = resolve16(repoRoot, plan.destination);
|
|
42277
|
+
mkdirSync16(dirname21(destination), { recursive: true });
|
|
41965
42278
|
copyFileSync(plan.source.fullPath, destination);
|
|
41966
42279
|
written += 1;
|
|
41967
42280
|
}
|
|
@@ -41979,8 +42292,8 @@ function writeStationSnapshot(options) {
|
|
|
41979
42292
|
},
|
|
41980
42293
|
files: manifestFiles
|
|
41981
42294
|
};
|
|
41982
|
-
const manifestPath =
|
|
41983
|
-
mkdirSync16(
|
|
42295
|
+
const manifestPath = resolve16(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
|
|
42296
|
+
mkdirSync16(dirname21(manifestPath), { recursive: true });
|
|
41984
42297
|
writeFileSync13(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
41985
42298
|
`);
|
|
41986
42299
|
return {
|
|
@@ -41991,7 +42304,7 @@ function writeStationSnapshot(options) {
|
|
|
41991
42304
|
};
|
|
41992
42305
|
}
|
|
41993
42306
|
// src/lib/station-hydrate.ts
|
|
41994
|
-
import { createHash as
|
|
42307
|
+
import { createHash as createHash21 } from "crypto";
|
|
41995
42308
|
import {
|
|
41996
42309
|
copyFileSync as copyFileSync2,
|
|
41997
42310
|
mkdirSync as mkdirSync17,
|
|
@@ -42000,7 +42313,7 @@ import {
|
|
|
42000
42313
|
statSync as statSync14,
|
|
42001
42314
|
writeFileSync as writeFileSync14
|
|
42002
42315
|
} from "fs";
|
|
42003
|
-
import { dirname as
|
|
42316
|
+
import { dirname as dirname22, join as join35, resolve as resolve17, sep as sep10 } from "path";
|
|
42004
42317
|
var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
|
|
42005
42318
|
var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
|
|
42006
42319
|
var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
|
|
@@ -42197,12 +42510,12 @@ function skillSha256(skill) {
|
|
|
42197
42510
|
return sha256File(skill.files[0].winner.fullPath);
|
|
42198
42511
|
}
|
|
42199
42512
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
42200
|
-
return
|
|
42513
|
+
return createHash21("sha256").update(joined.sort().join(`
|
|
42201
42514
|
`)).digest("hex");
|
|
42202
42515
|
}
|
|
42203
42516
|
function writeStationHydration(options) {
|
|
42204
|
-
const repoRoot =
|
|
42205
|
-
const cacheRoot =
|
|
42517
|
+
const repoRoot = resolve17(options.repoRoot ?? process.cwd());
|
|
42518
|
+
const cacheRoot = resolve17(options.cacheRoot ?? resolveCorpusRoot());
|
|
42206
42519
|
const plan = planStationHydration(options.stationId, repoRoot);
|
|
42207
42520
|
const resultSkills = plan.winners.map((skill) => ({
|
|
42208
42521
|
ident: skill.ident,
|
|
@@ -42256,7 +42569,7 @@ function writeStationHydration(options) {
|
|
|
42256
42569
|
}
|
|
42257
42570
|
let written = 0;
|
|
42258
42571
|
for (const entry of toWrite) {
|
|
42259
|
-
mkdirSync17(
|
|
42572
|
+
mkdirSync17(dirname22(entry.destination), { recursive: true });
|
|
42260
42573
|
copyFileSync2(entry.fullPath, entry.destination);
|
|
42261
42574
|
written += 1;
|
|
42262
42575
|
}
|
|
@@ -42277,8 +42590,8 @@ function writeStationHydration(options) {
|
|
|
42277
42590
|
},
|
|
42278
42591
|
skills: resultSkills
|
|
42279
42592
|
};
|
|
42280
|
-
const hydrationManifestPath = join35(
|
|
42281
|
-
mkdirSync17(
|
|
42593
|
+
const hydrationManifestPath = join35(dirname22(cacheRoot), `hydration-${options.stationId}.json`);
|
|
42594
|
+
mkdirSync17(dirname22(hydrationManifestPath), { recursive: true });
|
|
42282
42595
|
writeFileSync14(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
42283
42596
|
`);
|
|
42284
42597
|
return {
|
|
@@ -42289,23 +42602,23 @@ function writeStationHydration(options) {
|
|
|
42289
42602
|
};
|
|
42290
42603
|
}
|
|
42291
42604
|
// src/lib/private-publication-recovery.ts
|
|
42292
|
-
import { constants as
|
|
42293
|
-
import { dirname as
|
|
42605
|
+
import { constants as constants11, closeSync as closeSync12, fsyncSync as fsyncSync4, fstatSync as fstatSync12, lstatSync as lstatSync20, mkdirSync as mkdirSync18, openSync as openSync12, readSync as readSync10, realpathSync as realpathSync6, renameSync as renameSync7, unlinkSync as unlinkSync5, writeFileSync as writeFileSync15 } from "fs";
|
|
42606
|
+
import { dirname as dirname23, isAbsolute as isAbsolute18, join as join36, resolve as resolve18 } from "path";
|
|
42294
42607
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
42295
42608
|
var fail2 = () => {
|
|
42296
42609
|
throw new PrivatePublicationError("PUBLICATION_RECOVERY_INVALID", "The recovery directory is invalid or changed. Preserve it and inspect the existing intent; do not start a replacement automatically.");
|
|
42297
42610
|
};
|
|
42298
42611
|
function safeDirectory(directory2) {
|
|
42299
|
-
if (!
|
|
42612
|
+
if (!isAbsolute18(directory2) || directory2 !== resolve18(directory2) || realpathSync6(directory2) !== directory2)
|
|
42300
42613
|
return fail2();
|
|
42301
|
-
for (let path = directory2;; path =
|
|
42302
|
-
const stat2 =
|
|
42614
|
+
for (let path = directory2;; path = dirname23(path)) {
|
|
42615
|
+
const stat2 = lstatSync20(path);
|
|
42303
42616
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
42304
42617
|
return fail2();
|
|
42305
|
-
if (path ===
|
|
42618
|
+
if (path === dirname23(path))
|
|
42306
42619
|
break;
|
|
42307
42620
|
}
|
|
42308
|
-
const own2 =
|
|
42621
|
+
const own2 = lstatSync20(directory2);
|
|
42309
42622
|
if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
|
|
42310
42623
|
return fail2();
|
|
42311
42624
|
return { dev: own2.dev, ino: own2.ino };
|
|
@@ -42317,35 +42630,35 @@ function unchangedDirectory(directory2, identity3) {
|
|
|
42317
42630
|
}
|
|
42318
42631
|
function readOwned(directory2, name, max) {
|
|
42319
42632
|
const identity3 = safeDirectory(directory2), file2 = join36(directory2, name);
|
|
42320
|
-
const fd =
|
|
42633
|
+
const fd = openSync12(file2, constants11.O_RDONLY | constants11.O_NOFOLLOW);
|
|
42321
42634
|
try {
|
|
42322
|
-
const stat2 =
|
|
42635
|
+
const stat2 = fstatSync12(fd);
|
|
42323
42636
|
if (!stat2.isFile() || stat2.nlink !== 1 || stat2.size > max || stat2.size < 1 || (stat2.mode & 63) !== 0 || process.getuid && stat2.uid !== process.getuid())
|
|
42324
42637
|
return fail2();
|
|
42325
42638
|
const buffer = Buffer.alloc(Math.min(max, stat2.size) + 1);
|
|
42326
42639
|
let length = 0;
|
|
42327
42640
|
while (length < buffer.length) {
|
|
42328
|
-
const count =
|
|
42641
|
+
const count = readSync10(fd, buffer, length, buffer.length - length, length);
|
|
42329
42642
|
if (count === 0)
|
|
42330
42643
|
break;
|
|
42331
42644
|
length += count;
|
|
42332
42645
|
}
|
|
42333
|
-
const bytes = buffer.subarray(0, length), after =
|
|
42646
|
+
const bytes = buffer.subarray(0, length), after = fstatSync12(fd);
|
|
42334
42647
|
unchangedDirectory(directory2, identity3);
|
|
42335
42648
|
if (bytes.length !== stat2.size || stat2.size !== after.size || stat2.mtimeMs !== after.mtimeMs || stat2.ctimeMs !== after.ctimeMs)
|
|
42336
42649
|
return fail2();
|
|
42337
42650
|
return bytes;
|
|
42338
42651
|
} finally {
|
|
42339
|
-
|
|
42652
|
+
closeSync12(fd);
|
|
42340
42653
|
}
|
|
42341
42654
|
}
|
|
42342
42655
|
function writeOwned(directory2, name, bytes) {
|
|
42343
|
-
const fd =
|
|
42656
|
+
const fd = openSync12(join36(directory2, name), constants11.O_WRONLY | constants11.O_CREAT | constants11.O_EXCL | constants11.O_NOFOLLOW, 384);
|
|
42344
42657
|
try {
|
|
42345
42658
|
writeFileSync15(fd, bytes);
|
|
42346
42659
|
fsyncSync4(fd);
|
|
42347
42660
|
} finally {
|
|
42348
|
-
|
|
42661
|
+
closeSync12(fd);
|
|
42349
42662
|
}
|
|
42350
42663
|
}
|
|
42351
42664
|
function save(directory2, value) {
|
|
@@ -42354,11 +42667,11 @@ function save(directory2, value) {
|
|
|
42354
42667
|
`);
|
|
42355
42668
|
unchangedDirectory(directory2, identity3);
|
|
42356
42669
|
renameSync7(join36(directory2, temporary), join36(directory2, "receipt.json"));
|
|
42357
|
-
const fd =
|
|
42670
|
+
const fd = openSync12(directory2, constants11.O_RDONLY | constants11.O_NOFOLLOW);
|
|
42358
42671
|
try {
|
|
42359
42672
|
fsyncSync4(fd);
|
|
42360
42673
|
} finally {
|
|
42361
|
-
|
|
42674
|
+
closeSync12(fd);
|
|
42362
42675
|
}
|
|
42363
42676
|
}
|
|
42364
42677
|
function bind(client2, receipt) {
|
|
@@ -42369,20 +42682,20 @@ async function locked(directory2, action) {
|
|
|
42369
42682
|
const identity3 = safeDirectory(directory2), file2 = join36(directory2, "operation.lock");
|
|
42370
42683
|
let fd;
|
|
42371
42684
|
try {
|
|
42372
|
-
fd =
|
|
42685
|
+
fd = openSync12(file2, constants11.O_WRONLY | constants11.O_CREAT | constants11.O_EXCL | constants11.O_NOFOLLOW, 384);
|
|
42373
42686
|
} catch {
|
|
42374
42687
|
throw new PrivatePublicationError("PUBLICATION_RECOVERY_BUSY", "Another operation holds this recovery directory. If it crashed, confirm that process has stopped before explicitly removing operation.lock and resuming.");
|
|
42375
42688
|
}
|
|
42376
|
-
const lock =
|
|
42689
|
+
const lock = fstatSync12(fd);
|
|
42377
42690
|
try {
|
|
42378
42691
|
writeFileSync15(fd, JSON.stringify({ pid: process.pid }) + `
|
|
42379
42692
|
`);
|
|
42380
42693
|
fsyncSync4(fd);
|
|
42381
42694
|
return await action();
|
|
42382
42695
|
} finally {
|
|
42383
|
-
|
|
42696
|
+
closeSync12(fd);
|
|
42384
42697
|
unchangedDirectory(directory2, identity3);
|
|
42385
|
-
const now =
|
|
42698
|
+
const now = lstatSync20(file2);
|
|
42386
42699
|
if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
|
|
42387
42700
|
fail2();
|
|
42388
42701
|
unlinkSync5(file2);
|
|
@@ -42434,15 +42747,15 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
|
|
|
42434
42747
|
phase: "prepared",
|
|
42435
42748
|
intent: null
|
|
42436
42749
|
};
|
|
42437
|
-
if (!
|
|
42750
|
+
if (!isAbsolute18(recoveryDirectory) || resolve18(recoveryDirectory) !== recoveryDirectory || realpathSync6(dirname23(recoveryDirectory)) !== dirname23(recoveryDirectory))
|
|
42438
42751
|
return fail2();
|
|
42439
42752
|
mkdirSync18(recoveryDirectory, { mode: 448 });
|
|
42440
42753
|
safeDirectory(recoveryDirectory);
|
|
42441
|
-
const parent =
|
|
42754
|
+
const parent = openSync12(dirname23(recoveryDirectory), constants11.O_RDONLY | constants11.O_NOFOLLOW);
|
|
42442
42755
|
try {
|
|
42443
42756
|
fsyncSync4(parent);
|
|
42444
42757
|
} finally {
|
|
42445
|
-
|
|
42758
|
+
closeSync12(parent);
|
|
42446
42759
|
}
|
|
42447
42760
|
writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
|
|
42448
42761
|
save(recoveryDirectory, receipt);
|
|
@@ -42525,8 +42838,8 @@ async function inspectLocked(client2, directory2, cancel) {
|
|
|
42525
42838
|
return privatePublicationResult(directory2, receipt);
|
|
42526
42839
|
}
|
|
42527
42840
|
// src/lib/skill-context.ts
|
|
42528
|
-
import { createHash as
|
|
42529
|
-
import { resolve as
|
|
42841
|
+
import { createHash as createHash22 } from "crypto";
|
|
42842
|
+
import { resolve as resolve19 } from "path";
|
|
42530
42843
|
function words(value) {
|
|
42531
42844
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
42532
42845
|
}
|
|
@@ -42561,7 +42874,7 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
42561
42874
|
const promptWords = words(prompt);
|
|
42562
42875
|
const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
|
|
42563
42876
|
const loaded = new Set(resolved.session?.loaded ?? []);
|
|
42564
|
-
const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd,
|
|
42877
|
+
const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve19(input2.cwd)] : []];
|
|
42565
42878
|
const candidates = profile.selections.map((selection) => {
|
|
42566
42879
|
const key = selectionKey(selection);
|
|
42567
42880
|
if (input2.restore && loaded.has(key))
|
|
@@ -42614,7 +42927,7 @@ ${content}`;
|
|
|
42614
42927
|
`);
|
|
42615
42928
|
const receipt = {
|
|
42616
42929
|
schemaVersion: 1,
|
|
42617
|
-
id:
|
|
42930
|
+
id: createHash22("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
42618
42931
|
profileId: profile.profileId,
|
|
42619
42932
|
profileRevision: profile.profileRevision,
|
|
42620
42933
|
authority: profile.authority,
|
|
@@ -42630,9 +42943,9 @@ ${content}`;
|
|
|
42630
42943
|
return { context: context2, selections, omitted, receipt };
|
|
42631
42944
|
}
|
|
42632
42945
|
// src/lib/session-reconciliation.ts
|
|
42633
|
-
import { createHash as
|
|
42946
|
+
import { createHash as createHash23 } from "crypto";
|
|
42634
42947
|
var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
|
|
42635
|
-
var digest3 = (value) =>
|
|
42948
|
+
var digest3 = (value) => createHash23("sha256").update(JSON.stringify(value)).digest("hex");
|
|
42636
42949
|
function exactTimestamp(value) {
|
|
42637
42950
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
42638
42951
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -42921,6 +43234,7 @@ export {
|
|
|
42921
43234
|
captureDiscoveryPathSources,
|
|
42922
43235
|
captureDiscoveryDirectories,
|
|
42923
43236
|
captureDiscoveryByteSources,
|
|
43237
|
+
captureClaudeSettings,
|
|
42924
43238
|
captureClaudeMarketplaceRegistry,
|
|
42925
43239
|
canonicalizeManifest,
|
|
42926
43240
|
buildSkillsS3ObjectUrl,
|