@hasna/skills 0.7.1 → 0.7.2
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 +51 -0
- package/bin/index.js +809 -592
- package/bin/mcp.js +23 -2
- package/bin/migrate.js +1 -1
- package/bin/server.js +2 -3
- package/bin/worker.js +2 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +556 -361
- package/dist/lib/agent-discovery-bytes.d.ts +6 -0
- package/dist/lib/agent-discovery-directories.d.ts +8 -0
- package/dist/lib/agent-discovery.d.ts +7 -0
- package/dist/lib/agent-integration.d.ts +2 -0
- package/dist/lib/agent-policy-limits.d.ts +5 -0
- package/dist/sdk/index.js +579 -386
- package/package.json +1 -1
package/dist/sdk/index.js
CHANGED
|
@@ -30042,10 +30042,10 @@ var require_public_api = __commonJS((exports) => {
|
|
|
30042
30042
|
import { chmod, mkdir, readFile, rename, writeFile as writeFile2 } from "fs/promises";
|
|
30043
30043
|
import { Buffer as Buffer2 } from "buffer";
|
|
30044
30044
|
import { existsSync as existsSync22 } from "fs";
|
|
30045
|
-
import { join as
|
|
30045
|
+
import { join as join28 } from "path";
|
|
30046
30046
|
import { existsSync as existsSync18 } from "fs";
|
|
30047
30047
|
import { homedir as homedir6 } from "os";
|
|
30048
|
-
import { join as
|
|
30048
|
+
import { join as join29, resolve as resolve10 } from "path";
|
|
30049
30049
|
import { homedir as pathsResolverHomedir2 } from "os";
|
|
30050
30050
|
import { join as pathsResolverJoin2 } from "path";
|
|
30051
30051
|
import { createHmac as createHmac4, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
@@ -30207,7 +30207,7 @@ function effectiveHome2() {
|
|
|
30207
30207
|
return process.env["HOME"] || process.env["USERPROFILE"] || homedir6();
|
|
30208
30208
|
}
|
|
30209
30209
|
function legacyHomeDir() {
|
|
30210
|
-
return
|
|
30210
|
+
return join29(effectiveHome2(), ".hasna", "events");
|
|
30211
30211
|
}
|
|
30212
30212
|
function resolverHome() {
|
|
30213
30213
|
return dataDir2({ app: "events", home: effectiveHome2() || undefined });
|
|
@@ -30216,7 +30216,7 @@ function adoptResolverHome(resolved, env = process.env) {
|
|
|
30216
30216
|
const dataOverride = env.HASNA_DATA_HOME;
|
|
30217
30217
|
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
30218
30218
|
return true;
|
|
30219
|
-
return existsSync18(
|
|
30219
|
+
return existsSync18(join29(resolved, EVENTS_STORE_SENTINEL_FILE));
|
|
30220
30220
|
}
|
|
30221
30221
|
function exactEventsHome() {
|
|
30222
30222
|
const dir = process.env[HASNA_EVENTS_DIR_ENV];
|
|
@@ -30230,9 +30230,9 @@ function exactEventsHome() {
|
|
|
30230
30230
|
function getEventsHome() {
|
|
30231
30231
|
const exact = exactEventsHome();
|
|
30232
30232
|
if (exact)
|
|
30233
|
-
return
|
|
30233
|
+
return resolve10(exact);
|
|
30234
30234
|
const resolved = resolverHome();
|
|
30235
|
-
return adoptResolverHome(resolved) ?
|
|
30235
|
+
return adoptResolverHome(resolved) ? resolve10(resolved) : resolve10(legacyHomeDir());
|
|
30236
30236
|
}
|
|
30237
30237
|
function getEventsDataDir(override) {
|
|
30238
30238
|
return override || getEventsHome();
|
|
@@ -30247,9 +30247,9 @@ class JsonEventsStore {
|
|
|
30247
30247
|
constructor(dataDir22 = getEventsDataDir()) {
|
|
30248
30248
|
this.dataDir = dataDir22;
|
|
30249
30249
|
this.runtime = localJsonRuntime(dataDir22);
|
|
30250
|
-
this.channelsPath =
|
|
30251
|
-
this.eventsPath =
|
|
30252
|
-
this.deliveriesPath =
|
|
30250
|
+
this.channelsPath = join28(dataDir22, "channels.json");
|
|
30251
|
+
this.eventsPath = join28(dataDir22, "events.json");
|
|
30252
|
+
this.deliveriesPath = join28(dataDir22, "deliveries.json");
|
|
30253
30253
|
}
|
|
30254
30254
|
async init() {
|
|
30255
30255
|
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
@@ -31368,7 +31368,7 @@ var init_event_sink = __esm(() => {
|
|
|
31368
31368
|
// package.json
|
|
31369
31369
|
var package_default = {
|
|
31370
31370
|
name: "@hasna/skills",
|
|
31371
|
-
version: "0.7.
|
|
31371
|
+
version: "0.7.2",
|
|
31372
31372
|
description: "Skills library for AI coding agents",
|
|
31373
31373
|
type: "module",
|
|
31374
31374
|
bin: {
|
|
@@ -45190,7 +45190,7 @@ import { existsSync as existsSync17 } from "fs";
|
|
|
45190
45190
|
|
|
45191
45191
|
// src/lib/installer.ts
|
|
45192
45192
|
import { existsSync as existsSync14, readFileSync as readFileSync17, rmSync as rmSync3 } from "fs";
|
|
45193
|
-
import { dirname as
|
|
45193
|
+
import { dirname as dirname15, join as join24 } from "path";
|
|
45194
45194
|
import { homedir as homedir5 } from "os";
|
|
45195
45195
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
45196
45196
|
|
|
@@ -45198,7 +45198,7 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
45198
45198
|
import {
|
|
45199
45199
|
cpSync as cpSync3,
|
|
45200
45200
|
existsSync as existsSync11,
|
|
45201
|
-
lstatSync as
|
|
45201
|
+
lstatSync as lstatSync12,
|
|
45202
45202
|
mkdirSync as mkdirSync8,
|
|
45203
45203
|
mkdtempSync as mkdtempSync2,
|
|
45204
45204
|
readFileSync as readFileSync14,
|
|
@@ -45209,12 +45209,12 @@ import {
|
|
|
45209
45209
|
writeFileSync as writeFileSync6
|
|
45210
45210
|
} from "fs";
|
|
45211
45211
|
import { homedir as homedir4 } from "os";
|
|
45212
|
-
import { basename as basename3, dirname as
|
|
45212
|
+
import { basename as basename3, dirname as dirname14, join as join21 } from "path";
|
|
45213
45213
|
|
|
45214
45214
|
// src/lib/agent-integration.ts
|
|
45215
|
-
import { createHash as
|
|
45216
|
-
import { existsSync as existsSync5, lstatSync as
|
|
45217
|
-
import { dirname as
|
|
45215
|
+
import { createHash as createHash12, randomUUID as randomUUID6 } from "crypto";
|
|
45216
|
+
import { existsSync as existsSync5, lstatSync as lstatSync9, statSync as statSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync9, readdirSync as readdirSync6, opendirSync as opendirSync3, readlinkSync, realpathSync as realpathSync2, renameSync as renameSync2, rmdirSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, chmodSync, openSync as openSync6, closeSync as closeSync6, fsyncSync, fstatSync as fstatSync6, readSync as readSync5, constants as constants5, linkSync } from "fs";
|
|
45217
|
+
import { dirname as dirname11, join as join15, resolve as resolve7, relative as relative2, isAbsolute as isAbsolute7, sep as sep2 } from "path";
|
|
45218
45218
|
import { homedir as homedir3 } from "os";
|
|
45219
45219
|
|
|
45220
45220
|
// src/lib/managed-policy.ts
|
|
@@ -45484,7 +45484,7 @@ function readSkillSession(sessionId, options = {}) {
|
|
|
45484
45484
|
}
|
|
45485
45485
|
|
|
45486
45486
|
// src/lib/agent-policy-limits.ts
|
|
45487
|
-
var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRoots: 512, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
|
|
45487
|
+
var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
|
|
45488
45488
|
function object2(value) {
|
|
45489
45489
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
45490
45490
|
}
|
|
@@ -45548,12 +45548,20 @@ function assertAgentPolicyCollections(policy) {
|
|
|
45548
45548
|
requireBound(object2(source));
|
|
45549
45549
|
text(source.path);
|
|
45550
45550
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
45551
|
+
if (source.hashMode !== undefined)
|
|
45552
|
+
requireBound(source.hashMode === "bytes" && source.format === undefined && source.fields === undefined);
|
|
45551
45553
|
if (source.format !== undefined)
|
|
45552
45554
|
requireBound(["json", "toml", "yaml"].includes(source.format));
|
|
45553
45555
|
if (source.fields !== undefined)
|
|
45554
45556
|
for (const field of array(source.fields, AGENT_POLICY_LIMITS.fields))
|
|
45555
45557
|
text(field, 256);
|
|
45556
45558
|
}
|
|
45559
|
+
if (value.directories !== undefined)
|
|
45560
|
+
for (const directory of array(value.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
45561
|
+
requireBound(object2(directory));
|
|
45562
|
+
text(directory.path);
|
|
45563
|
+
requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
|
|
45564
|
+
}
|
|
45557
45565
|
if (value.builtinNames !== undefined)
|
|
45558
45566
|
for (const name of array(value.builtinNames, AGENT_POLICY_LIMITS.builtinNames))
|
|
45559
45567
|
text(name, 128);
|
|
@@ -45682,9 +45690,9 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
45682
45690
|
}
|
|
45683
45691
|
|
|
45684
45692
|
// src/lib/agent-discovery.ts
|
|
45685
|
-
import { createHash as
|
|
45686
|
-
import { lstatSync as
|
|
45687
|
-
import { dirname as
|
|
45693
|
+
import { createHash as createHash11 } from "crypto";
|
|
45694
|
+
import { lstatSync as lstatSync8, readFileSync as readFileSync8, readdirSync as readdirSync5, realpathSync } from "fs";
|
|
45695
|
+
import { dirname as dirname10, isAbsolute as isAbsolute6, join as join14, resolve as resolve6, sep } from "path";
|
|
45688
45696
|
|
|
45689
45697
|
// ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/index.js
|
|
45690
45698
|
var composer = require_composer();
|
|
@@ -45802,6 +45810,10 @@ function configureHermesHooks(text2, supervisor, previous) {
|
|
|
45802
45810
|
function assertHermesEnvironment(home) {
|
|
45803
45811
|
if (process.env.TERMINAL_CWD)
|
|
45804
45812
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes TERMINAL_CWD requires a separately reviewed effective project; unset it before using this adapter");
|
|
45813
|
+
for (const variable of ["HERMES_BUNDLED_PLUGINS", "HERMES_BUNDLED_SKILLS"]) {
|
|
45814
|
+
if (process.env[variable])
|
|
45815
|
+
throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
|
|
45816
|
+
}
|
|
45805
45817
|
const selected = process.env.HERMES_HOME?.trim();
|
|
45806
45818
|
if (selected && (!isAbsolute3(selected) || resolve3(selected) !== join12(resolve3(home), ".hermes")))
|
|
45807
45819
|
throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
|
|
@@ -45893,8 +45905,147 @@ if (failed) process.exitCode = 2;
|
|
|
45893
45905
|
`;
|
|
45894
45906
|
}
|
|
45895
45907
|
|
|
45908
|
+
// src/lib/agent-discovery-directories.ts
|
|
45909
|
+
import { createHash as createHash9 } from "crypto";
|
|
45910
|
+
import { lstatSync as lstatSync6, opendirSync as opendirSync2 } from "fs";
|
|
45911
|
+
import { dirname as dirname8, isAbsolute as isAbsolute4, join as join13, resolve as resolve4 } from "path";
|
|
45912
|
+
function need(value, message) {
|
|
45913
|
+
if (!value)
|
|
45914
|
+
throw new Error(message);
|
|
45915
|
+
}
|
|
45916
|
+
function safe(path) {
|
|
45917
|
+
need(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute4(path) && resolve4(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
|
|
45918
|
+
for (let cursor = path;; cursor = dirname8(cursor)) {
|
|
45919
|
+
need(!lstatSync6(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
|
|
45920
|
+
if (dirname8(cursor) === cursor)
|
|
45921
|
+
break;
|
|
45922
|
+
}
|
|
45923
|
+
}
|
|
45924
|
+
function same(before, after) {
|
|
45925
|
+
return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
|
|
45926
|
+
}
|
|
45927
|
+
function capture(path, budget) {
|
|
45928
|
+
safe(path);
|
|
45929
|
+
const root3 = lstatSync6(path, { throwIfNoEntry: false });
|
|
45930
|
+
if (!root3)
|
|
45931
|
+
return { path, sha256: null };
|
|
45932
|
+
need(root3.isDirectory(), "Unsupported native discovery directory input");
|
|
45933
|
+
const hash2 = createHash9("sha256");
|
|
45934
|
+
function walk2(current, relative2, before, depth) {
|
|
45935
|
+
need(depth <= 64, "Native discovery directory depth limit exceeded");
|
|
45936
|
+
const names = [], directory = opendirSync2(current);
|
|
45937
|
+
try {
|
|
45938
|
+
for (let entry = directory.readSync();entry; entry = directory.readSync()) {
|
|
45939
|
+
need(++budget.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
|
|
45940
|
+
names.push(entry.name);
|
|
45941
|
+
}
|
|
45942
|
+
} finally {
|
|
45943
|
+
directory.closeSync();
|
|
45944
|
+
}
|
|
45945
|
+
need(new Set(names).size === names.length, "Ambiguous native discovery directory names");
|
|
45946
|
+
for (const name of names.sort()) {
|
|
45947
|
+
const child = join13(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
|
|
45948
|
+
need(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
|
|
45949
|
+
const stat = lstatSync6(child);
|
|
45950
|
+
need(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
|
|
45951
|
+
const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
|
|
45952
|
+
`;
|
|
45953
|
+
budget.bytes += Buffer.byteLength(row);
|
|
45954
|
+
need(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
|
|
45955
|
+
hash2.update(row);
|
|
45956
|
+
if (stat.isDirectory())
|
|
45957
|
+
walk2(child, childRelative, stat, depth + 1);
|
|
45958
|
+
}
|
|
45959
|
+
need(same(before, lstatSync6(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
|
|
45960
|
+
}
|
|
45961
|
+
walk2(path, "", root3, 0);
|
|
45962
|
+
safe(path);
|
|
45963
|
+
return { path, sha256: hash2.digest("hex") };
|
|
45964
|
+
}
|
|
45965
|
+
function captureDiscoveryDirectories(paths) {
|
|
45966
|
+
need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
|
|
45967
|
+
const budget = { entries: 0, bytes: 0 };
|
|
45968
|
+
return paths.map((path) => capture(path, budget));
|
|
45969
|
+
}
|
|
45970
|
+
function verifyDiscoveryDirectories(directories) {
|
|
45971
|
+
need(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
|
|
45972
|
+
for (const directory of directories)
|
|
45973
|
+
need(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
|
|
45974
|
+
const current = captureDiscoveryDirectories(directories.map((directory) => directory.path));
|
|
45975
|
+
for (let i3 = 0;i3 < directories.length; i3++)
|
|
45976
|
+
need(current[i3].sha256 === directories[i3].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i3].path}`);
|
|
45977
|
+
}
|
|
45978
|
+
|
|
45979
|
+
// src/lib/agent-discovery-bytes.ts
|
|
45980
|
+
import { createHash as createHash10 } from "crypto";
|
|
45981
|
+
import { closeSync as closeSync5, constants as constants4, fstatSync as fstatSync5, lstatSync as lstatSync7, openSync as openSync5, readSync as readSync4 } from "fs";
|
|
45982
|
+
import { dirname as dirname9, isAbsolute as isAbsolute5, resolve as resolve5 } from "path";
|
|
45983
|
+
function discoveryByteBudget() {
|
|
45984
|
+
return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
|
|
45985
|
+
}
|
|
45986
|
+
function safe2(path) {
|
|
45987
|
+
if (typeof path !== "string" || !isAbsolute5(path) || path !== resolve5(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
|
|
45988
|
+
throw new Error("Expected a canonical absolute raw discovery path");
|
|
45989
|
+
for (let cursor = path;; cursor = dirname9(cursor)) {
|
|
45990
|
+
if (lstatSync7(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
45991
|
+
throw new Error("Refusing symlink raw discovery input");
|
|
45992
|
+
if (dirname9(cursor) === cursor)
|
|
45993
|
+
break;
|
|
45994
|
+
}
|
|
45995
|
+
}
|
|
45996
|
+
function same2(left, right) {
|
|
45997
|
+
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
|
|
45998
|
+
}
|
|
45999
|
+
function consume(budget, bytes) {
|
|
46000
|
+
if (bytes > budget.remaining)
|
|
46001
|
+
throw new Error("Raw discovery aggregate byte limit exceeded");
|
|
46002
|
+
budget.remaining -= bytes;
|
|
46003
|
+
}
|
|
46004
|
+
function hashRawDiscoveryFile(path, budget, changes) {
|
|
46005
|
+
safe2(path);
|
|
46006
|
+
if (changes?.has(path)) {
|
|
46007
|
+
const text2 = changes.get(path), bytes = Buffer.byteLength(text2);
|
|
46008
|
+
if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
46009
|
+
throw new Error("Raw discovery file byte limit exceeded");
|
|
46010
|
+
consume(budget, bytes);
|
|
46011
|
+
return createHash10("sha256").update(text2).digest("hex");
|
|
46012
|
+
}
|
|
46013
|
+
const initial = lstatSync7(path, { throwIfNoEntry: false });
|
|
46014
|
+
if (!initial)
|
|
46015
|
+
return null;
|
|
46016
|
+
if (!initial.isFile() || initial.size > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
46017
|
+
throw new Error("Unsupported or oversized raw discovery input");
|
|
46018
|
+
if (initial.size > budget.remaining)
|
|
46019
|
+
throw new Error("Raw discovery aggregate byte limit exceeded");
|
|
46020
|
+
const fd = openSync5(path, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
|
|
46021
|
+
try {
|
|
46022
|
+
const opened = fstatSync5(fd);
|
|
46023
|
+
if (!opened.isFile() || !same2(initial, opened))
|
|
46024
|
+
throw new Error("Raw discovery input changed during open");
|
|
46025
|
+
const hash2 = createHash10("sha256"), buffer = Buffer.alloc(64 * 1024);
|
|
46026
|
+
let bytes = 0;
|
|
46027
|
+
while (true) {
|
|
46028
|
+
const count = readSync4(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
|
|
46029
|
+
if (!count)
|
|
46030
|
+
break;
|
|
46031
|
+
bytes += count;
|
|
46032
|
+
if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
46033
|
+
throw new Error("Raw discovery file byte limit exceeded");
|
|
46034
|
+
consume(budget, count);
|
|
46035
|
+
hash2.update(buffer.subarray(0, count));
|
|
46036
|
+
}
|
|
46037
|
+
const final = fstatSync5(fd), current = lstatSync7(path, { throwIfNoEntry: false });
|
|
46038
|
+
safe2(path);
|
|
46039
|
+
if (!current?.isFile() || !same2(opened, final) || !same2(final, current) || bytes !== final.size)
|
|
46040
|
+
throw new Error("Raw discovery input changed during read");
|
|
46041
|
+
return hash2.digest("hex");
|
|
46042
|
+
} finally {
|
|
46043
|
+
closeSync5(fd);
|
|
46044
|
+
}
|
|
46045
|
+
}
|
|
46046
|
+
|
|
45896
46047
|
// src/lib/agent-discovery.ts
|
|
45897
|
-
var digest2 = (text2) =>
|
|
46048
|
+
var digest2 = (text2) => createHash11("sha256").update(text2).digest("hex");
|
|
45898
46049
|
function parseConfig(text2, path, toml = false) {
|
|
45899
46050
|
try {
|
|
45900
46051
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -45905,28 +46056,35 @@ function parseConfig(text2, path, toml = false) {
|
|
|
45905
46056
|
throw new Error(`Invalid native discovery configuration: ${path}`);
|
|
45906
46057
|
}
|
|
45907
46058
|
}
|
|
45908
|
-
function
|
|
45909
|
-
if (!
|
|
46059
|
+
function safe3(path) {
|
|
46060
|
+
if (!isAbsolute6(path) || path.includes("\x00"))
|
|
45910
46061
|
throw new Error("Expected an absolute native discovery path");
|
|
45911
|
-
for (let cursor = path;; cursor =
|
|
45912
|
-
if (
|
|
46062
|
+
for (let cursor = path;; cursor = dirname10(cursor)) {
|
|
46063
|
+
if (lstatSync8(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
45913
46064
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
45914
|
-
if (
|
|
46065
|
+
if (dirname10(cursor) === cursor)
|
|
45915
46066
|
break;
|
|
45916
46067
|
}
|
|
45917
46068
|
}
|
|
45918
46069
|
function read(path, changes) {
|
|
45919
|
-
|
|
46070
|
+
safe3(path);
|
|
45920
46071
|
if (changes?.has(path))
|
|
45921
46072
|
return changes.get(path);
|
|
45922
|
-
const stat =
|
|
46073
|
+
const stat = lstatSync8(path, { throwIfNoEntry: false });
|
|
45923
46074
|
if (!stat)
|
|
45924
46075
|
return null;
|
|
45925
46076
|
if (!stat.isFile() || stat.size > 16 * 1024 * 1024)
|
|
45926
46077
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
45927
46078
|
return readFileSync8(path, "utf8");
|
|
45928
46079
|
}
|
|
45929
|
-
function projected(source, changes) {
|
|
46080
|
+
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
46081
|
+
if (source.hashMode !== undefined) {
|
|
46082
|
+
if (source.hashMode !== "bytes")
|
|
46083
|
+
throw new Error("Invalid native discovery hash mode");
|
|
46084
|
+
if (source.format !== undefined || source.fields !== undefined)
|
|
46085
|
+
throw new Error("Raw discovery witnesses cannot project configuration fields");
|
|
46086
|
+
return hashRawDiscoveryFile(source.path, budget, changes);
|
|
46087
|
+
}
|
|
45930
46088
|
const text2 = read(source.path, changes);
|
|
45931
46089
|
if (text2 === null)
|
|
45932
46090
|
return null;
|
|
@@ -45940,22 +46098,34 @@ function projected(source, changes) {
|
|
|
45940
46098
|
function verifyAgentDiscovery(binding) {
|
|
45941
46099
|
if (!binding || !Array.isArray(binding.sources) || !Array.isArray(binding.roots) || binding.sources.length > AGENT_POLICY_LIMITS.discoverySources || binding.roots.length > AGENT_POLICY_LIMITS.discoveryRoots)
|
|
45942
46100
|
throw new Error("Invalid native discovery binding");
|
|
46101
|
+
if (binding.agent === "hermes" && !binding.directories?.length)
|
|
46102
|
+
throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
|
|
46103
|
+
if (binding.directories !== undefined)
|
|
46104
|
+
verifyDiscoveryDirectories(binding.directories);
|
|
46105
|
+
const budget = discoveryByteBudget();
|
|
45943
46106
|
for (const source of binding.sources) {
|
|
45944
46107
|
if (source.format !== undefined && (!["json", "toml", "yaml"].includes(source.format) || !Array.isArray(source.fields) || !source.fields.length || source.fields.length > 64 || source.fields.some((field) => typeof field !== "string" || !field)))
|
|
45945
46108
|
throw new Error("Invalid native discovery projection");
|
|
45946
46109
|
if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
45947
46110
|
throw new Error("Invalid native discovery digest");
|
|
45948
|
-
if (projected(source) !== source.sha256)
|
|
46111
|
+
if (projected(source, undefined, budget) !== source.sha256)
|
|
45949
46112
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
45950
46113
|
}
|
|
45951
46114
|
for (const root3 of binding.roots)
|
|
45952
|
-
|
|
46115
|
+
safe3(root3);
|
|
45953
46116
|
}
|
|
45954
46117
|
function rebindAgentDiscovery(binding, changes) {
|
|
45955
|
-
|
|
46118
|
+
const budget = discoveryByteBudget();
|
|
46119
|
+
return { ...binding, sources: binding.sources.map((source) => ({ ...source, sha256: projected(source, changes, budget) })) };
|
|
46120
|
+
}
|
|
46121
|
+
function captureDiscoveryByteSources(paths) {
|
|
46122
|
+
if (!Array.isArray(paths) || paths.length > AGENT_POLICY_LIMITS.discoverySources || new Set(paths).size !== paths.length)
|
|
46123
|
+
throw new Error("Invalid raw discovery source collection");
|
|
46124
|
+
const budget = discoveryByteBudget();
|
|
46125
|
+
return paths.map((path) => ({ path, hashMode: "bytes", sha256: hashRawDiscoveryFile(path, budget) }));
|
|
45956
46126
|
}
|
|
45957
46127
|
function resolveAgentDiscovery(options) {
|
|
45958
|
-
const canonical = options.canonical ??
|
|
46128
|
+
const canonical = options.canonical ?? resolve6, home = resolve6(options.home), agent = options.agent;
|
|
45959
46129
|
const sources = [], roots = new Set, builtinNames = [];
|
|
45960
46130
|
const witness = (path, format, fields) => {
|
|
45961
46131
|
const source = { path: canonical(path), sha256: null, ...format ? { format, fields } : {} };
|
|
@@ -45965,14 +46135,14 @@ function resolveAgentDiscovery(options) {
|
|
|
45965
46135
|
};
|
|
45966
46136
|
if (agent === "hermes")
|
|
45967
46137
|
assertHermesEnvironment(home);
|
|
45968
|
-
const configPath =
|
|
46138
|
+
const configPath = join14(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
|
|
45969
46139
|
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : ["version"]);
|
|
45970
46140
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
45971
46141
|
const unresolved = (detail) => {
|
|
45972
46142
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
45973
46143
|
};
|
|
45974
46144
|
if (agent === "hermes") {
|
|
45975
|
-
const profile2 = witness(
|
|
46145
|
+
const profile2 = witness(join14(home, ".hermes/active_profile"));
|
|
45976
46146
|
if (profile2?.trim() && profile2.trim() !== "default")
|
|
45977
46147
|
unresolved("a non-default profile is active");
|
|
45978
46148
|
const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
|
|
@@ -45986,8 +46156,8 @@ function resolveAgentDiscovery(options) {
|
|
|
45986
46156
|
continue;
|
|
45987
46157
|
if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
|
|
45988
46158
|
unresolved("user-specific tilde expansion requires a separate discovery adapter");
|
|
45989
|
-
const path = normalized === "~" ? home : normalized.startsWith("~/") ?
|
|
45990
|
-
|
|
46159
|
+
const path = normalized === "~" ? home : normalized.startsWith("~/") ? join14(home, normalized.slice(2)) : resolve6(home, ".hermes", normalized);
|
|
46160
|
+
safe3(path);
|
|
45991
46161
|
roots.add(path);
|
|
45992
46162
|
}
|
|
45993
46163
|
}
|
|
@@ -45995,24 +46165,24 @@ function resolveAgentDiscovery(options) {
|
|
|
45995
46165
|
const executable = Bun.which("gemini");
|
|
45996
46166
|
if (executable) {
|
|
45997
46167
|
let packageRoot;
|
|
45998
|
-
for (let directory =
|
|
45999
|
-
const path =
|
|
46168
|
+
for (let directory = dirname10(realpathSync(executable)), depth = 0;depth < 8; directory = dirname10(directory), depth++) {
|
|
46169
|
+
const path = join14(directory, "package.json"), raw = read(path);
|
|
46000
46170
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
46001
46171
|
packageRoot = directory;
|
|
46002
46172
|
witness(path);
|
|
46003
46173
|
break;
|
|
46004
46174
|
}
|
|
46005
|
-
if (
|
|
46175
|
+
if (dirname10(directory) === directory)
|
|
46006
46176
|
break;
|
|
46007
46177
|
}
|
|
46008
46178
|
if (!packageRoot)
|
|
46009
46179
|
unresolved("installed Gemini package layout is not recognized");
|
|
46010
|
-
const builtinRoot =
|
|
46011
|
-
|
|
46012
|
-
if (!
|
|
46180
|
+
const builtinRoot = join14(packageRoot, "bundle/builtin");
|
|
46181
|
+
safe3(builtinRoot);
|
|
46182
|
+
if (!lstatSync8(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
46013
46183
|
unresolved("installed Gemini builtin root is missing");
|
|
46014
46184
|
for (const name of readdirSync5(builtinRoot).sort()) {
|
|
46015
|
-
const text2 = witness(
|
|
46185
|
+
const text2 = witness(join14(builtinRoot, name, "SKILL.md"));
|
|
46016
46186
|
const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
|
|
46017
46187
|
if (!match?.[1] || builtinNames.includes(match[1]))
|
|
46018
46188
|
unresolved("installed Gemini builtin name is missing or ambiguous");
|
|
@@ -46026,7 +46196,7 @@ function resolveAgentDiscovery(options) {
|
|
|
46026
46196
|
if (review) {
|
|
46027
46197
|
if (review.pluginHooks !== "reviewed-no-skill-injection" || !Array.isArray(review.sources) || !review.sources.length || !Array.isArray(review.roots))
|
|
46028
46198
|
throw new Error("Discovery review must bind sources and confirm plugin hooks do not inject retired skills");
|
|
46029
|
-
const supplied = { agent, roots: review.roots, sources: review.sources, method: "reviewed" };
|
|
46199
|
+
const supplied = { agent, roots: review.roots, sources: review.sources, ...review.directories !== undefined ? { directories: review.directories } : {}, method: "reviewed" };
|
|
46030
46200
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
46031
46201
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
46032
46202
|
verifyAgentDiscovery(supplied);
|
|
@@ -46036,40 +46206,40 @@ function resolveAgentDiscovery(options) {
|
|
|
46036
46206
|
}
|
|
46037
46207
|
function plugin(root3) {
|
|
46038
46208
|
root3 = canonical(root3);
|
|
46039
|
-
|
|
46040
|
-
const manifestPath =
|
|
46209
|
+
safe3(root3);
|
|
46210
|
+
const manifestPath = join14(root3, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
46041
46211
|
const raw = witness(manifestPath);
|
|
46042
46212
|
if (raw === null)
|
|
46043
46213
|
unresolved("plugin manifest missing");
|
|
46044
46214
|
const manifest = parseConfig(raw, manifestPath);
|
|
46045
|
-
const hooks = witness(
|
|
46215
|
+
const hooks = witness(join14(root3, "hooks/hooks.json"));
|
|
46046
46216
|
if (manifest.hooks !== undefined || hooks !== null)
|
|
46047
46217
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
46048
|
-
const commands2 =
|
|
46049
|
-
|
|
46050
|
-
if (agent === "claude" && (manifest.commands !== undefined ||
|
|
46218
|
+
const commands2 = join14(root3, "commands");
|
|
46219
|
+
safe3(commands2);
|
|
46220
|
+
if (agent === "claude" && (manifest.commands !== undefined || lstatSync8(commands2, { throwIfNoEntry: false })?.isDirectory() && readdirSync5(commands2).length))
|
|
46051
46221
|
unresolved("legacy command discovery requires retirement or a dedicated format adapter");
|
|
46052
|
-
const rootSkill = witness(
|
|
46222
|
+
const rootSkill = witness(join14(root3, "SKILL.md"));
|
|
46053
46223
|
if (rootSkill !== null)
|
|
46054
46224
|
roots.add(root3);
|
|
46055
46225
|
const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
46056
46226
|
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
46057
46227
|
unresolved("unsupported plugin skill paths");
|
|
46058
46228
|
if (agent === "claude")
|
|
46059
|
-
roots.add(
|
|
46229
|
+
roots.add(join14(root3, "skills"));
|
|
46060
46230
|
for (const value of declared) {
|
|
46061
|
-
const path =
|
|
46231
|
+
const path = resolve6(root3, value);
|
|
46062
46232
|
if (path !== root3 && !path.startsWith(root3 + sep))
|
|
46063
46233
|
unresolved("plugin skill path escapes its root");
|
|
46064
|
-
roots.add(path.endsWith(`${sep}SKILL.md`) ?
|
|
46234
|
+
roots.add(path.endsWith(`${sep}SKILL.md`) ? dirname10(path) : path);
|
|
46065
46235
|
}
|
|
46066
46236
|
}
|
|
46067
46237
|
if (agent === "claude") {
|
|
46068
|
-
const registrations = witness(
|
|
46069
|
-
const installed = registrations === null ? {} : parseConfig(registrations,
|
|
46070
|
-
const commands2 = canonical(
|
|
46071
|
-
|
|
46072
|
-
if (
|
|
46238
|
+
const registrations = witness(join14(home, ".claude/plugins/installed_plugins.json"));
|
|
46239
|
+
const installed = registrations === null ? {} : parseConfig(registrations, join14(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
46240
|
+
const commands2 = canonical(join14(home, ".claude/commands"));
|
|
46241
|
+
safe3(commands2);
|
|
46242
|
+
if (lstatSync8(commands2, { throwIfNoEntry: false })?.isDirectory() && readdirSync5(commands2).length)
|
|
46073
46243
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
46074
46244
|
for (const [id, enabled] of Object.entries(config.enabledPlugins ?? {})) {
|
|
46075
46245
|
if (enabled === false)
|
|
@@ -46089,34 +46259,34 @@ function resolveAgentDiscovery(options) {
|
|
|
46089
46259
|
unresolved("unsupported plugin enablement");
|
|
46090
46260
|
const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
|
|
46091
46261
|
const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
|
|
46092
|
-
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !
|
|
46262
|
+
if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute6(market.source))
|
|
46093
46263
|
unresolved("enabled plugin marketplace is not an explicit local source");
|
|
46094
|
-
const catalogText = witness(
|
|
46264
|
+
const catalogText = witness(join14(market.source, ".agents/plugins/marketplace.json"));
|
|
46095
46265
|
if (catalogText === null)
|
|
46096
46266
|
unresolved("marketplace catalog missing");
|
|
46097
|
-
const catalog = parseConfig(catalogText,
|
|
46267
|
+
const catalog = parseConfig(catalogText, join14(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
46098
46268
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
46099
46269
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
46100
|
-
plugin(
|
|
46270
|
+
plugin(resolve6(market.source, matches[0].source.path));
|
|
46101
46271
|
}
|
|
46102
46272
|
} else if (agent === "opencode") {
|
|
46103
|
-
if (witness(
|
|
46273
|
+
if (witness(join14(home, ".config/opencode/opencode.jsonc")) !== null || witness(join14(home, ".config/opencode/config.json")) !== null)
|
|
46104
46274
|
unresolved("additional config layers require review");
|
|
46105
46275
|
if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
|
|
46106
46276
|
unresolved("external plugin or remote skill source");
|
|
46107
46277
|
for (const path of config.skills?.paths ?? []) {
|
|
46108
|
-
if (typeof path !== "string" || !
|
|
46278
|
+
if (typeof path !== "string" || !isAbsolute6(path))
|
|
46109
46279
|
unresolved("relative or malformed added skill root");
|
|
46110
46280
|
roots.add(path);
|
|
46111
46281
|
}
|
|
46112
|
-
const localPlugins =
|
|
46113
|
-
|
|
46114
|
-
if (
|
|
46282
|
+
const localPlugins = join14(home, ".config/opencode/plugins");
|
|
46283
|
+
safe3(localPlugins);
|
|
46284
|
+
if (lstatSync8(localPlugins, { throwIfNoEntry: false }) && readdirSync5(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
46115
46285
|
unresolved("additional local plugin hooks require review");
|
|
46116
46286
|
} else if (agent === "hermes") {
|
|
46117
|
-
for (const directory of [
|
|
46118
|
-
|
|
46119
|
-
if (
|
|
46287
|
+
for (const directory of [join14(home, ".hermes/plugins"), join14(home, ".hermes/hermes-agent")]) {
|
|
46288
|
+
safe3(directory);
|
|
46289
|
+
if (lstatSync8(directory, { throwIfNoEntry: false }))
|
|
46120
46290
|
unresolved("installed runtime/plugin discovery requires reviewed source bindings");
|
|
46121
46291
|
}
|
|
46122
46292
|
if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
|
|
@@ -46124,24 +46294,24 @@ function resolveAgentDiscovery(options) {
|
|
|
46124
46294
|
} else if (agent === "gemini") {
|
|
46125
46295
|
if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
|
|
46126
46296
|
unresolved("custom extension or skill paths");
|
|
46127
|
-
const directory =
|
|
46128
|
-
|
|
46129
|
-
if (
|
|
46297
|
+
const directory = join14(home, ".gemini/extensions");
|
|
46298
|
+
safe3(directory);
|
|
46299
|
+
if (lstatSync8(directory, { throwIfNoEntry: false }))
|
|
46130
46300
|
for (const name of readdirSync5(directory)) {
|
|
46131
|
-
const root3 =
|
|
46132
|
-
|
|
46133
|
-
if (!
|
|
46301
|
+
const root3 = join14(directory, name);
|
|
46302
|
+
safe3(root3);
|
|
46303
|
+
if (!lstatSync8(root3)?.isDirectory())
|
|
46134
46304
|
continue;
|
|
46135
|
-
const raw = witness(
|
|
46305
|
+
const raw = witness(join14(root3, "gemini-extension.json"));
|
|
46136
46306
|
if (raw === null)
|
|
46137
46307
|
unresolved("extension registration missing");
|
|
46138
|
-
const manifest = parseConfig(raw,
|
|
46139
|
-
if (manifest.hooks !== undefined || witness(
|
|
46308
|
+
const manifest = parseConfig(raw, join14(root3, "gemini-extension.json"));
|
|
46309
|
+
if (manifest.hooks !== undefined || witness(join14(root3, "hooks/hooks.json")) !== null || witness(join14(root3, ".gemini-extension-install.json")) !== null)
|
|
46140
46310
|
unresolved("extension hooks or linked registration require review");
|
|
46141
|
-
roots.add(
|
|
46311
|
+
roots.add(join14(root3, "skills"));
|
|
46142
46312
|
}
|
|
46143
46313
|
}
|
|
46144
|
-
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {} };
|
|
46314
|
+
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join14(home, ".hermes/plugins"), join14(home, ".hermes/hermes-agent")].map((path) => canonical(path))) } : {} };
|
|
46145
46315
|
}
|
|
46146
46316
|
|
|
46147
46317
|
// src/lib/agent-adapters.ts
|
|
@@ -46206,7 +46376,7 @@ export default async ({ directory }) => {
|
|
|
46206
46376
|
}
|
|
46207
46377
|
|
|
46208
46378
|
// src/lib/agent-integration.ts
|
|
46209
|
-
var sha = (data) =>
|
|
46379
|
+
var sha = (data) => createHash12("sha256").update(data).digest("hex");
|
|
46210
46380
|
var ROOTS = [
|
|
46211
46381
|
["claude", ".claude/skills"],
|
|
46212
46382
|
["codex", ".codex/skills"],
|
|
@@ -46225,11 +46395,11 @@ var ROOTS = [
|
|
|
46225
46395
|
["copilot", ".github/skills"]
|
|
46226
46396
|
];
|
|
46227
46397
|
function assertSafePath(path) {
|
|
46228
|
-
let cursor =
|
|
46398
|
+
let cursor = resolve7(path);
|
|
46229
46399
|
while (true) {
|
|
46230
|
-
if (
|
|
46400
|
+
if (lstatSync9(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
46231
46401
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
46232
|
-
const parent =
|
|
46402
|
+
const parent = dirname11(cursor);
|
|
46233
46403
|
if (parent === cursor)
|
|
46234
46404
|
return;
|
|
46235
46405
|
cursor = parent;
|
|
@@ -46237,17 +46407,17 @@ function assertSafePath(path) {
|
|
|
46237
46407
|
}
|
|
46238
46408
|
function readRootAlias(home, agent) {
|
|
46239
46409
|
assertSafePath(home);
|
|
46240
|
-
const realHome = realpathSync2(home), alias =
|
|
46241
|
-
const stat =
|
|
46410
|
+
const realHome = realpathSync2(home), alias = join15(realHome, `.${agent}`);
|
|
46411
|
+
const stat = lstatSync9(alias, { throwIfNoEntry: false, bigint: true });
|
|
46242
46412
|
if (!stat?.isSymbolicLink())
|
|
46243
46413
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
46244
|
-
const link = readlinkSync(alias), targetPath =
|
|
46414
|
+
const link = readlinkSync(alias), targetPath = resolve7(dirname11(alias), link);
|
|
46245
46415
|
assertSafePath(targetPath);
|
|
46246
|
-
const targetStat =
|
|
46416
|
+
const targetStat = lstatSync9(targetPath, { throwIfNoEntry: false });
|
|
46247
46417
|
if (!targetStat?.isDirectory())
|
|
46248
46418
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
46249
46419
|
const target = realpathSync2(targetPath), inside = relative2(realHome, target);
|
|
46250
|
-
if (!inside || inside === ".." || inside.startsWith(`..${sep2}`) ||
|
|
46420
|
+
if (!inside || inside === ".." || inside.startsWith(`..${sep2}`) || isAbsolute7(inside))
|
|
46251
46421
|
throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
|
|
46252
46422
|
return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
|
|
46253
46423
|
}
|
|
@@ -46256,19 +46426,19 @@ function rootAliases(home, allowed = false) {
|
|
|
46256
46426
|
return [];
|
|
46257
46427
|
const result = [];
|
|
46258
46428
|
for (const agent of ["claude", "codex"])
|
|
46259
|
-
if (
|
|
46429
|
+
if (lstatSync9(join15(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
|
|
46260
46430
|
result.push(readRootAlias(home, agent));
|
|
46261
46431
|
if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep2) || other.target.startsWith(item.target + sep2))))
|
|
46262
46432
|
throw new Error("Agent root alias targets must not overlap");
|
|
46263
46433
|
return result;
|
|
46264
46434
|
}
|
|
46265
46435
|
function canonicalAgentPath(path, aliases) {
|
|
46266
|
-
const absolute =
|
|
46267
|
-
return binding ?
|
|
46436
|
+
const absolute = resolve7(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep2));
|
|
46437
|
+
return binding ? join15(binding.target, relative2(binding.alias, absolute)) : absolute;
|
|
46268
46438
|
}
|
|
46269
46439
|
function recheckRootAliases(aliases) {
|
|
46270
46440
|
for (const binding of aliases) {
|
|
46271
|
-
if (!["claude", "codex"].includes(binding.agent) || binding.alias !==
|
|
46441
|
+
if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join15(binding.home, `.${binding.agent}`))
|
|
46272
46442
|
throw new Error("Invalid agent root alias binding");
|
|
46273
46443
|
const current = readRootAlias(binding.home, binding.agent);
|
|
46274
46444
|
if (JSON.stringify(current) !== JSON.stringify(binding))
|
|
@@ -46277,42 +46447,42 @@ function recheckRootAliases(aliases) {
|
|
|
46277
46447
|
}
|
|
46278
46448
|
function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
46279
46449
|
assertSafePath(path);
|
|
46280
|
-
const before =
|
|
46450
|
+
const before = lstatSync9(path);
|
|
46281
46451
|
if (!before.isFile() || before.size > maximum)
|
|
46282
46452
|
throw new Error("Unsupported or oversized native skill file");
|
|
46283
|
-
const descriptor =
|
|
46453
|
+
const descriptor = openSync6(path, constants5.O_RDONLY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK);
|
|
46284
46454
|
try {
|
|
46285
|
-
const opened =
|
|
46455
|
+
const opened = fstatSync6(descriptor);
|
|
46286
46456
|
if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size)
|
|
46287
46457
|
throw new Error("Native skill file changed while reading");
|
|
46288
46458
|
const bytes = Buffer.allocUnsafe(before.size + 1);
|
|
46289
46459
|
let length = 0;
|
|
46290
46460
|
while (length < bytes.length) {
|
|
46291
|
-
const count =
|
|
46461
|
+
const count = readSync5(descriptor, bytes, length, bytes.length - length, null);
|
|
46292
46462
|
if (!count)
|
|
46293
46463
|
break;
|
|
46294
46464
|
length += count;
|
|
46295
46465
|
}
|
|
46296
|
-
const after =
|
|
46466
|
+
const after = fstatSync6(descriptor);
|
|
46297
46467
|
if (length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
|
|
46298
46468
|
throw new Error("Native skill file changed while reading");
|
|
46299
46469
|
return bytes.subarray(0, length);
|
|
46300
46470
|
} finally {
|
|
46301
|
-
|
|
46471
|
+
closeSync6(descriptor);
|
|
46302
46472
|
}
|
|
46303
46473
|
}
|
|
46304
46474
|
function treeHash(root3) {
|
|
46305
|
-
const hash2 =
|
|
46475
|
+
const hash2 = createHash12("sha256");
|
|
46306
46476
|
let bytes = 0, files = 0;
|
|
46307
46477
|
function visit2(path) {
|
|
46308
|
-
const stat =
|
|
46478
|
+
const stat = lstatSync9(path);
|
|
46309
46479
|
if (stat.isSymbolicLink())
|
|
46310
46480
|
throw new Error(`Refusing symlink in skill: ${path}`);
|
|
46311
46481
|
const rel = relative2(root3, path);
|
|
46312
46482
|
if (stat.isDirectory()) {
|
|
46313
46483
|
hash2.update(`d\x00${rel}\x00`);
|
|
46314
46484
|
for (const name of readdirSync6(path).sort())
|
|
46315
|
-
visit2(
|
|
46485
|
+
visit2(join15(path, name));
|
|
46316
46486
|
return;
|
|
46317
46487
|
}
|
|
46318
46488
|
if (!stat.isFile())
|
|
@@ -46330,37 +46500,50 @@ function treeHash(root3) {
|
|
|
46330
46500
|
}
|
|
46331
46501
|
function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
46332
46502
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
46333
|
-
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(
|
|
46334
|
-
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(
|
|
46335
|
-
for (const project of new Set([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []].map((path) =>
|
|
46503
|
+
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join15(home, path), aliases)]);
|
|
46504
|
+
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join15(home, adapter.root, CLI_BRIDGE_NAME), aliases));
|
|
46505
|
+
for (const project of new Set([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []].map((path) => resolve7(path)))) {
|
|
46336
46506
|
for (const [agent, path] of ROOTS)
|
|
46337
|
-
roots.push([agent, canonicalAgentPath(
|
|
46507
|
+
roots.push([agent, canonicalAgentPath(join15(project, path), aliases)]);
|
|
46338
46508
|
}
|
|
46339
46509
|
const entries = [], seen = new Set;
|
|
46340
46510
|
const cacheScans = new Map, maxAliasProofEntries = 1e4;
|
|
46511
|
+
let discoveryEntries = 0, discoveryPathBytes = 0;
|
|
46512
|
+
function admit(path) {
|
|
46513
|
+
if (++discoveryEntries > 20000)
|
|
46514
|
+
throw new Error("Native skill discovery entry limit exceeded");
|
|
46515
|
+
discoveryPathBytes += Buffer.byteLength(path, "utf8");
|
|
46516
|
+
if (discoveryPathBytes > 4 * 1024 * 1024)
|
|
46517
|
+
throw new Error("Native skill discovery metadata limit exceeded");
|
|
46518
|
+
}
|
|
46341
46519
|
function emptySiblingCacheAlias(path, parent) {
|
|
46342
|
-
const link = readlinkSync(path), target =
|
|
46343
|
-
if (
|
|
46520
|
+
const link = readlinkSync(path), target = resolve7(parent, link);
|
|
46521
|
+
if (dirname11(target) !== parent || (isAbsolute7(link) ? link !== target : dirname11(link) !== "."))
|
|
46344
46522
|
return false;
|
|
46345
|
-
if (!
|
|
46523
|
+
if (!lstatSync9(target, { throwIfNoEntry: false })?.isDirectory())
|
|
46346
46524
|
return false;
|
|
46347
46525
|
const scan = cacheScans.get(target);
|
|
46348
46526
|
return Boolean(scan?.complete && !scan.hasSkills && scan.entries <= maxAliasProofEntries);
|
|
46349
46527
|
}
|
|
46350
|
-
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false) {
|
|
46528
|
+
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
|
|
46529
|
+
if (!admitted)
|
|
46530
|
+
admit(path);
|
|
46351
46531
|
const scan = { complete: true, hasSkills: false, entries: 1 };
|
|
46352
46532
|
assertSafePath(path);
|
|
46353
46533
|
if (!existsSync5(path))
|
|
46354
46534
|
return scan;
|
|
46355
|
-
|
|
46535
|
+
const stat = lstatSync9(path);
|
|
46536
|
+
if (stat.isFile())
|
|
46356
46537
|
return scan;
|
|
46357
|
-
if (
|
|
46538
|
+
if (!stat.isDirectory())
|
|
46539
|
+
throw new Error(`Unsupported native discovery entry: ${path}`);
|
|
46540
|
+
if (existsSync5(join15(path, "SKILL.md"))) {
|
|
46358
46541
|
scan.hasSkills = true;
|
|
46359
46542
|
if (seen.has(path))
|
|
46360
46543
|
return scan;
|
|
46361
46544
|
seen.add(path);
|
|
46362
46545
|
let managed = false;
|
|
46363
|
-
const marker =
|
|
46546
|
+
const marker = join15(path, ".hasna-skills.json");
|
|
46364
46547
|
if (existsSync5(marker)) {
|
|
46365
46548
|
try {
|
|
46366
46549
|
managed = JSON.parse(readFileSync9(marker, "utf8")).managedBy === "@hasna/skills";
|
|
@@ -46368,12 +46551,22 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
46368
46551
|
}
|
|
46369
46552
|
const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
|
|
46370
46553
|
const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep2));
|
|
46371
|
-
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(
|
|
46554
|
+
entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join15(home, ".codex", "skills", ".system"), aliases) + sep2) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve7(home) } : {}, ...rootAlias ? { rootAlias } : {} });
|
|
46372
46555
|
return scan;
|
|
46373
46556
|
}
|
|
46374
|
-
if (depth > (vendor ?
|
|
46557
|
+
if (depth > (vendor ? 32 : 3))
|
|
46375
46558
|
return { ...scan, complete: false };
|
|
46376
|
-
const children =
|
|
46559
|
+
const children = [], directory = opendirSync3(path);
|
|
46560
|
+
try {
|
|
46561
|
+
let child;
|
|
46562
|
+
while ((child = directory.readSync()) !== null) {
|
|
46563
|
+
admit(join15(path, child.name));
|
|
46564
|
+
children.push(child);
|
|
46565
|
+
}
|
|
46566
|
+
} finally {
|
|
46567
|
+
directory.closeSync();
|
|
46568
|
+
}
|
|
46569
|
+
children.sort((a3, b3) => a3.name < b3.name ? -1 : a3.name > b3.name ? 1 : 0);
|
|
46377
46570
|
if (pluginCache)
|
|
46378
46571
|
children.sort((a3, b3) => Number(a3.isSymbolicLink()) - Number(b3.isSymbolicLink()));
|
|
46379
46572
|
for (const { name } of children) {
|
|
@@ -46384,15 +46577,15 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
46384
46577
|
const isVendor = vendor || name === ".system";
|
|
46385
46578
|
if (isVendor && !options.includeVendor)
|
|
46386
46579
|
continue;
|
|
46387
|
-
const child =
|
|
46388
|
-
if (isVendor &&
|
|
46580
|
+
const child = join15(path, name);
|
|
46581
|
+
if (isVendor && lstatSync9(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
46389
46582
|
const target = statSync3(child, { throwIfNoEntry: false });
|
|
46390
46583
|
if (target?.isFile() || pluginCache && target?.isDirectory() && emptySiblingCacheAlias(child, path)) {
|
|
46391
46584
|
scan.entries++;
|
|
46392
46585
|
continue;
|
|
46393
46586
|
}
|
|
46394
46587
|
}
|
|
46395
|
-
const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache);
|
|
46588
|
+
const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true);
|
|
46396
46589
|
scan.complete &&= childScan.complete;
|
|
46397
46590
|
scan.hasSkills ||= childScan.hasSkills;
|
|
46398
46591
|
scan.entries += childScan.entries;
|
|
@@ -46411,8 +46604,8 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
46411
46604
|
scanRoot(agent, path);
|
|
46412
46605
|
if (options.includeVendor) {
|
|
46413
46606
|
for (const agent of ["codex", "claude"])
|
|
46414
|
-
scanRoot(agent, canonicalAgentPath(
|
|
46415
|
-
scanRoot("gemini",
|
|
46607
|
+
scanRoot(agent, canonicalAgentPath(join15(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
46608
|
+
scanRoot("gemini", join15(home, ".gemini", "extensions"), true);
|
|
46416
46609
|
}
|
|
46417
46610
|
const configured = options.configured ? INTEGRATION_AGENTS.flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
|
|
46418
46611
|
for (const root3 of [...options.agentRoots ?? [], ...configured])
|
|
@@ -46510,12 +46703,12 @@ function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
|
46510
46703
|
let result = text2;
|
|
46511
46704
|
const selections2 = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
46512
46705
|
for (const skill of selections2) {
|
|
46513
|
-
const path =
|
|
46706
|
+
const path = join15(skill.path, "SKILL.md");
|
|
46514
46707
|
let found = false;
|
|
46515
46708
|
result = result.replace(/^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm, (section) => {
|
|
46516
46709
|
const parsed = Bun.TOML.parse(section);
|
|
46517
46710
|
const declared = parsed.skills?.config?.[0]?.path;
|
|
46518
|
-
if (!declared || ![
|
|
46711
|
+
if (!declared || ![resolve7(path), resolve7(skill.path)].includes(canonicalAgentPath(declared, aliases)))
|
|
46519
46712
|
return section;
|
|
46520
46713
|
found = true;
|
|
46521
46714
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -46539,7 +46732,7 @@ function planAgentIntegration(options) {
|
|
|
46539
46732
|
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) || profileId.includes(".."))
|
|
46540
46733
|
throw new Error("Invalid selection profile id");
|
|
46541
46734
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
46542
|
-
const policyPath =
|
|
46735
|
+
const policyPath = join15(dataDir2, "agent-policy.json");
|
|
46543
46736
|
assertSafePath(policyPath);
|
|
46544
46737
|
const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
|
|
46545
46738
|
if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
|
|
@@ -46556,19 +46749,19 @@ function planAgentIntegration(options) {
|
|
|
46556
46749
|
if (!INTEGRATION_AGENTS.includes(agent))
|
|
46557
46750
|
throw new Error(`Unsupported agent: ${agent}`);
|
|
46558
46751
|
const adapter = AGENT_ADAPTERS[agent];
|
|
46559
|
-
const bridgePath = canonicalAgentPath(
|
|
46752
|
+
const bridgePath = canonicalAgentPath(join15(home, adapter.root, CLI_BRIDGE_NAME), aliases);
|
|
46560
46753
|
assertSafePath(bridgePath);
|
|
46561
46754
|
if (existsSync5(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
|
|
46562
46755
|
throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
|
|
46563
46756
|
for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
|
|
46564
|
-
const path2 =
|
|
46757
|
+
const path2 = join15(bridgePath, name), before2 = readOptional(path2);
|
|
46565
46758
|
if (before2 !== after2)
|
|
46566
46759
|
changes.push({ path: path2, before: before2, after: after2 });
|
|
46567
46760
|
}
|
|
46568
|
-
const path = canonicalAgentPath(
|
|
46761
|
+
const path = canonicalAgentPath(join15(home, adapter.config), aliases);
|
|
46569
46762
|
const before = readOptional(path);
|
|
46570
46763
|
if (agent === "hermes") {
|
|
46571
|
-
const supervisorPath =
|
|
46764
|
+
const supervisorPath = join15(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(options.command ?? "skills", profileId);
|
|
46572
46765
|
const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
|
|
46573
46766
|
const previous = policy.bridge?.supervisors?.hermes;
|
|
46574
46767
|
if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
|
|
@@ -46579,7 +46772,7 @@ function planAgentIntegration(options) {
|
|
|
46579
46772
|
const after2 = configureHermesHooks(before, supervisor, previous);
|
|
46580
46773
|
if (before !== after2)
|
|
46581
46774
|
changes.push({ path, before, after: after2 });
|
|
46582
|
-
const optOut =
|
|
46775
|
+
const optOut = join15(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
|
|
46583
46776
|
if (optOutBefore === null)
|
|
46584
46777
|
changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
|
|
46585
46778
|
` });
|
|
@@ -46591,7 +46784,7 @@ function planAgentIntegration(options) {
|
|
|
46591
46784
|
if (!permission || typeof permission !== "object" || Array.isArray(permission))
|
|
46592
46785
|
throw new Error("Expected OpenCode permission object");
|
|
46593
46786
|
config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
|
|
46594
|
-
const pluginPath =
|
|
46787
|
+
const pluginPath = join15(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
|
|
46595
46788
|
const pluginAfter = renderOpenCodePlugin(options.command ?? "skills", profileId);
|
|
46596
46789
|
const priorCommand = policy.bridge?.commands?.opencode;
|
|
46597
46790
|
const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
|
|
@@ -46608,7 +46801,7 @@ function planAgentIntegration(options) {
|
|
|
46608
46801
|
if (before !== after)
|
|
46609
46802
|
changes.push({ path, before, after });
|
|
46610
46803
|
if (agent === "codex") {
|
|
46611
|
-
const configPath = canonicalAgentPath(
|
|
46804
|
+
const configPath = canonicalAgentPath(join15(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
|
|
46612
46805
|
const next = disableCodexSkills(previous ?? "", nativeSkills, aliases, bridgePath);
|
|
46613
46806
|
if (next !== (previous ?? ""))
|
|
46614
46807
|
changes.push({ path: configPath, before: previous, after: next });
|
|
@@ -46621,12 +46814,12 @@ function planAgentIntegration(options) {
|
|
|
46621
46814
|
version: CLI_BRIDGE_VERSION,
|
|
46622
46815
|
digest: CLI_BRIDGE_DIGEST,
|
|
46623
46816
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
46624
|
-
home:
|
|
46817
|
+
home: resolve7(home),
|
|
46625
46818
|
includeVendor: true,
|
|
46626
|
-
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path:
|
|
46819
|
+
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join15(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha(renderHermesSupervisor(options.command ?? "skills", profileId)) } } } : {},
|
|
46627
46820
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries(options.agents.map((agent) => [agent, options.command ?? "skills"])) },
|
|
46628
46821
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, options.agents.includes(agent) ? profileId : policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
46629
|
-
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(
|
|
46822
|
+
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join15(home, ".codex", "skills", ".system"), aliases) + sep2)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
46630
46823
|
rootAliases: aliases,
|
|
46631
46824
|
discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
|
|
46632
46825
|
} };
|
|
@@ -46638,7 +46831,7 @@ function planAgentIntegration(options) {
|
|
|
46638
46831
|
}
|
|
46639
46832
|
function atomicWrite2(path, content) {
|
|
46640
46833
|
assertSafePath(path);
|
|
46641
|
-
mkdirSync5(
|
|
46834
|
+
mkdirSync5(dirname11(path), { recursive: true, mode: 448 });
|
|
46642
46835
|
const temporary = `${path}.skills-${randomUUID6()}`;
|
|
46643
46836
|
try {
|
|
46644
46837
|
writeFileSync3(temporary, content, { mode: 384, flag: "wx" });
|
|
@@ -46649,12 +46842,12 @@ function atomicWrite2(path, content) {
|
|
|
46649
46842
|
}
|
|
46650
46843
|
}
|
|
46651
46844
|
function applyAgentIntegration(plan) {
|
|
46652
|
-
const policyPath =
|
|
46845
|
+
const policyPath = join15(plan.dataDir, "agent-policy.json");
|
|
46653
46846
|
assertSafePath(policyPath);
|
|
46654
|
-
const currentText = (path) =>
|
|
46847
|
+
const currentText = (path) => resolve7(path) === resolve7(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
|
|
46655
46848
|
readManagedSkillPolicySnapshot(plan.dataDir);
|
|
46656
46849
|
for (const change of plan.changes)
|
|
46657
|
-
if (
|
|
46850
|
+
if (resolve7(change.path) === resolve7(policyPath))
|
|
46658
46851
|
parseManagedSkillPolicy(change.after);
|
|
46659
46852
|
const aliases = plan.rootAliases ?? [];
|
|
46660
46853
|
recheckRootAliases(aliases);
|
|
@@ -46665,7 +46858,7 @@ function applyAgentIntegration(plan) {
|
|
|
46665
46858
|
for (const change of plan.changes)
|
|
46666
46859
|
if (currentText(change.path) !== change.before)
|
|
46667
46860
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
46668
|
-
const backupRoot =
|
|
46861
|
+
const backupRoot = join15(plan.dataDir, "migration", randomUUID6()), backups = [], written = [], createdDirectories = new Set;
|
|
46669
46862
|
if (!plan.changes.length) {
|
|
46670
46863
|
recheckRootAliases(aliases);
|
|
46671
46864
|
return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
@@ -46675,7 +46868,7 @@ function applyAgentIntegration(plan) {
|
|
|
46675
46868
|
chmodSync(backupRoot, 448);
|
|
46676
46869
|
for (const [index, change] of plan.changes.entries())
|
|
46677
46870
|
if (change.before !== null) {
|
|
46678
|
-
const backup =
|
|
46871
|
+
const backup = join15(backupRoot, `${index}-${sha(change.path).slice(0, 12)}.backup`);
|
|
46679
46872
|
writeFileSync3(backup, change.before, { mode: 384, flag: "wx" });
|
|
46680
46873
|
backups.push(backup);
|
|
46681
46874
|
}
|
|
@@ -46684,7 +46877,7 @@ function applyAgentIntegration(plan) {
|
|
|
46684
46877
|
recheckRootAliases(aliases);
|
|
46685
46878
|
if (currentText(change.path) !== change.before)
|
|
46686
46879
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
46687
|
-
for (let directory =
|
|
46880
|
+
for (let directory = dirname11(change.path);!existsSync5(directory); directory = dirname11(directory))
|
|
46688
46881
|
createdDirectories.add(directory);
|
|
46689
46882
|
const after = change.after;
|
|
46690
46883
|
atomicWrite2(change.path, after);
|
|
@@ -46693,7 +46886,7 @@ function applyAgentIntegration(plan) {
|
|
|
46693
46886
|
recheckRootAliases(aliases);
|
|
46694
46887
|
for (const binding of plan.discoveryAfter ?? [])
|
|
46695
46888
|
verifyAgentDiscovery(binding);
|
|
46696
|
-
atomicWrite2(
|
|
46889
|
+
atomicWrite2(join15(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha(change.before), afterHash: sha(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
|
46697
46890
|
`);
|
|
46698
46891
|
} catch (error) {
|
|
46699
46892
|
for (const change of written.reverse()) {
|
|
@@ -46724,11 +46917,11 @@ function applyAgentIntegration(plan) {
|
|
|
46724
46917
|
}
|
|
46725
46918
|
function syncArchiveDirectory(path) {
|
|
46726
46919
|
assertSafePath(path);
|
|
46727
|
-
const descriptor =
|
|
46920
|
+
const descriptor = openSync6(path, constants5.O_RDONLY | constants5.O_DIRECTORY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK);
|
|
46728
46921
|
try {
|
|
46729
46922
|
fsyncSync(descriptor);
|
|
46730
46923
|
} finally {
|
|
46731
|
-
|
|
46924
|
+
closeSync6(descriptor);
|
|
46732
46925
|
}
|
|
46733
46926
|
}
|
|
46734
46927
|
function writeArchiveJournal(path, value) {
|
|
@@ -46736,18 +46929,18 @@ function writeArchiveJournal(path, value) {
|
|
|
46736
46929
|
const temporary = `${path}.skills-${randomUUID6()}`;
|
|
46737
46930
|
let descriptor;
|
|
46738
46931
|
try {
|
|
46739
|
-
descriptor =
|
|
46932
|
+
descriptor = openSync6(temporary, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, 384);
|
|
46740
46933
|
writeFileSync3(descriptor, `${JSON.stringify(value)}
|
|
46741
46934
|
`);
|
|
46742
46935
|
fsyncSync(descriptor);
|
|
46743
|
-
|
|
46936
|
+
closeSync6(descriptor);
|
|
46744
46937
|
descriptor = undefined;
|
|
46745
46938
|
renameSync2(temporary, path);
|
|
46746
|
-
syncArchiveDirectory(
|
|
46939
|
+
syncArchiveDirectory(dirname11(path));
|
|
46747
46940
|
} finally {
|
|
46748
46941
|
if (descriptor !== undefined)
|
|
46749
|
-
|
|
46750
|
-
if (
|
|
46942
|
+
closeSync6(descriptor);
|
|
46943
|
+
if (lstatSync9(temporary, { throwIfNoEntry: false }))
|
|
46751
46944
|
unlinkSync2(temporary);
|
|
46752
46945
|
}
|
|
46753
46946
|
}
|
|
@@ -46758,7 +46951,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
46758
46951
|
recheckRootAliases(aliases);
|
|
46759
46952
|
for (const entry of inventory.filter((entry2) => entry2.bridge)) {
|
|
46760
46953
|
const adapter = AGENT_ADAPTERS[entry.agent];
|
|
46761
|
-
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(
|
|
46954
|
+
const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join15(entry.bridgeHome, adapter.root, CLI_BRIDGE_NAME), aliases) : undefined;
|
|
46762
46955
|
if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
|
|
46763
46956
|
throw new Error(`Skills bridge changed after planning: ${entry.path}`);
|
|
46764
46957
|
}
|
|
@@ -46768,11 +46961,11 @@ function archiveNativeSkills(inventory, options) {
|
|
|
46768
46961
|
throw new Error(`Native skill changed after planning: ${entry.path}`);
|
|
46769
46962
|
if (!selected.length)
|
|
46770
46963
|
return { entries: [], ...aliases.length ? { rootAliases: aliases } : {} };
|
|
46771
|
-
const operationId = randomUUID6(), archiveRoot =
|
|
46964
|
+
const operationId = randomUUID6(), archiveRoot = join15(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join15(archiveRoot, "receipt.json");
|
|
46772
46965
|
const entries = selected.map((entry, index) => ({
|
|
46773
|
-
source: entry.vendor ?
|
|
46774
|
-
archive:
|
|
46775
|
-
hash: entry.vendor ? sha(readNativeBytes(
|
|
46966
|
+
source: entry.vendor ? join15(entry.path, "SKILL.md") : entry.path,
|
|
46967
|
+
archive: join15(archiveRoot, `${index}-${sha(entry.path).slice(0, 12)}`),
|
|
46968
|
+
hash: entry.vendor ? sha(readNativeBytes(join15(entry.path, "SKILL.md"))) : entry.hash,
|
|
46776
46969
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
46777
46970
|
status: "planned"
|
|
46778
46971
|
}));
|
|
@@ -46780,7 +46973,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
46780
46973
|
const moved = [];
|
|
46781
46974
|
const verifyArchive = (entry) => {
|
|
46782
46975
|
assertSafePath(entry.archive);
|
|
46783
|
-
const stat =
|
|
46976
|
+
const stat = lstatSync9(entry.archive);
|
|
46784
46977
|
if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
|
|
46785
46978
|
throw new Error("Archive type changed");
|
|
46786
46979
|
if ((entry.discoveryOnly ? sha(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
@@ -46788,14 +46981,14 @@ function archiveNativeSkills(inventory, options) {
|
|
|
46788
46981
|
};
|
|
46789
46982
|
assertSafePath(archiveRoot);
|
|
46790
46983
|
const created = [];
|
|
46791
|
-
for (let path = archiveRoot;!existsSync5(path); path =
|
|
46984
|
+
for (let path = archiveRoot;!existsSync5(path); path = dirname11(path))
|
|
46792
46985
|
created.push(path);
|
|
46793
46986
|
mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
|
|
46794
46987
|
try {
|
|
46795
46988
|
for (const directory of created)
|
|
46796
46989
|
syncArchiveDirectory(directory);
|
|
46797
46990
|
if (created.length)
|
|
46798
|
-
syncArchiveDirectory(
|
|
46991
|
+
syncArchiveDirectory(dirname11(created.at(-1)));
|
|
46799
46992
|
writeArchiveJournal(receiptPath, journal);
|
|
46800
46993
|
for (const [index, entry] of selected.entries()) {
|
|
46801
46994
|
recheckRootAliases(aliases);
|
|
@@ -46807,7 +47000,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
46807
47000
|
writeArchiveJournal(receiptPath, journal);
|
|
46808
47001
|
renameSync2(move.source, move.archive);
|
|
46809
47002
|
moved.push(move);
|
|
46810
|
-
syncArchiveDirectory(
|
|
47003
|
+
syncArchiveDirectory(dirname11(move.source));
|
|
46811
47004
|
syncArchiveDirectory(archiveRoot);
|
|
46812
47005
|
verifyArchive(move);
|
|
46813
47006
|
move.status = "archived";
|
|
@@ -46828,7 +47021,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
46828
47021
|
recheckRootAliases(aliases);
|
|
46829
47022
|
assertSafePath(entry.source);
|
|
46830
47023
|
assertSafePath(entry.archive);
|
|
46831
|
-
if (
|
|
47024
|
+
if (lstatSync9(entry.source, { throwIfNoEntry: false })) {
|
|
46832
47025
|
entry.status = "recovery-required";
|
|
46833
47026
|
entry.conflict = "source-occupied";
|
|
46834
47027
|
continue;
|
|
@@ -46842,10 +47035,10 @@ function archiveNativeSkills(inventory, options) {
|
|
|
46842
47035
|
}
|
|
46843
47036
|
if (entry.discoveryOnly) {
|
|
46844
47037
|
linkSync(entry.archive, entry.source);
|
|
46845
|
-
syncArchiveDirectory(
|
|
47038
|
+
syncArchiveDirectory(dirname11(entry.source));
|
|
46846
47039
|
unlinkSync2(entry.archive);
|
|
46847
47040
|
} else {
|
|
46848
|
-
if (
|
|
47041
|
+
if (lstatSync9(entry.source, { throwIfNoEntry: false })) {
|
|
46849
47042
|
entry.status = "recovery-required";
|
|
46850
47043
|
entry.conflict = "source-occupied";
|
|
46851
47044
|
continue;
|
|
@@ -46853,7 +47046,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
46853
47046
|
renameSync2(entry.archive, entry.source);
|
|
46854
47047
|
}
|
|
46855
47048
|
entry.status = "restored";
|
|
46856
|
-
syncArchiveDirectory(
|
|
47049
|
+
syncArchiveDirectory(dirname11(entry.source));
|
|
46857
47050
|
syncArchiveDirectory(archiveRoot);
|
|
46858
47051
|
} catch {
|
|
46859
47052
|
entry.status = "recovery-required";
|
|
@@ -46890,7 +47083,7 @@ import {
|
|
|
46890
47083
|
statSync as statSync8,
|
|
46891
47084
|
writeFileSync as writeFileSync5
|
|
46892
47085
|
} from "fs";
|
|
46893
|
-
import { basename as basename2, dirname as
|
|
47086
|
+
import { basename as basename2, dirname as dirname13, isAbsolute as isAbsolute9, join as join20, normalize as normalize2, relative as relative4, resolve as resolve8, sep as sep4 } from "path";
|
|
46894
47087
|
|
|
46895
47088
|
// src/lib/registry-data/development-tools.ts
|
|
46896
47089
|
var DEVELOPMENT_TOOLS_SKILLS = [
|
|
@@ -47603,7 +47796,7 @@ var SKILLS = [
|
|
|
47603
47796
|
|
|
47604
47797
|
// src/lib/hosted-skill-set.ts
|
|
47605
47798
|
import { existsSync as existsSync6, readFileSync as readFileSync10, readdirSync as readdirSync7, statSync as statSync4 } from "fs";
|
|
47606
|
-
import { join as
|
|
47799
|
+
import { join as join16 } from "path";
|
|
47607
47800
|
var HOSTED_RUNTIMES = new Set(["hosted"]);
|
|
47608
47801
|
var HOSTED_SOURCES = new Set(["remote", "private-hosted"]);
|
|
47609
47802
|
function normalizeMarker(value) {
|
|
@@ -47616,7 +47809,7 @@ function isHostedMetadataPackage(pkg) {
|
|
|
47616
47809
|
return HOSTED_RUNTIMES.has(normalizeMarker(skills.runtime)) || HOSTED_SOURCES.has(normalizeMarker(skills.source));
|
|
47617
47810
|
}
|
|
47618
47811
|
function isHostedMetadataSkillDir(skillDir) {
|
|
47619
|
-
const pkgPath =
|
|
47812
|
+
const pkgPath = join16(skillDir, "package.json");
|
|
47620
47813
|
if (!existsSync6(pkgPath))
|
|
47621
47814
|
return false;
|
|
47622
47815
|
try {
|
|
@@ -47641,8 +47834,8 @@ var BRACE_SOURCE_EXCLUSION = new RegExp(`^!skills/\\{(${SLUG}(?:,${SLUG})+)\\}/s
|
|
|
47641
47834
|
var SINGLE_SOURCE_EXCLUSION = new RegExp(`^!skills/(${SLUG})/src$`);
|
|
47642
47835
|
|
|
47643
47836
|
// src/lib/skill-validation.ts
|
|
47644
|
-
import { existsSync as existsSync7, lstatSync as
|
|
47645
|
-
import { isAbsolute as
|
|
47837
|
+
import { existsSync as existsSync7, lstatSync as lstatSync10, readFileSync as readFileSync11, readdirSync as readdirSync8, statSync as statSync5 } from "fs";
|
|
47838
|
+
import { isAbsolute as isAbsolute8, join as join17, normalize } from "path";
|
|
47646
47839
|
var VALID_SKILL_KINDS = ["executable", "instruction"];
|
|
47647
47840
|
var DOC_FILES = ["SKILL.md", "README.md", "CLAUDE.md"];
|
|
47648
47841
|
var RESERVED_SKILL_ENTRIES = new Set([
|
|
@@ -47705,7 +47898,7 @@ function sortMessages(messages) {
|
|
|
47705
47898
|
return [...messages].sort((a3, b3) => a3.code.localeCompare(b3.code) || a3.message.localeCompare(b3.message));
|
|
47706
47899
|
}
|
|
47707
47900
|
function isSafeRelativePath(value) {
|
|
47708
|
-
if (!value.trim() ||
|
|
47901
|
+
if (!value.trim() || isAbsolute8(value))
|
|
47709
47902
|
return false;
|
|
47710
47903
|
const normalized = normalize(value).replace(/\\/g, "/");
|
|
47711
47904
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
@@ -47805,11 +47998,11 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
47805
47998
|
add(issues, "skill.name_invalid", `Skill name '${bareName}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
|
|
47806
47999
|
}
|
|
47807
48000
|
for (const entry of readdirSync8(skillPath).sort()) {
|
|
47808
|
-
const entryPath =
|
|
48001
|
+
const entryPath = join17(skillPath, entry);
|
|
47809
48002
|
if (RESERVED_SKILL_ENTRIES.has(entry)) {
|
|
47810
48003
|
add(issues, "skill.reserved_file", `Reserved file '${entry}' is not allowed in skill packages`);
|
|
47811
48004
|
}
|
|
47812
|
-
if (
|
|
48005
|
+
if (lstatSync10(entryPath).isSymbolicLink()) {
|
|
47813
48006
|
add(issues, "skill.symlink_forbidden", `Symlink '${entry}' is not allowed in skill packages`);
|
|
47814
48007
|
}
|
|
47815
48008
|
if (!KNOWN_TOP_LEVEL_ENTRIES.has(entry)) {
|
|
@@ -47817,13 +48010,13 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
47817
48010
|
}
|
|
47818
48011
|
}
|
|
47819
48012
|
for (const docFile of DOC_FILES) {
|
|
47820
|
-
if (existsSync7(
|
|
48013
|
+
if (existsSync7(join17(skillPath, docFile)))
|
|
47821
48014
|
metadata.docFiles.push(docFile);
|
|
47822
48015
|
}
|
|
47823
48016
|
if (metadata.docFiles.length === 0) {
|
|
47824
48017
|
add(issues, "skill.docs_missing", "Missing documentation file: expected SKILL.md, README.md, or CLAUDE.md");
|
|
47825
48018
|
}
|
|
47826
|
-
const skillMdPath =
|
|
48019
|
+
const skillMdPath = join17(skillPath, "SKILL.md");
|
|
47827
48020
|
if (existsSync7(skillMdPath)) {
|
|
47828
48021
|
const frontmatter = parseSkillFrontmatter(readFileSync11(skillMdPath, "utf-8"));
|
|
47829
48022
|
if (!frontmatter) {
|
|
@@ -47867,7 +48060,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
47867
48060
|
}
|
|
47868
48061
|
metadata.kind = resolvedKind;
|
|
47869
48062
|
const isInstruction = resolvedKind === "instruction";
|
|
47870
|
-
const pkgPath =
|
|
48063
|
+
const pkgPath = join17(skillPath, "package.json");
|
|
47871
48064
|
if (!existsSync7(pkgPath)) {
|
|
47872
48065
|
if (!isInstruction)
|
|
47873
48066
|
add(issues, "package.missing", "Missing package.json");
|
|
@@ -47926,7 +48119,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
47926
48119
|
add(issues, "package.bin_target_unsafe", `package.json bin '${command3}' target '${target}' must stay inside the skill directory`);
|
|
47927
48120
|
continue;
|
|
47928
48121
|
}
|
|
47929
|
-
const targetPath =
|
|
48122
|
+
const targetPath = join17(skillPath, target);
|
|
47930
48123
|
if (!existsSync7(targetPath)) {
|
|
47931
48124
|
add(warnings, "package.bin_target_missing", `package.json bin '${command3}' target '${target}' is not present before build`);
|
|
47932
48125
|
} else if (statSync5(targetPath).isDirectory()) {
|
|
@@ -47944,17 +48137,17 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
47944
48137
|
metadata.runtime = "none";
|
|
47945
48138
|
} else {
|
|
47946
48139
|
metadata.runtime = hostedMetadata ? "hosted" : "local";
|
|
47947
|
-
const srcDir =
|
|
48140
|
+
const srcDir = join17(skillPath, "src");
|
|
47948
48141
|
if (hostedMetadata) {
|
|
47949
48142
|
if (existsSync7(srcDir)) {
|
|
47950
48143
|
add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
|
|
47951
48144
|
}
|
|
47952
48145
|
} else if (!existsSync7(srcDir)) {
|
|
47953
48146
|
add(issues, "skill.src_missing", "Missing src/ directory");
|
|
47954
|
-
} else if (!existsSync7(
|
|
48147
|
+
} else if (!existsSync7(join17(srcDir, "index.ts")) && !existsSync7(join17(srcDir, "index.js"))) {
|
|
47955
48148
|
add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
|
|
47956
48149
|
} else {
|
|
47957
|
-
const indexPath = existsSync7(
|
|
48150
|
+
const indexPath = existsSync7(join17(srcDir, "index.ts")) ? join17(srcDir, "index.ts") : join17(srcDir, "index.js");
|
|
47958
48151
|
const size = statSync5(indexPath).size;
|
|
47959
48152
|
if (size < 50)
|
|
47960
48153
|
add(warnings, "skill.src_index_minimal", `Source entry point is very small (${size}B)`);
|
|
@@ -47979,7 +48172,7 @@ function validateRegistryConsistency(registry, skillsDir) {
|
|
|
47979
48172
|
return false;
|
|
47980
48173
|
})));
|
|
47981
48174
|
const skillDirs = existsSync7(skillsDir) ? readdirSync8(skillsDir).filter((entry) => {
|
|
47982
|
-
const fullPath =
|
|
48175
|
+
const fullPath = join17(skillsDir, entry);
|
|
47983
48176
|
return !entry.startsWith(".") && entry !== "_common" && statSync5(fullPath).isDirectory();
|
|
47984
48177
|
}) : [];
|
|
47985
48178
|
const directoryNames = new Set(skillDirs);
|
|
@@ -47995,9 +48188,9 @@ function validateRegistryConsistency(registry, skillsDir) {
|
|
|
47995
48188
|
}
|
|
47996
48189
|
|
|
47997
48190
|
// src/lib/skill-hash.ts
|
|
47998
|
-
import { createHash as
|
|
48191
|
+
import { createHash as createHash13 } from "crypto";
|
|
47999
48192
|
import { existsSync as existsSync8, readdirSync as readdirSync9, readFileSync as readFileSync12, statSync as statSync6 } from "fs";
|
|
48000
|
-
import { join as
|
|
48193
|
+
import { join as join18, sep as sep3 } from "path";
|
|
48001
48194
|
var CONTENT_HASH_ALGORITHM = "sha256";
|
|
48002
48195
|
var HASH_EXCLUDE_DIRS = new Set([".git", "node_modules", "dist", "build", ".turbo"]);
|
|
48003
48196
|
function excludedHashEntry(name, directory) {
|
|
@@ -48059,7 +48252,7 @@ function collectBundleFiles(skillPath) {
|
|
|
48059
48252
|
if (seen.has(entry))
|
|
48060
48253
|
continue;
|
|
48061
48254
|
seen.add(entry);
|
|
48062
|
-
const absolute =
|
|
48255
|
+
const absolute = join18(skillPath, entry);
|
|
48063
48256
|
if (!existsSync8(absolute))
|
|
48064
48257
|
continue;
|
|
48065
48258
|
if (statSync6(absolute).isDirectory())
|
|
@@ -48073,7 +48266,7 @@ function collectDirectory(files, dir, rel) {
|
|
|
48073
48266
|
for (const entry of readdirSync9(dir).sort()) {
|
|
48074
48267
|
if (entry.startsWith("."))
|
|
48075
48268
|
continue;
|
|
48076
|
-
const absolute =
|
|
48269
|
+
const absolute = join18(dir, entry);
|
|
48077
48270
|
const childRel = `${rel}/${entry}`;
|
|
48078
48271
|
let stats;
|
|
48079
48272
|
try {
|
|
@@ -48119,13 +48312,13 @@ function* bundleHashParts(files) {
|
|
|
48119
48312
|
yield new TextEncoder().encode("\x00");
|
|
48120
48313
|
}
|
|
48121
48314
|
function hashBundleFiles(files) {
|
|
48122
|
-
const hash2 =
|
|
48315
|
+
const hash2 = createHash13(CONTENT_HASH_ALGORITHM);
|
|
48123
48316
|
for (const part of bundleHashParts(files))
|
|
48124
48317
|
hash2.update(part);
|
|
48125
48318
|
return hash2.digest("hex");
|
|
48126
48319
|
}
|
|
48127
48320
|
async function hashBundleFilesCooperatively(files, check) {
|
|
48128
|
-
const hash2 =
|
|
48321
|
+
const hash2 = createHash13(CONTENT_HASH_ALGORITHM);
|
|
48129
48322
|
let bytesSinceYield = 0;
|
|
48130
48323
|
for (const part of bundleHashParts(files)) {
|
|
48131
48324
|
for (let offset = 0;offset < part.byteLength; offset += 64 * 1024) {
|
|
@@ -48134,7 +48327,7 @@ async function hashBundleFilesCooperatively(files, check) {
|
|
|
48134
48327
|
hash2.update(chunk);
|
|
48135
48328
|
bytesSinceYield += chunk.byteLength;
|
|
48136
48329
|
if (bytesSinceYield >= 256 * 1024) {
|
|
48137
|
-
await new Promise((
|
|
48330
|
+
await new Promise((resolve8) => setImmediate(resolve8));
|
|
48138
48331
|
bytesSinceYield = 0;
|
|
48139
48332
|
}
|
|
48140
48333
|
}
|
|
@@ -48287,7 +48480,7 @@ async function hashContentEntries(entries, options) {
|
|
|
48287
48480
|
const normalized = [];
|
|
48288
48481
|
let normalizedBytes = 0;
|
|
48289
48482
|
let manifest;
|
|
48290
|
-
await new Promise((
|
|
48483
|
+
await new Promise((resolve8) => setImmediate(resolve8));
|
|
48291
48484
|
for (const entry of snapshot) {
|
|
48292
48485
|
check();
|
|
48293
48486
|
if (!coveredContentPath(entry.path))
|
|
@@ -48300,7 +48493,7 @@ async function hashContentEntries(entries, options) {
|
|
|
48300
48493
|
contentLimit("Content hash normalized byte limit exceeded");
|
|
48301
48494
|
normalizedBytes += file.content.byteLength;
|
|
48302
48495
|
normalized.push(file);
|
|
48303
|
-
await new Promise((
|
|
48496
|
+
await new Promise((resolve8) => setImmediate(resolve8));
|
|
48304
48497
|
}
|
|
48305
48498
|
normalized.sort((a3, b3) => a3.rel < b3.rel ? -1 : a3.rel > b3.rel ? 1 : 0);
|
|
48306
48499
|
check();
|
|
@@ -48508,13 +48701,13 @@ function validateRuntimeContract(manifest, issues, strict) {
|
|
|
48508
48701
|
import {
|
|
48509
48702
|
cpSync,
|
|
48510
48703
|
existsSync as existsSync9,
|
|
48511
|
-
lstatSync as
|
|
48704
|
+
lstatSync as lstatSync11,
|
|
48512
48705
|
mkdirSync as mkdirSync6,
|
|
48513
48706
|
readFileSync as readFileSync13,
|
|
48514
48707
|
realpathSync as realpathSync3,
|
|
48515
48708
|
writeFileSync as writeFileSync4
|
|
48516
48709
|
} from "fs";
|
|
48517
|
-
import { basename, dirname as
|
|
48710
|
+
import { basename, dirname as dirname12, join as join19, relative as relative3 } from "path";
|
|
48518
48711
|
var ANY_SEGMENT_COPY_EXCLUDES = new Set([
|
|
48519
48712
|
".git",
|
|
48520
48713
|
".DS_Store",
|
|
@@ -48571,9 +48764,9 @@ function readPortableSkillManifestForImport(skillPath) {
|
|
|
48571
48764
|
return readManifest(skillPath, basename(skillPath), normalizeNewPortableSkillName);
|
|
48572
48765
|
}
|
|
48573
48766
|
function readManifest(skillPath, fallbackName, normalizeName) {
|
|
48574
|
-
const skillJsonPath =
|
|
48575
|
-
const skillMdPath =
|
|
48576
|
-
const pkgPath =
|
|
48767
|
+
const skillJsonPath = join19(skillPath, "skill.json");
|
|
48768
|
+
const skillMdPath = join19(skillPath, "SKILL.md");
|
|
48769
|
+
const pkgPath = join19(skillPath, "package.json");
|
|
48577
48770
|
const jsonManifest = existsSync9(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
48578
48771
|
const frontmatter = existsSync9(skillMdPath) ? parseSkillFrontmatter(readFileSync13(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
48579
48772
|
const pkg = existsSync9(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
@@ -48604,9 +48797,9 @@ function parseSkillKind(value) {
|
|
|
48604
48797
|
return;
|
|
48605
48798
|
}
|
|
48606
48799
|
function readDeclaredSkillVersion(skillPath) {
|
|
48607
|
-
const skillJsonPath =
|
|
48608
|
-
const skillMdPath =
|
|
48609
|
-
const pkgPath =
|
|
48800
|
+
const skillJsonPath = join19(skillPath, "skill.json");
|
|
48801
|
+
const skillMdPath = join19(skillPath, "SKILL.md");
|
|
48802
|
+
const pkgPath = join19(skillPath, "package.json");
|
|
48610
48803
|
const jsonManifest = existsSync9(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
48611
48804
|
const frontmatter = existsSync9(skillMdPath) ? parseSkillFrontmatter(readFileSync13(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
48612
48805
|
const pkg = existsSync9(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
@@ -48631,7 +48824,7 @@ function createInstructionManifest(name, options) {
|
|
|
48631
48824
|
}
|
|
48632
48825
|
function writeInstructionSkillTemplate(skillPath, manifest) {
|
|
48633
48826
|
mkdirSync6(skillPath, { recursive: true });
|
|
48634
|
-
writeFileSync4(
|
|
48827
|
+
writeFileSync4(join19(skillPath, "SKILL.md"), renderInstructionSkillMd(manifest));
|
|
48635
48828
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
48636
48829
|
}
|
|
48637
48830
|
function renderInstructionSkillMd(manifest) {
|
|
@@ -48681,12 +48874,12 @@ function createPortableManifest(name, options) {
|
|
|
48681
48874
|
};
|
|
48682
48875
|
}
|
|
48683
48876
|
function writePortableSkillTemplate(skillPath, manifest) {
|
|
48684
|
-
mkdirSync6(
|
|
48685
|
-
writeFileSync4(
|
|
48686
|
-
writeFileSync4(
|
|
48687
|
-
writeFileSync4(
|
|
48688
|
-
writeFileSync4(
|
|
48689
|
-
writeFileSync4(
|
|
48877
|
+
mkdirSync6(join19(skillPath, "src"), { recursive: true });
|
|
48878
|
+
writeFileSync4(join19(skillPath, "SKILL.md"), renderSkillMd(manifest));
|
|
48879
|
+
writeFileSync4(join19(skillPath, "AGENTS.md"), renderAgentsMd(manifest));
|
|
48880
|
+
writeFileSync4(join19(skillPath, "package.json"), renderPackageJson(manifest));
|
|
48881
|
+
writeFileSync4(join19(skillPath, "tsconfig.json"), renderTsconfig());
|
|
48882
|
+
writeFileSync4(join19(skillPath, "src", "index.ts"), renderEntrypoint(manifest));
|
|
48690
48883
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
48691
48884
|
}
|
|
48692
48885
|
function fillContractDefaults(manifest, entrypoint) {
|
|
@@ -48711,7 +48904,7 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
48711
48904
|
content_hash: undefined
|
|
48712
48905
|
}
|
|
48713
48906
|
};
|
|
48714
|
-
writeFileSync4(
|
|
48907
|
+
writeFileSync4(join19(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withoutHash) }, null, 2)}
|
|
48715
48908
|
`);
|
|
48716
48909
|
const hash2 = computeContentHash(skillPath);
|
|
48717
48910
|
const withHash = {
|
|
@@ -48721,12 +48914,12 @@ function writeSkillJsonWithHash(skillPath, manifest) {
|
|
|
48721
48914
|
content_hash: hash2
|
|
48722
48915
|
}
|
|
48723
48916
|
};
|
|
48724
|
-
writeFileSync4(
|
|
48917
|
+
writeFileSync4(join19(skillPath, "skill.json"), `${JSON.stringify({ ...existing, ...renderSkillJsonObject(withHash) }, null, 2)}
|
|
48725
48918
|
`);
|
|
48726
48919
|
return withHash;
|
|
48727
48920
|
}
|
|
48728
48921
|
function readExistingSkillJson(skillPath) {
|
|
48729
|
-
const path =
|
|
48922
|
+
const path = join19(skillPath, "skill.json");
|
|
48730
48923
|
if (!existsSync9(path))
|
|
48731
48924
|
return {};
|
|
48732
48925
|
try {
|
|
@@ -48760,24 +48953,24 @@ function ensurePortableSkillFiles(skillPath, manifest) {
|
|
|
48760
48953
|
tags: next.tags?.length ? next.tags : ["custom", next.name]
|
|
48761
48954
|
};
|
|
48762
48955
|
const entry = next.commands[0]?.entry ?? "src/index.ts";
|
|
48763
|
-
if (entry && !existsSync9(
|
|
48764
|
-
mkdirSync6(
|
|
48765
|
-
writeFileSync4(
|
|
48956
|
+
if (entry && !existsSync9(join19(skillPath, entry))) {
|
|
48957
|
+
mkdirSync6(dirname12(join19(skillPath, entry)), { recursive: true });
|
|
48958
|
+
writeFileSync4(join19(skillPath, entry), renderEntrypoint(next));
|
|
48766
48959
|
}
|
|
48767
|
-
if (!existsSync9(
|
|
48768
|
-
writeFileSync4(
|
|
48960
|
+
if (!existsSync9(join19(skillPath, "SKILL.md")))
|
|
48961
|
+
writeFileSync4(join19(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
48769
48962
|
else
|
|
48770
|
-
writeFileSync4(
|
|
48771
|
-
if (!existsSync9(
|
|
48772
|
-
writeFileSync4(
|
|
48963
|
+
writeFileSync4(join19(skillPath, "SKILL.md"), ensureSkillMdFrontmatter(readFileSync13(join19(skillPath, "SKILL.md"), "utf-8"), next));
|
|
48964
|
+
if (!existsSync9(join19(skillPath, "AGENTS.md")))
|
|
48965
|
+
writeFileSync4(join19(skillPath, "AGENTS.md"), renderAgentsMd(next));
|
|
48773
48966
|
ensurePackageJson(skillPath, next);
|
|
48774
|
-
if (!existsSync9(
|
|
48775
|
-
writeFileSync4(
|
|
48967
|
+
if (!existsSync9(join19(skillPath, "tsconfig.json")))
|
|
48968
|
+
writeFileSync4(join19(skillPath, "tsconfig.json"), renderTsconfig());
|
|
48776
48969
|
writeSkillJsonWithHash(skillPath, next);
|
|
48777
48970
|
return readPortableSkillManifest(skillPath, next.name);
|
|
48778
48971
|
}
|
|
48779
48972
|
function ensurePackageJson(skillPath, manifest) {
|
|
48780
|
-
const pkgPath =
|
|
48973
|
+
const pkgPath = join19(skillPath, "package.json");
|
|
48781
48974
|
const first = manifest.commands[0] ?? { name: manifest.name, entry: "src/index.ts" };
|
|
48782
48975
|
const commandName = normalizePortableSkillName(first.name || manifest.name);
|
|
48783
48976
|
const entry = (first.entry ?? "src/index.ts").replace(/^\.\//, "");
|
|
@@ -48824,17 +49017,17 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
48824
49017
|
inputs: [],
|
|
48825
49018
|
commands: []
|
|
48826
49019
|
};
|
|
48827
|
-
if (!existsSync9(
|
|
48828
|
-
writeFileSync4(
|
|
49020
|
+
if (!existsSync9(join19(skillPath, "SKILL.md"))) {
|
|
49021
|
+
writeFileSync4(join19(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
48829
49022
|
} else {
|
|
48830
|
-
const path =
|
|
49023
|
+
const path = join19(skillPath, "SKILL.md");
|
|
48831
49024
|
const content = readFileSync13(path, "utf8");
|
|
48832
49025
|
const declaredName = parseSkillFrontmatter(content)?.name;
|
|
48833
49026
|
if (declaredName && declaredName !== next.name) {
|
|
48834
49027
|
writeFileSync4(path, renameInstructionFrontmatter(content, next.name));
|
|
48835
49028
|
}
|
|
48836
49029
|
}
|
|
48837
|
-
const packagePath =
|
|
49030
|
+
const packagePath = join19(skillPath, "package.json");
|
|
48838
49031
|
if (existsSync9(packagePath)) {
|
|
48839
49032
|
const pkg = readJsonObject(packagePath);
|
|
48840
49033
|
if (typeof pkg.name === "string" && pkg.name !== next.name) {
|
|
@@ -48866,7 +49059,7 @@ function renameInstructionFrontmatter(content, name) {
|
|
|
48866
49059
|
return renamed + content.slice(frontmatter[0].length);
|
|
48867
49060
|
}
|
|
48868
49061
|
function copySkillDirectory(source, destination) {
|
|
48869
|
-
const resolvedSource =
|
|
49062
|
+
const resolvedSource = lstatSync11(source).isSymbolicLink() ? realpathSync3(source) : source;
|
|
48870
49063
|
mkdirSync6(destination, { recursive: true });
|
|
48871
49064
|
cpSync(resolvedSource, destination, {
|
|
48872
49065
|
recursive: true,
|
|
@@ -48879,7 +49072,7 @@ function copySkillDirectory(source, destination) {
|
|
|
48879
49072
|
if (isExcludedCopyEntry(segments[i3], i3 === 0))
|
|
48880
49073
|
return false;
|
|
48881
49074
|
}
|
|
48882
|
-
if (
|
|
49075
|
+
if (lstatSync11(src).isSymbolicLink())
|
|
48883
49076
|
return false;
|
|
48884
49077
|
return true;
|
|
48885
49078
|
}
|
|
@@ -49158,11 +49351,11 @@ function getPortableSkillsRoot(options = {}) {
|
|
|
49158
49351
|
function resolvePortableSkillsRoot(options, migrate) {
|
|
49159
49352
|
if (options.rootDir)
|
|
49160
49353
|
return options.rootDir;
|
|
49161
|
-
const appDir = options.homeDir ?
|
|
49162
|
-
const cache3 =
|
|
49354
|
+
const appDir = options.homeDir ? join20(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
|
|
49355
|
+
const cache3 = join20(appDir, SKILLS_CACHE_DIRNAME);
|
|
49163
49356
|
if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache3))
|
|
49164
49357
|
return cache3;
|
|
49165
|
-
const installed =
|
|
49358
|
+
const installed = join20(appDir, INSTALLED_SKILLS_DIRNAME);
|
|
49166
49359
|
if (migrate)
|
|
49167
49360
|
migrateLegacySkillLayout(appDir, installed);
|
|
49168
49361
|
return installed;
|
|
@@ -49170,7 +49363,7 @@ function resolvePortableSkillsRoot(options, migrate) {
|
|
|
49170
49363
|
function looksLikeSkillDirectory(path) {
|
|
49171
49364
|
if (!safeIsDirectory(path))
|
|
49172
49365
|
return false;
|
|
49173
|
-
return existsSync10(
|
|
49366
|
+
return existsSync10(join20(path, "SKILL.md")) || existsSync10(join20(path, "skill.json")) || existsSync10(join20(path, "package.json"));
|
|
49174
49367
|
}
|
|
49175
49368
|
function migrateLegacySkillLayout(appDir, installed) {
|
|
49176
49369
|
if (!safeIsDirectory(appDir))
|
|
@@ -49180,7 +49373,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
49180
49373
|
for (const entry of readdirSync10(appDir)) {
|
|
49181
49374
|
if (entry.startsWith(".") || entry === INSTALLED_SKILLS_DIRNAME)
|
|
49182
49375
|
continue;
|
|
49183
|
-
const path =
|
|
49376
|
+
const path = join20(appDir, entry);
|
|
49184
49377
|
if (entry === LEGACY_CUSTOM_DIRNAME) {
|
|
49185
49378
|
if (!safeIsDirectory(path))
|
|
49186
49379
|
continue;
|
|
@@ -49188,7 +49381,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
49188
49381
|
for (const nested of readdirSync10(path)) {
|
|
49189
49382
|
if (nested.startsWith("."))
|
|
49190
49383
|
continue;
|
|
49191
|
-
const nestedPath =
|
|
49384
|
+
const nestedPath = join20(path, nested);
|
|
49192
49385
|
if (looksLikeSkillDirectory(nestedPath))
|
|
49193
49386
|
candidates.push({ from: nestedPath, name: nested });
|
|
49194
49387
|
}
|
|
@@ -49202,10 +49395,10 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
49202
49395
|
return;
|
|
49203
49396
|
}
|
|
49204
49397
|
for (const { from, name } of candidates) {
|
|
49205
|
-
const target =
|
|
49398
|
+
const target = join20(installed, name);
|
|
49206
49399
|
if (existsSync10(target))
|
|
49207
49400
|
continue;
|
|
49208
|
-
const staging =
|
|
49401
|
+
const staging = join20(installed, `.migrating-${name}-${process.pid}`);
|
|
49209
49402
|
try {
|
|
49210
49403
|
rmSync(staging, { recursive: true, force: true });
|
|
49211
49404
|
cpSync2(from, staging, { recursive: true, errorOnExist: false });
|
|
@@ -49218,7 +49411,7 @@ function migrateLegacySkillLayout(appDir, installed) {
|
|
|
49218
49411
|
}
|
|
49219
49412
|
}
|
|
49220
49413
|
function getPortableSkillPath(name, options = {}) {
|
|
49221
|
-
return
|
|
49414
|
+
return join20(getPortableSkillsRoot(options), normalizePortableSkillName(name));
|
|
49222
49415
|
}
|
|
49223
49416
|
function findPortableSkill(name, options = {}) {
|
|
49224
49417
|
let normalized;
|
|
@@ -49244,7 +49437,7 @@ function listPortableSkills(options = {}) {
|
|
|
49244
49437
|
for (const entry of readdirSync10(root3).sort()) {
|
|
49245
49438
|
if (entry.startsWith("."))
|
|
49246
49439
|
continue;
|
|
49247
|
-
const path =
|
|
49440
|
+
const path = join20(root3, entry);
|
|
49248
49441
|
if (!safeIsDirectory(path))
|
|
49249
49442
|
continue;
|
|
49250
49443
|
try {
|
|
@@ -49278,7 +49471,7 @@ function isOfficialSkillName(name) {
|
|
|
49278
49471
|
function scaffoldPortableSkill(name, options = {}) {
|
|
49279
49472
|
const skillName = normalizeNewPortableSkillName(name);
|
|
49280
49473
|
const root3 = getPortableSkillsRoot(options);
|
|
49281
|
-
const skillPath =
|
|
49474
|
+
const skillPath = join20(root3, skillName);
|
|
49282
49475
|
if (existsSync10(skillPath)) {
|
|
49283
49476
|
if (!options.overwrite)
|
|
49284
49477
|
throw new Error(`Skill '${skillName}' already exists at ${skillPath}`);
|
|
@@ -49310,7 +49503,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
49310
49503
|
const skipped = [];
|
|
49311
49504
|
const entries = readdirSync10(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
|
|
49312
49505
|
for (const entryName of entries) {
|
|
49313
|
-
const childPath =
|
|
49506
|
+
const childPath = join20(absoluteSource, entryName);
|
|
49314
49507
|
if (!safeIsDirectory(childPath))
|
|
49315
49508
|
continue;
|
|
49316
49509
|
if (!isSkillCandidate(childPath)) {
|
|
@@ -49339,23 +49532,23 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
|
|
|
49339
49532
|
};
|
|
49340
49533
|
}
|
|
49341
49534
|
function isSkillCandidate(dir) {
|
|
49342
|
-
return existsSync10(
|
|
49535
|
+
return existsSync10(join20(dir, "SKILL.md")) || existsSync10(join20(dir, "skill.json")) || existsSync10(join20(dir, "package.json"));
|
|
49343
49536
|
}
|
|
49344
49537
|
function physicalImportPath(path) {
|
|
49345
|
-
let ancestor =
|
|
49538
|
+
let ancestor = resolve8(path);
|
|
49346
49539
|
const suffix = [];
|
|
49347
49540
|
while (!existsSync10(ancestor)) {
|
|
49348
49541
|
suffix.unshift(basename2(ancestor));
|
|
49349
|
-
ancestor =
|
|
49542
|
+
ancestor = dirname13(ancestor);
|
|
49350
49543
|
}
|
|
49351
|
-
return
|
|
49544
|
+
return join20(realpathSync4(ancestor), ...suffix);
|
|
49352
49545
|
}
|
|
49353
49546
|
function assertDisjointImportPaths(source, destination) {
|
|
49354
49547
|
const sourcePath = realpathSync4(source);
|
|
49355
49548
|
const destinationPath = physicalImportPath(destination);
|
|
49356
49549
|
const contains = (parent, child) => {
|
|
49357
49550
|
const path = relative4(parent, child);
|
|
49358
|
-
return path === "" || !
|
|
49551
|
+
return path === "" || !isAbsolute9(path) && path !== ".." && !path.startsWith(`..${sep4}`);
|
|
49359
49552
|
};
|
|
49360
49553
|
if (contains(sourcePath, destinationPath) || contains(destinationPath, sourcePath)) {
|
|
49361
49554
|
throw new Error("Source and destination skill directories must not overlap");
|
|
@@ -49374,16 +49567,16 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
49374
49567
|
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename2(absoluteSource)}')` : ""} matches a bundled official skill.`;
|
|
49375
49568
|
throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
|
|
49376
49569
|
}
|
|
49377
|
-
assertDisjointImportPaths(absoluteSource,
|
|
49570
|
+
assertDisjointImportPaths(absoluteSource, join20(resolvePortableSkillsRoot(options, false), skillName));
|
|
49378
49571
|
const root3 = getPortableSkillsRoot(options);
|
|
49379
|
-
const destination =
|
|
49572
|
+
const destination = join20(root3, skillName);
|
|
49380
49573
|
assertDisjointImportPaths(absoluteSource, destination);
|
|
49381
49574
|
if (existsSync10(destination)) {
|
|
49382
49575
|
if (!options.overwrite)
|
|
49383
49576
|
throw new Error(`Skill '${skillName}' already exists at ${destination}`);
|
|
49384
49577
|
rmSync(destination, { recursive: true, force: true });
|
|
49385
49578
|
}
|
|
49386
|
-
mkdirSync7(
|
|
49579
|
+
mkdirSync7(dirname13(destination), { recursive: true });
|
|
49387
49580
|
copySkillDirectory(absoluteSource, destination);
|
|
49388
49581
|
const base = {
|
|
49389
49582
|
...inferred,
|
|
@@ -49420,10 +49613,10 @@ function buildCorpusManifest(input, name) {
|
|
|
49420
49613
|
function writeCorpusSkill(input, options = {}) {
|
|
49421
49614
|
const name = normalizePortableSkillName(input.name);
|
|
49422
49615
|
const root3 = getPortableSkillsRoot(options);
|
|
49423
|
-
const skillPath =
|
|
49616
|
+
const skillPath = join20(root3, name);
|
|
49424
49617
|
const created = !existsSync10(skillPath);
|
|
49425
49618
|
mkdirSync7(skillPath, { recursive: true });
|
|
49426
|
-
writeFileSync5(
|
|
49619
|
+
writeFileSync5(join20(skillPath, "SKILL.md"), input.skillMd);
|
|
49427
49620
|
const manifest = buildCorpusManifest(input, name);
|
|
49428
49621
|
writeSkillJsonWithHash(skillPath, manifest);
|
|
49429
49622
|
return { name, path: skillPath, manifest, created };
|
|
@@ -49431,19 +49624,19 @@ function writeCorpusSkill(input, options = {}) {
|
|
|
49431
49624
|
function installCorpusSkillAtomically(input, options = {}) {
|
|
49432
49625
|
const name = normalizePortableSkillName(input.name);
|
|
49433
49626
|
const root3 = getPortableSkillsRoot(options);
|
|
49434
|
-
const target =
|
|
49627
|
+
const target = join20(root3, name);
|
|
49435
49628
|
const created = !existsSync10(target);
|
|
49436
49629
|
mkdirSync7(root3, { recursive: true });
|
|
49437
|
-
const staging = mkdtempSync(
|
|
49630
|
+
const staging = mkdtempSync(join20(root3, `.pull-${name}-`));
|
|
49438
49631
|
let moved = false;
|
|
49439
49632
|
let backup = null;
|
|
49440
49633
|
try {
|
|
49441
|
-
writeFileSync5(
|
|
49634
|
+
writeFileSync5(join20(staging, "SKILL.md"), input.skillMd);
|
|
49442
49635
|
const manifest = buildCorpusManifest(input, name);
|
|
49443
49636
|
writeSkillJsonWithHash(staging, manifest);
|
|
49444
49637
|
if (existsSync10(target)) {
|
|
49445
|
-
backup = mkdtempSync(
|
|
49446
|
-
renameSync3(target,
|
|
49638
|
+
backup = mkdtempSync(join20(root3, `.pull-backup-${name}-`));
|
|
49639
|
+
renameSync3(target, join20(backup, name));
|
|
49447
49640
|
moved = true;
|
|
49448
49641
|
}
|
|
49449
49642
|
renameSync3(staging, target);
|
|
@@ -49452,9 +49645,9 @@ function installCorpusSkillAtomically(input, options = {}) {
|
|
|
49452
49645
|
return { name, path: target, manifest, created };
|
|
49453
49646
|
} catch (error) {
|
|
49454
49647
|
rmSync(staging, { recursive: true, force: true });
|
|
49455
|
-
if (moved && backup && existsSync10(
|
|
49648
|
+
if (moved && backup && existsSync10(join20(backup, name))) {
|
|
49456
49649
|
try {
|
|
49457
|
-
renameSync3(
|
|
49650
|
+
renameSync3(join20(backup, name), target);
|
|
49458
49651
|
} catch {}
|
|
49459
49652
|
}
|
|
49460
49653
|
throw error;
|
|
@@ -49467,8 +49660,8 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
49467
49660
|
const warnings = [...base.warnings];
|
|
49468
49661
|
let manifest;
|
|
49469
49662
|
if (existsSync10(skillPath)) {
|
|
49470
|
-
const skillJsonPath =
|
|
49471
|
-
const skillMdPath =
|
|
49663
|
+
const skillJsonPath = join20(skillPath, "skill.json");
|
|
49664
|
+
const skillMdPath = join20(skillPath, "SKILL.md");
|
|
49472
49665
|
if (!existsSync10(skillJsonPath) && !existsSync10(skillMdPath)) {
|
|
49473
49666
|
add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
|
|
49474
49667
|
}
|
|
@@ -49488,7 +49681,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
49488
49681
|
add3(issues, "portable.version_missing", "Portable manifest missing version");
|
|
49489
49682
|
}
|
|
49490
49683
|
const contractIssues = validatePortableManifestContract(manifest, {
|
|
49491
|
-
strict: existsSync10(
|
|
49684
|
+
strict: existsSync10(join20(skillPath, "skill.json")),
|
|
49492
49685
|
skillPath
|
|
49493
49686
|
});
|
|
49494
49687
|
for (const issue of contractIssues)
|
|
@@ -49524,7 +49717,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
49524
49717
|
add3(issues, "portable.command_entry_unsafe", `Command '${command3.name}' entry '${command3.entry}' must stay inside the skill directory`);
|
|
49525
49718
|
continue;
|
|
49526
49719
|
}
|
|
49527
|
-
const entryPath =
|
|
49720
|
+
const entryPath = join20(skillPath, command3.entry);
|
|
49528
49721
|
if (!existsSync10(entryPath))
|
|
49529
49722
|
add3(issues, "portable.command_entry_missing", `Command '${command3.name}' entry '${command3.entry}' is missing`);
|
|
49530
49723
|
else if (statSync8(entryPath).isDirectory())
|
|
@@ -49535,7 +49728,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
49535
49728
|
} catch (error) {
|
|
49536
49729
|
add3(issues, "portable.manifest_invalid", error.message);
|
|
49537
49730
|
}
|
|
49538
|
-
if (manifest?.kind !== "instruction" && !existsSync10(
|
|
49731
|
+
if (manifest?.kind !== "instruction" && !existsSync10(join20(skillPath, "AGENTS.md"))) {
|
|
49539
49732
|
add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
|
|
49540
49733
|
}
|
|
49541
49734
|
}
|
|
@@ -49571,12 +49764,12 @@ async function runPortableSkill(name, args, options = {}) {
|
|
|
49571
49764
|
if (!isSafeRelativePath2(command3.entry)) {
|
|
49572
49765
|
return { exitCode: 1, error: `Portable skill '${name}' command entry is unsafe` };
|
|
49573
49766
|
}
|
|
49574
|
-
const entryPath =
|
|
49767
|
+
const entryPath = join20(skill.path, command3.entry);
|
|
49575
49768
|
if (!existsSync10(entryPath)) {
|
|
49576
49769
|
return { exitCode: 1, error: `Entry point '${command3.entry}' not found in portable skill '${name}'` };
|
|
49577
49770
|
}
|
|
49578
|
-
const pkgPath =
|
|
49579
|
-
const nodeModules =
|
|
49771
|
+
const pkgPath = join20(skill.path, "package.json");
|
|
49772
|
+
const nodeModules = join20(skill.path, "node_modules");
|
|
49580
49773
|
if (existsSync10(pkgPath) && !existsSync10(nodeModules) && hasPackageDependencies(pkgPath)) {
|
|
49581
49774
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
49582
49775
|
cwd: skill.path,
|
|
@@ -49623,7 +49816,7 @@ function safeIsDirectory(path) {
|
|
|
49623
49816
|
}
|
|
49624
49817
|
}
|
|
49625
49818
|
function isSafeRelativePath2(value) {
|
|
49626
|
-
if (!value.trim() ||
|
|
49819
|
+
if (!value.trim() || isAbsolute9(value))
|
|
49627
49820
|
return false;
|
|
49628
49821
|
const normalized = normalize2(value).replace(/\\/g, "/");
|
|
49629
49822
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
@@ -49648,9 +49841,9 @@ function isSkillsOwnershipMarker(marker) {
|
|
|
49648
49841
|
return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
|
|
49649
49842
|
}
|
|
49650
49843
|
function hasSkillsOwnershipMarker(dir) {
|
|
49651
|
-
const path =
|
|
49844
|
+
const path = join21(dir, SYNC_MARKER_FILE);
|
|
49652
49845
|
try {
|
|
49653
|
-
if (!
|
|
49846
|
+
if (!lstatSync12(path).isFile())
|
|
49654
49847
|
return false;
|
|
49655
49848
|
const marker = JSON.parse(readFileSync14(path, "utf8"));
|
|
49656
49849
|
return isSkillsOwnershipMarker(marker);
|
|
@@ -49672,9 +49865,9 @@ function resolveSyncAgents(arg) {
|
|
|
49672
49865
|
function agentGlobalSkillsDir(agent, homeDir2 = homedir4()) {
|
|
49673
49866
|
switch (agent) {
|
|
49674
49867
|
case "opencode":
|
|
49675
|
-
return
|
|
49868
|
+
return join21(homeDir2, ".config", "opencode", "skills");
|
|
49676
49869
|
default:
|
|
49677
|
-
return
|
|
49870
|
+
return join21(homeDir2, `.${agent}`, "skills");
|
|
49678
49871
|
}
|
|
49679
49872
|
}
|
|
49680
49873
|
function adaptSkillMdForAgent(skillMd, agent) {
|
|
@@ -49733,7 +49926,7 @@ function resolveSyncCorpus(options = {}) {
|
|
|
49733
49926
|
function packageSourceRoots(source) {
|
|
49734
49927
|
const roots = [];
|
|
49735
49928
|
for (const sub of ["skills"]) {
|
|
49736
|
-
const candidate =
|
|
49929
|
+
const candidate = join21(source, sub);
|
|
49737
49930
|
if (existsSync11(candidate) && isDirectory(candidate))
|
|
49738
49931
|
roots.push(candidate);
|
|
49739
49932
|
}
|
|
@@ -49749,10 +49942,10 @@ function containsSkillDirectories(path) {
|
|
|
49749
49942
|
return false;
|
|
49750
49943
|
}
|
|
49751
49944
|
return entries.some((entry) => {
|
|
49752
|
-
const candidate =
|
|
49945
|
+
const candidate = join21(path, entry);
|
|
49753
49946
|
if (!isDirectory(candidate))
|
|
49754
49947
|
return false;
|
|
49755
|
-
return existsSync11(
|
|
49948
|
+
return existsSync11(join21(candidate, "SKILL.md")) || existsSync11(join21(candidate, "skill.json")) || existsSync11(join21(candidate, "package.json"));
|
|
49756
49949
|
});
|
|
49757
49950
|
}
|
|
49758
49951
|
function isDirectory(path) {
|
|
@@ -49792,7 +49985,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
49792
49985
|
actions.push({
|
|
49793
49986
|
skill: name,
|
|
49794
49987
|
agent,
|
|
49795
|
-
path:
|
|
49988
|
+
path: join21(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
|
|
49796
49989
|
action: "skip",
|
|
49797
49990
|
reason: "not found in this machine's corpus"
|
|
49798
49991
|
});
|
|
@@ -49822,7 +50015,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
49822
50015
|
}
|
|
49823
50016
|
function writeManagedAgentSkill(params) {
|
|
49824
50017
|
const homeDir2 = params.homeDir ?? homedir4();
|
|
49825
|
-
const dir =
|
|
50018
|
+
const dir = join21(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
|
|
49826
50019
|
const result = writeManagedSkillDir(dir, params.skillMd, {
|
|
49827
50020
|
skill: params.skill,
|
|
49828
50021
|
source: params.source,
|
|
@@ -49839,8 +50032,8 @@ function writeManagedAgentSkill(params) {
|
|
|
49839
50032
|
};
|
|
49840
50033
|
}
|
|
49841
50034
|
function writeManagedSkillDir(dir, skillMd, options) {
|
|
49842
|
-
const skillMdPath =
|
|
49843
|
-
const markerPath =
|
|
50035
|
+
const skillMdPath = join21(dir, "SKILL.md");
|
|
50036
|
+
const markerPath = join21(dir, SYNC_MARKER_FILE);
|
|
49844
50037
|
const dirExists = existsSync11(dir);
|
|
49845
50038
|
const managed = hasSkillsOwnershipMarker(dir);
|
|
49846
50039
|
const hasSkillMd = existsSync11(skillMdPath);
|
|
@@ -49876,13 +50069,13 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
49876
50069
|
const action = dirExists ? "update" : "create";
|
|
49877
50070
|
if (options.dryRun)
|
|
49878
50071
|
return { action, path: skillMdPath };
|
|
49879
|
-
const parentDir =
|
|
50072
|
+
const parentDir = dirname14(dir);
|
|
49880
50073
|
mkdirSync8(parentDir, { recursive: true });
|
|
49881
|
-
const transactionDir = mkdtempSync2(
|
|
49882
|
-
const candidateDir =
|
|
49883
|
-
const backupDir =
|
|
49884
|
-
const candidateSkillMdPath =
|
|
49885
|
-
const candidateMarkerPath =
|
|
50074
|
+
const transactionDir = mkdtempSync2(join21(parentDir, `.hasna-skills-write-${basename3(dir)}-`));
|
|
50075
|
+
const candidateDir = join21(transactionDir, "candidate");
|
|
50076
|
+
const backupDir = join21(transactionDir, "backup");
|
|
50077
|
+
const candidateSkillMdPath = join21(candidateDir, "SKILL.md");
|
|
50078
|
+
const candidateMarkerPath = join21(candidateDir, SYNC_MARKER_FILE);
|
|
49886
50079
|
const marker = {
|
|
49887
50080
|
managedBy: SYNC_MARKER_MANAGED_BY,
|
|
49888
50081
|
skill: options.skill,
|
|
@@ -49931,7 +50124,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
49931
50124
|
return { action, path: skillMdPath };
|
|
49932
50125
|
}
|
|
49933
50126
|
function removeManagedAgentSkill(skill, agent, homeDir2 = homedir4()) {
|
|
49934
|
-
const dir =
|
|
50127
|
+
const dir = join21(agentGlobalSkillsDir(agent, homeDir2), skill);
|
|
49935
50128
|
if (!hasSkillsOwnershipMarker(dir))
|
|
49936
50129
|
return false;
|
|
49937
50130
|
rmSync2(dir, { recursive: true, force: true });
|
|
@@ -49939,7 +50132,7 @@ function removeManagedAgentSkill(skill, agent, homeDir2 = homedir4()) {
|
|
|
49939
50132
|
}
|
|
49940
50133
|
function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
|
|
49941
50134
|
if (kind === undefined || kind === "instruction" || preferBundledDocs) {
|
|
49942
|
-
const skillMdPath =
|
|
50135
|
+
const skillMdPath = join21(skillPath, "SKILL.md");
|
|
49943
50136
|
if (existsSync11(skillMdPath))
|
|
49944
50137
|
return readFileSync14(skillMdPath, "utf-8");
|
|
49945
50138
|
}
|
|
@@ -49972,7 +50165,7 @@ function normalizeSkillName(name) {
|
|
|
49972
50165
|
|
|
49973
50166
|
// src/lib/registry.ts
|
|
49974
50167
|
import { existsSync as existsSync12, readFileSync as readFileSync15, readdirSync as readdirSync12 } from "fs";
|
|
49975
|
-
import { join as
|
|
50168
|
+
import { join as join22 } from "path";
|
|
49976
50169
|
|
|
49977
50170
|
// src/lib/registry-merge.ts
|
|
49978
50171
|
var SKILL_SOURCE_PRECEDENCE = {
|
|
@@ -50193,7 +50386,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
50193
50386
|
for (const entry of entries) {
|
|
50194
50387
|
if (!entry.isDirectory())
|
|
50195
50388
|
continue;
|
|
50196
|
-
const skillMdPath =
|
|
50389
|
+
const skillMdPath = join22(dir, entry.name, "SKILL.md");
|
|
50197
50390
|
if (!existsSync12(skillMdPath))
|
|
50198
50391
|
continue;
|
|
50199
50392
|
let content;
|
|
@@ -50213,7 +50406,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
50213
50406
|
category: fm.category || "Development Tools",
|
|
50214
50407
|
tags: fm.tags || [],
|
|
50215
50408
|
...fm.kind ? { kind: fm.kind } : {},
|
|
50216
|
-
...isHostedMetadataSkillDir(
|
|
50409
|
+
...isHostedMetadataSkillDir(join22(dir, entry.name)) ? { serverOwned: true } : {},
|
|
50217
50410
|
source
|
|
50218
50411
|
});
|
|
50219
50412
|
}
|
|
@@ -50229,8 +50422,8 @@ function findExtensionSkillPath(name) {
|
|
|
50229
50422
|
for (const entry of entries) {
|
|
50230
50423
|
if (!entry.isDirectory())
|
|
50231
50424
|
continue;
|
|
50232
|
-
const skillDir =
|
|
50233
|
-
const skillMdPath =
|
|
50425
|
+
const skillDir = join22(config.extensionsDir, entry.name);
|
|
50426
|
+
const skillMdPath = join22(skillDir, "SKILL.md");
|
|
50234
50427
|
if (!existsSync12(skillMdPath))
|
|
50235
50428
|
continue;
|
|
50236
50429
|
let content;
|
|
@@ -50268,7 +50461,7 @@ function loadRegistry(cwd) {
|
|
|
50268
50461
|
const official = SKILLS.map((s2) => ({ ...s2, source: "official" }));
|
|
50269
50462
|
const extensions = config.extensionsDir ? discoverSkillsInDir(config.extensionsDir, "extension") : [];
|
|
50270
50463
|
const portableCustom = listPortableSkillMetas();
|
|
50271
|
-
const legacyCustom = discoverSkillsInDir(
|
|
50464
|
+
const legacyCustom = discoverSkillsInDir(join22(dataDir2, "custom"));
|
|
50272
50465
|
const globalCustom = mergeCustomSkills([...legacyCustom, ...portableCustom]);
|
|
50273
50466
|
registryCache = mergeSkillRegistryLists(official, extensions, globalCustom);
|
|
50274
50467
|
registryCacheTime = now;
|
|
@@ -50317,7 +50510,7 @@ function getAllTags() {
|
|
|
50317
50510
|
|
|
50318
50511
|
// src/lib/project-state.ts
|
|
50319
50512
|
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "fs";
|
|
50320
|
-
import { join as
|
|
50513
|
+
import { join as join23 } from "path";
|
|
50321
50514
|
var VALID_PIN_SOURCES = [
|
|
50322
50515
|
"official",
|
|
50323
50516
|
"custom",
|
|
@@ -50332,10 +50525,10 @@ var SKILLS_PROJECT_DIR = ".skills";
|
|
|
50332
50525
|
var PROJECT_CONFIG_FILE = "project.json";
|
|
50333
50526
|
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
50334
50527
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
50335
|
-
return
|
|
50528
|
+
return join23(targetDir, SKILLS_PROJECT_DIR);
|
|
50336
50529
|
}
|
|
50337
50530
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
50338
|
-
return
|
|
50531
|
+
return join23(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
50339
50532
|
}
|
|
50340
50533
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
50341
50534
|
const path = getProjectConfigPath(targetDir);
|
|
@@ -50456,16 +50649,16 @@ function isPinSource(value) {
|
|
|
50456
50649
|
}
|
|
50457
50650
|
|
|
50458
50651
|
// src/lib/installer.ts
|
|
50459
|
-
var __dirname2 =
|
|
50652
|
+
var __dirname2 = dirname15(fileURLToPath2(import.meta.url));
|
|
50460
50653
|
function findSkillsDir() {
|
|
50461
50654
|
let dir = __dirname2;
|
|
50462
50655
|
for (let i3 = 0;i3 < 5; i3++) {
|
|
50463
|
-
const candidate =
|
|
50656
|
+
const candidate = join24(dir, "skills");
|
|
50464
50657
|
if (existsSync14(candidate) && !dir.includes(".skills"))
|
|
50465
50658
|
return candidate;
|
|
50466
|
-
dir =
|
|
50659
|
+
dir = dirname15(dir);
|
|
50467
50660
|
}
|
|
50468
|
-
return
|
|
50661
|
+
return join24(__dirname2, "..", "skills");
|
|
50469
50662
|
}
|
|
50470
50663
|
var SKILLS_DIR = findSkillsDir();
|
|
50471
50664
|
function getSkillPath(name) {
|
|
@@ -50473,13 +50666,13 @@ function getSkillPath(name) {
|
|
|
50473
50666
|
const portable = findPortableSkill(skillName);
|
|
50474
50667
|
if (portable)
|
|
50475
50668
|
return portable.path;
|
|
50476
|
-
const legacyCustomPath =
|
|
50669
|
+
const legacyCustomPath = join24(getDataDir(), "custom", skillName);
|
|
50477
50670
|
if (existsSync14(legacyCustomPath))
|
|
50478
50671
|
return legacyCustomPath;
|
|
50479
50672
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
50480
50673
|
if (extensionPath)
|
|
50481
50674
|
return extensionPath;
|
|
50482
|
-
return
|
|
50675
|
+
return join24(SKILLS_DIR, skillName);
|
|
50483
50676
|
}
|
|
50484
50677
|
function getCanonicalSkillName(name) {
|
|
50485
50678
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
@@ -50542,7 +50735,7 @@ function createLocalSkillManifest(name, generateSkillMd) {
|
|
|
50542
50735
|
if (!existsSync14(sourcePath))
|
|
50543
50736
|
return null;
|
|
50544
50737
|
let skillMd = "";
|
|
50545
|
-
const skillMdPath =
|
|
50738
|
+
const skillMdPath = join24(sourcePath, "SKILL.md");
|
|
50546
50739
|
if (existsSync14(skillMdPath)) {
|
|
50547
50740
|
skillMd = readFileSync17(skillMdPath, "utf-8");
|
|
50548
50741
|
} else if (generateSkillMd) {
|
|
@@ -50616,16 +50809,16 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
50616
50809
|
const base = projectDir || process.cwd();
|
|
50617
50810
|
switch (agent) {
|
|
50618
50811
|
case "pi":
|
|
50619
|
-
return scope === "project" ?
|
|
50812
|
+
return scope === "project" ? join24(base, ".pi", "skills") : join24(homedir5(), ".pi", "agent", "skills");
|
|
50620
50813
|
case "opencode":
|
|
50621
|
-
return scope === "project" ?
|
|
50814
|
+
return scope === "project" ? join24(base, ".opencode", "skills") : join24(homedir5(), ".config", "opencode", "skills");
|
|
50622
50815
|
default:
|
|
50623
|
-
return scope === "project" ?
|
|
50816
|
+
return scope === "project" ? join24(base, `.${agent}`, "skills") : join24(homedir5(), `.${agent}`, "skills");
|
|
50624
50817
|
}
|
|
50625
50818
|
}
|
|
50626
50819
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
50627
50820
|
const skillName = normalizeSkillName(getCanonicalSkillName(name));
|
|
50628
|
-
return
|
|
50821
|
+
return join24(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
50629
50822
|
}
|
|
50630
50823
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
50631
50824
|
assertNativeExportAllowed();
|
|
@@ -50661,7 +50854,7 @@ function removeSkillForAgent(name, options) {
|
|
|
50661
50854
|
}
|
|
50662
50855
|
function resolveAgentSkillMd(name, generateSkillMd) {
|
|
50663
50856
|
const sourcePath = getSkillPath(name);
|
|
50664
|
-
const skillMdPath =
|
|
50857
|
+
const skillMdPath = join24(sourcePath, "SKILL.md");
|
|
50665
50858
|
if (existsSync14(skillMdPath))
|
|
50666
50859
|
return readFileSync17(skillMdPath, "utf-8");
|
|
50667
50860
|
if (generateSkillMd)
|
|
@@ -50696,7 +50889,7 @@ function generateMinimalSkillMd(name) {
|
|
|
50696
50889
|
"---",
|
|
50697
50890
|
""
|
|
50698
50891
|
].filter(Boolean);
|
|
50699
|
-
const fallbackDoc = readFileIfExists(
|
|
50892
|
+
const fallbackDoc = readFileIfExists(join24(sourcePath, "README.md")) || readFileIfExists(join24(sourcePath, "CLAUDE.md"));
|
|
50700
50893
|
if (fallbackDoc)
|
|
50701
50894
|
return `${frontmatter.join(`
|
|
50702
50895
|
`)}${fallbackDoc.trim()}
|
|
@@ -50715,7 +50908,7 @@ skills run ${canonicalName}
|
|
|
50715
50908
|
`;
|
|
50716
50909
|
}
|
|
50717
50910
|
function readBundledSkillVersion(name) {
|
|
50718
|
-
const pkgPath =
|
|
50911
|
+
const pkgPath = join24(getSkillPath(name), "package.json");
|
|
50719
50912
|
if (!existsSync14(pkgPath))
|
|
50720
50913
|
return "unknown";
|
|
50721
50914
|
try {
|
|
@@ -50734,15 +50927,15 @@ function loadProjectConfigCompat(targetDir) {
|
|
|
50734
50927
|
|
|
50735
50928
|
// src/server/seed-bundled.ts
|
|
50736
50929
|
import { readFileSync as readFileSync21 } from "fs";
|
|
50737
|
-
import { join as
|
|
50930
|
+
import { join as join27 } from "path";
|
|
50738
50931
|
|
|
50739
50932
|
// src/server/registry.ts
|
|
50740
50933
|
import { existsSync as existsSync16, readFileSync as readFileSync20 } from "fs";
|
|
50741
|
-
import { resolve as
|
|
50934
|
+
import { resolve as resolve9, sep as sep5 } from "path";
|
|
50742
50935
|
|
|
50743
50936
|
// src/lib/dependency-preparation.ts
|
|
50744
|
-
import { constants as
|
|
50745
|
-
import { join as
|
|
50937
|
+
import { constants as constants6, closeSync as closeSync7, fstatSync as fstatSync7, lstatSync as lstatSync13, mkdirSync as mkdirSync10, openSync as openSync7, readFileSync as readFileSync18, readdirSync as readdirSync13, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3, ftruncateSync, writeSync } from "fs";
|
|
50938
|
+
import { join as join25 } from "path";
|
|
50746
50939
|
|
|
50747
50940
|
// src/lib/preparation-process.ts
|
|
50748
50941
|
function capturePreparationProcess(child, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
|
|
@@ -50789,15 +50982,15 @@ var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation";
|
|
|
50789
50982
|
var RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.";
|
|
50790
50983
|
function hasDependencyPreparationMarker(skillPath) {
|
|
50791
50984
|
try {
|
|
50792
|
-
|
|
50985
|
+
lstatSync13(join25(skillPath, DEPENDENCY_PREPARATION_MARKER));
|
|
50793
50986
|
return true;
|
|
50794
50987
|
} catch (error) {
|
|
50795
50988
|
return error.code !== "ENOENT";
|
|
50796
50989
|
}
|
|
50797
50990
|
}
|
|
50798
50991
|
function claimPreparation(skillPath) {
|
|
50799
|
-
const marker =
|
|
50800
|
-
const state2 =
|
|
50992
|
+
const marker = join25(skillPath, DEPENDENCY_PREPARATION_MARKER);
|
|
50993
|
+
const state2 = join25(marker, "state.json"), active = join25(marker, "active");
|
|
50801
50994
|
let created = false;
|
|
50802
50995
|
try {
|
|
50803
50996
|
mkdirSync10(marker, { mode: 448 });
|
|
@@ -50808,21 +51001,21 @@ function claimPreparation(skillPath) {
|
|
|
50808
51001
|
}
|
|
50809
51002
|
let fd;
|
|
50810
51003
|
try {
|
|
50811
|
-
if (!
|
|
51004
|
+
if (!lstatSync13(marker).isDirectory() || lstatSync13(marker).isSymbolicLink())
|
|
50812
51005
|
return;
|
|
50813
51006
|
if (!created) {
|
|
50814
51007
|
if (readdirSync13(marker).some((name) => name !== "state.json"))
|
|
50815
51008
|
return;
|
|
50816
|
-
if (!
|
|
51009
|
+
if (!lstatSync13(state2).isFile() || lstatSync13(state2).isSymbolicLink() || lstatSync13(state2).size > 1024)
|
|
50817
51010
|
return;
|
|
50818
51011
|
const previous = JSON.parse(readFileSync18(state2, "utf8"));
|
|
50819
51012
|
if (previous.version !== 1 || previous.status !== "failed" || Object.keys(previous).length !== 2)
|
|
50820
51013
|
return;
|
|
50821
51014
|
}
|
|
50822
51015
|
mkdirSync10(active, { mode: 448 });
|
|
50823
|
-
fd =
|
|
50824
|
-
if (!
|
|
50825
|
-
|
|
51016
|
+
fd = openSync7(state2, constants6.O_RDWR | constants6.O_NOFOLLOW | (created ? constants6.O_CREAT | constants6.O_EXCL : 0), 384);
|
|
51017
|
+
if (!fstatSync7(fd).isFile()) {
|
|
51018
|
+
closeSync7(fd);
|
|
50826
51019
|
return;
|
|
50827
51020
|
}
|
|
50828
51021
|
const descriptor = fd;
|
|
@@ -50834,7 +51027,7 @@ function claimPreparation(skillPath) {
|
|
|
50834
51027
|
record2("pending");
|
|
50835
51028
|
return { abandon() {
|
|
50836
51029
|
try {
|
|
50837
|
-
|
|
51030
|
+
closeSync7(descriptor);
|
|
50838
51031
|
} catch {}
|
|
50839
51032
|
}, finish(success) {
|
|
50840
51033
|
try {
|
|
@@ -50842,7 +51035,7 @@ function claimPreparation(skillPath) {
|
|
|
50842
51035
|
unlinkSync3(state2);
|
|
50843
51036
|
else
|
|
50844
51037
|
record2("failed");
|
|
50845
|
-
|
|
51038
|
+
closeSync7(descriptor);
|
|
50846
51039
|
fd = undefined;
|
|
50847
51040
|
rmdirSync2(active);
|
|
50848
51041
|
if (success)
|
|
@@ -50850,7 +51043,7 @@ function claimPreparation(skillPath) {
|
|
|
50850
51043
|
return true;
|
|
50851
51044
|
} catch {
|
|
50852
51045
|
try {
|
|
50853
|
-
|
|
51046
|
+
closeSync7(descriptor);
|
|
50854
51047
|
} catch {}
|
|
50855
51048
|
return false;
|
|
50856
51049
|
}
|
|
@@ -50858,7 +51051,7 @@ function claimPreparation(skillPath) {
|
|
|
50858
51051
|
} catch {
|
|
50859
51052
|
if (fd !== undefined)
|
|
50860
51053
|
try {
|
|
50861
|
-
|
|
51054
|
+
closeSync7(fd);
|
|
50862
51055
|
} catch {}
|
|
50863
51056
|
return;
|
|
50864
51057
|
}
|
|
@@ -50949,11 +51142,11 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
|
|
|
50949
51142
|
|
|
50950
51143
|
// src/lib/skillinfo.ts
|
|
50951
51144
|
import { existsSync as existsSync15, readFileSync as readFileSync19 } from "fs";
|
|
50952
|
-
import { join as
|
|
51145
|
+
import { join as join26 } from "path";
|
|
50953
51146
|
function isInstructionSkillDir(skillPath, meta) {
|
|
50954
51147
|
if (meta?.kind === "instruction")
|
|
50955
51148
|
return true;
|
|
50956
|
-
const skillMdPath =
|
|
51149
|
+
const skillMdPath = join26(skillPath, "SKILL.md");
|
|
50957
51150
|
if (!existsSync15(skillMdPath))
|
|
50958
51151
|
return false;
|
|
50959
51152
|
try {
|
|
@@ -50983,9 +51176,9 @@ function getSkillDocs(name) {
|
|
|
50983
51176
|
if (!existsSync15(skillPath))
|
|
50984
51177
|
return null;
|
|
50985
51178
|
return {
|
|
50986
|
-
skillMd: readIfExists(
|
|
50987
|
-
readme: readIfExists(
|
|
50988
|
-
claudeMd: readIfExists(
|
|
51179
|
+
skillMd: readIfExists(join26(skillPath, "SKILL.md")),
|
|
51180
|
+
readme: readIfExists(join26(skillPath, "README.md")),
|
|
51181
|
+
claudeMd: readIfExists(join26(skillPath, "CLAUDE.md"))
|
|
50989
51182
|
};
|
|
50990
51183
|
}
|
|
50991
51184
|
function getSkillBestDoc(name) {
|
|
@@ -51000,14 +51193,14 @@ function getSkillRequirements(name) {
|
|
|
51000
51193
|
return null;
|
|
51001
51194
|
const texts = [];
|
|
51002
51195
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
51003
|
-
const content = readIfExists(
|
|
51196
|
+
const content = readIfExists(join26(skillPath, file));
|
|
51004
51197
|
if (content)
|
|
51005
51198
|
texts.push(content);
|
|
51006
51199
|
}
|
|
51007
51200
|
const meta = getSkill(name);
|
|
51008
51201
|
let dependencies = {};
|
|
51009
51202
|
try {
|
|
51010
|
-
dependencies = JSON.parse(readFileSync19(
|
|
51203
|
+
dependencies = JSON.parse(readFileSync19(join26(skillPath, "package.json"), "utf8")).dependencies || {};
|
|
51011
51204
|
} catch {}
|
|
51012
51205
|
return getSkillRequirementsFromContent(name, texts, dependencies, meta);
|
|
51013
51206
|
}
|
|
@@ -51069,7 +51262,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
51069
51262
|
error: `Skill '${name}' is an instruction skill (kind: instruction) and is not runnable. Instruction skills are consumed by coding agents via SKILL.md, not executed with 'skills run'.`
|
|
51070
51263
|
};
|
|
51071
51264
|
}
|
|
51072
|
-
const pkgPath =
|
|
51265
|
+
const pkgPath = join26(skillPath, "package.json");
|
|
51073
51266
|
if (!existsSync15(pkgPath)) {
|
|
51074
51267
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
51075
51268
|
}
|
|
@@ -51089,12 +51282,12 @@ async function runSkill(name, args, options = {}) {
|
|
|
51089
51282
|
} catch {
|
|
51090
51283
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
51091
51284
|
}
|
|
51092
|
-
const entryPath =
|
|
51285
|
+
const entryPath = join26(skillPath, entryPoint);
|
|
51093
51286
|
if (!existsSync15(entryPath)) {
|
|
51094
51287
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
51095
51288
|
}
|
|
51096
51289
|
const env = { ...process.env, ...options.env };
|
|
51097
|
-
if (!existsSync15(
|
|
51290
|
+
if (!existsSync15(join26(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
|
|
51098
51291
|
const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
|
|
51099
51292
|
if (failure)
|
|
51100
51293
|
return failure;
|
|
@@ -51172,10 +51365,10 @@ function generateSkillMd(name) {
|
|
|
51172
51365
|
"---"
|
|
51173
51366
|
].join(`
|
|
51174
51367
|
`);
|
|
51175
|
-
const readme = readIfExists(
|
|
51176
|
-
const claudeMd = readIfExists(
|
|
51368
|
+
const readme = readIfExists(join26(skillPath, "README.md"));
|
|
51369
|
+
const claudeMd = readIfExists(join26(skillPath, "CLAUDE.md"));
|
|
51177
51370
|
let cliCommand = null;
|
|
51178
|
-
const pkgPath =
|
|
51371
|
+
const pkgPath = join26(skillPath, "package.json");
|
|
51179
51372
|
if (existsSync15(pkgPath)) {
|
|
51180
51373
|
try {
|
|
51181
51374
|
const pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
|
|
@@ -51291,15 +51484,15 @@ function getServerSkillMd(slug) {
|
|
|
51291
51484
|
const docs = getSkillDocs(name);
|
|
51292
51485
|
if (docs?.skillMd)
|
|
51293
51486
|
return docs.skillMd;
|
|
51294
|
-
const skillsDir =
|
|
51295
|
-
const path =
|
|
51487
|
+
const skillsDir = resolve9(process.cwd(), "skills");
|
|
51488
|
+
const path = resolve9(skillsDir, name, "SKILL.md");
|
|
51296
51489
|
if (!isInsideDir(skillsDir, path))
|
|
51297
51490
|
return null;
|
|
51298
51491
|
return existsSync16(path) ? readFileSync20(path, "utf8") : null;
|
|
51299
51492
|
}
|
|
51300
51493
|
|
|
51301
51494
|
// src/server/skills-api.ts
|
|
51302
|
-
import { createHash as
|
|
51495
|
+
import { createHash as createHash14 } from "crypto";
|
|
51303
51496
|
var SLUG_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
51304
51497
|
var SHA256_HEX2 = /^[0-9a-f]{64}$/;
|
|
51305
51498
|
var MAX_SLUG_LENGTH = 128;
|
|
@@ -51511,7 +51704,7 @@ async function parsePublishRequest(request, config) {
|
|
|
51511
51704
|
if (bundleBytes.byteLength === 0) {
|
|
51512
51705
|
throw new SkillRequestError(400, "BUNDLE_EMPTY", "the uploaded bundle is empty");
|
|
51513
51706
|
}
|
|
51514
|
-
const sha2562 =
|
|
51707
|
+
const sha2562 = createHash14("sha256").update(bundleBytes).digest("hex");
|
|
51515
51708
|
const claimed = optionalString(manifest.bundleSha256);
|
|
51516
51709
|
if (claimed)
|
|
51517
51710
|
assertSha2562(claimed);
|
|
@@ -51626,7 +51819,7 @@ async function readPublishedBundle(store, artifactStorage, principal, slug) {
|
|
|
51626
51819
|
if (!bytes) {
|
|
51627
51820
|
throw new SkillRequestError(503, "BUNDLE_BACKEND_UNAVAILABLE", "bundle storage backend unavailable");
|
|
51628
51821
|
}
|
|
51629
|
-
const actual =
|
|
51822
|
+
const actual = createHash14("sha256").update(bytes).digest("hex");
|
|
51630
51823
|
if (actual !== record2.bundleSha256) {
|
|
51631
51824
|
throw new SkillRequestError(500, "BUNDLE_DIGEST_DRIFT", `stored bundle for '${slug}' hashes to ${actual} but was published as ${record2.bundleSha256}`);
|
|
51632
51825
|
}
|
|
@@ -51774,7 +51967,7 @@ async function readSkillVersionBundle(store, artifactStorage, principal, slug, v
|
|
|
51774
51967
|
const bytes = await artifactStorage.readBundle(bundle);
|
|
51775
51968
|
if (!bytes)
|
|
51776
51969
|
throw new SkillRequestError(503, "BUNDLE_BACKEND_UNAVAILABLE", "bundle storage backend unavailable");
|
|
51777
|
-
const actual =
|
|
51970
|
+
const actual = createHash14("sha256").update(bytes).digest("hex");
|
|
51778
51971
|
if (actual !== found.bundleSha256) {
|
|
51779
51972
|
throw new SkillRequestError(500, "BUNDLE_DIGEST_DRIFT", `stored bundle for '${slug}@${version2}' hashes to ${actual} but was published as ${found.bundleSha256}`);
|
|
51780
51973
|
}
|
|
@@ -51818,7 +52011,7 @@ async function seedBundledCorpus(options) {
|
|
|
51818
52011
|
}
|
|
51819
52012
|
const manifest = readPortableSkillManifest(dir, slug);
|
|
51820
52013
|
const packed = packSkillBundle(dir, { maxUnpackedBytes: 50000000 });
|
|
51821
|
-
const skillMdPath =
|
|
52014
|
+
const skillMdPath = join27(dir, "SKILL.md");
|
|
51822
52015
|
const skillMd = existsSync17(skillMdPath) ? readFileSync21(skillMdPath, "utf-8") : undefined;
|
|
51823
52016
|
const content = {
|
|
51824
52017
|
slug,
|
|
@@ -52086,7 +52279,7 @@ async function handleProfileApi(store, principal, request, parts, config) {
|
|
|
52086
52279
|
// src/server/runtime-store.ts
|
|
52087
52280
|
import { Database as Database3 } from "bun:sqlite";
|
|
52088
52281
|
import { mkdirSync as mkdirSync11 } from "fs";
|
|
52089
|
-
import { dirname as
|
|
52282
|
+
import { dirname as dirname16 } from "path";
|
|
52090
52283
|
|
|
52091
52284
|
// src/sdk/execution/types.ts
|
|
52092
52285
|
var EXECUTION_PROTOCOL_VERSION = REMOTE_SKILL_RUN_CONTRACT_VERSION;
|
|
@@ -52142,7 +52335,7 @@ class RuntimeExecutionStore {
|
|
|
52142
52335
|
}
|
|
52143
52336
|
const path = target.kind === "sqlite" ? target.path : ":memory:";
|
|
52144
52337
|
if (path !== ":memory:")
|
|
52145
|
-
mkdirSync11(
|
|
52338
|
+
mkdirSync11(dirname16(path), { recursive: true });
|
|
52146
52339
|
const db = new Database3(path);
|
|
52147
52340
|
db.run("PRAGMA busy_timeout=5000");
|
|
52148
52341
|
db.run("PRAGMA journal_mode=WAL");
|
|
@@ -52382,7 +52575,7 @@ class RuntimeExecutionStore {
|
|
|
52382
52575
|
}
|
|
52383
52576
|
|
|
52384
52577
|
// src/sdk/execution/admission.ts
|
|
52385
|
-
import { createHash as
|
|
52578
|
+
import { createHash as createHash16 } from "crypto";
|
|
52386
52579
|
|
|
52387
52580
|
// src/sdk/execution/image-profile.ts
|
|
52388
52581
|
class ImageProfileResolutionError extends Error {
|
|
@@ -52980,11 +53173,11 @@ function ownedAdmission(admission) {
|
|
|
52980
53173
|
};
|
|
52981
53174
|
}
|
|
52982
53175
|
function digestInput(input) {
|
|
52983
|
-
return
|
|
53176
|
+
return createHash16("sha256").update(canonicalJson(input)).digest("hex");
|
|
52984
53177
|
}
|
|
52985
53178
|
|
|
52986
53179
|
// src/sdk/execution/dispatchers/ecs.ts
|
|
52987
|
-
import { createHash as
|
|
53180
|
+
import { createHash as createHash17 } from "crypto";
|
|
52988
53181
|
|
|
52989
53182
|
// src/sdk/execution/state-machine.ts
|
|
52990
53183
|
var LEGAL_TRANSITIONS = {
|
|
@@ -56383,13 +56576,13 @@ function observedTask(states, taskArn) {
|
|
|
56383
56576
|
return state2.lastStatus === "STOPPED" || LIVE_TASK_STATUSES.has(state2.lastStatus) ? state2 : undefined;
|
|
56384
56577
|
}
|
|
56385
56578
|
function clientTokenFor(runId2, attemptId) {
|
|
56386
|
-
return
|
|
56579
|
+
return createHash17("sha256").update(`${runId2}\x00${attemptId}`).digest("hex").slice(0, CLIENT_TOKEN_BYTES * 2);
|
|
56387
56580
|
}
|
|
56388
56581
|
function startedByFor(runId2, attemptNumber) {
|
|
56389
56582
|
return `skills-exec/${runId2}/a${attemptNumber}`;
|
|
56390
56583
|
}
|
|
56391
56584
|
function requestDigestFor(admission, attemptId) {
|
|
56392
|
-
return
|
|
56585
|
+
return createHash17("sha256").update(canonicalJson({ admission, attemptId })).digest("hex");
|
|
56393
56586
|
}
|
|
56394
56587
|
|
|
56395
56588
|
class EcsDispatcher {
|
|
@@ -56685,7 +56878,7 @@ function validText(value) {
|
|
|
56685
56878
|
}
|
|
56686
56879
|
|
|
56687
56880
|
// src/server/runtime-policy.ts
|
|
56688
|
-
import { createHash as
|
|
56881
|
+
import { createHash as createHash18, createHmac as createHmac3, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
56689
56882
|
var RUNTIME_MAX_INPUT_BYTES = 1e5;
|
|
56690
56883
|
var RUNTIME_MAX_BUNDLE_BYTES = 1e6;
|
|
56691
56884
|
var RUNTIME_MAX_ARTIFACT_BYTES = 2000000;
|
|
@@ -56757,7 +56950,7 @@ function verifyRuntimeToken(token, key, admission, attempt, now = Date.now()) {
|
|
|
56757
56950
|
const actual = Buffer.from(token);
|
|
56758
56951
|
return actual.length === expected.length && timingSafeEqual2(actual, expected);
|
|
56759
56952
|
}
|
|
56760
|
-
var hashBytes = (bytes) =>
|
|
56953
|
+
var hashBytes = (bytes) => createHash18("sha256").update(bytes).digest("hex");
|
|
56761
56954
|
|
|
56762
56955
|
// src/server/runtime-api.ts
|
|
56763
56956
|
async function createRuntimeService(options) {
|
|
@@ -57206,7 +57399,7 @@ function normalizePrefix(value) {
|
|
|
57206
57399
|
}
|
|
57207
57400
|
|
|
57208
57401
|
// src/server/handlers.ts
|
|
57209
|
-
import { createHash as
|
|
57402
|
+
import { createHash as createHash19 } from "crypto";
|
|
57210
57403
|
|
|
57211
57404
|
// src/server/store.ts
|
|
57212
57405
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
@@ -58238,7 +58431,7 @@ function textArtifact(run, relativePath, bodyText, contentType = relativePath.en
|
|
|
58238
58431
|
relativePath,
|
|
58239
58432
|
contentType,
|
|
58240
58433
|
byteSize: bytes.byteLength,
|
|
58241
|
-
sha256:
|
|
58434
|
+
sha256: createHash19("sha256").update(bytes).digest("hex"),
|
|
58242
58435
|
visibility: "private"
|
|
58243
58436
|
},
|
|
58244
58437
|
body: { relativePath, bodyText, contentType }
|
|
@@ -62836,7 +63029,7 @@ var localRunExecutor = {
|
|
|
62836
63029
|
// src/sdk/storage.ts
|
|
62837
63030
|
var artifactStorageSeam = ArtifactStorage;
|
|
62838
63031
|
// src/sdk/outputs.ts
|
|
62839
|
-
import { createHash as
|
|
63032
|
+
import { createHash as createHash20 } from "crypto";
|
|
62840
63033
|
class DatabaseOnlyObjectStore {
|
|
62841
63034
|
usesS3 = false;
|
|
62842
63035
|
async materialize(run, artifact, body3) {
|
|
@@ -62883,7 +63076,7 @@ function createGovernedArtifactWriter(options) {
|
|
|
62883
63076
|
const stamped = {
|
|
62884
63077
|
...meta,
|
|
62885
63078
|
byteSize: outputBytes,
|
|
62886
|
-
sha256:
|
|
63079
|
+
sha256: createHash20("sha256").update(persistedBytes).digest("hex"),
|
|
62887
63080
|
visibility: options.visibility ?? config.defaultVisibility,
|
|
62888
63081
|
...config.artifactTtlSeconds !== undefined ? { expiresAt: expiresAtFor(createdAt, config.artifactTtlSeconds) } : {}
|
|
62889
63082
|
};
|
|
@@ -63621,10 +63814,10 @@ async function readQuoteUnavailableCode(response) {
|
|
|
63621
63814
|
let timer;
|
|
63622
63815
|
let deadlineExceeded = false;
|
|
63623
63816
|
const expired = Symbol("quote body deadline");
|
|
63624
|
-
const deadline = new Promise((
|
|
63817
|
+
const deadline = new Promise((resolve11) => {
|
|
63625
63818
|
timer = setTimeout(() => {
|
|
63626
63819
|
deadlineExceeded = true;
|
|
63627
|
-
|
|
63820
|
+
resolve11(expired);
|
|
63628
63821
|
reader.cancel().catch(() => {});
|
|
63629
63822
|
}, 1500);
|
|
63630
63823
|
});
|
|
@@ -64533,7 +64726,7 @@ async function requestInvitationEmail(origin, action, input) {
|
|
|
64533
64726
|
}
|
|
64534
64727
|
|
|
64535
64728
|
// src/lib/remote-private-publications.ts
|
|
64536
|
-
import { createHash as
|
|
64729
|
+
import { createHash as createHash21 } from "crypto";
|
|
64537
64730
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
64538
64731
|
var failures = {
|
|
64539
64732
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -64580,7 +64773,7 @@ var hash2 = (v2) => typeof v2 === "string" && /^[a-f0-9]{64}$/.test(v2);
|
|
|
64580
64773
|
var record6 = (v2) => !!v2 && typeof v2 === "object" && !Array.isArray(v2);
|
|
64581
64774
|
var exact = (v2, keys) => record6(v2) && Object.keys(v2).sort().join(",") === keys.sort().join(",");
|
|
64582
64775
|
var date = (v2) => typeof v2 === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v2) && Number.isFinite(Date.parse(v2));
|
|
64583
|
-
var publicationSha256 = (bytes) =>
|
|
64776
|
+
var publicationSha256 = (bytes) => createHash21("sha256").update(bytes).digest("hex");
|
|
64584
64777
|
function checkedPublicationDeclaration(value) {
|
|
64585
64778
|
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 || !hash2(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
|
|
64586
64779
|
return bad();
|
|
@@ -64825,12 +65018,12 @@ class RemotePrivatePublicationsClient {
|
|
|
64825
65018
|
previous = view;
|
|
64826
65019
|
if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
|
|
64827
65020
|
return view;
|
|
64828
|
-
await new Promise((
|
|
65021
|
+
await new Promise((resolve11) => {
|
|
64829
65022
|
const timer = setTimeout(done, Math.min(1000, until - Date.now()));
|
|
64830
65023
|
function done() {
|
|
64831
65024
|
clearTimeout(timer);
|
|
64832
65025
|
options.signal?.removeEventListener("abort", done);
|
|
64833
|
-
|
|
65026
|
+
resolve11();
|
|
64834
65027
|
}
|
|
64835
65028
|
options.signal?.addEventListener("abort", done, { once: true });
|
|
64836
65029
|
});
|
|
@@ -65062,23 +65255,23 @@ class RemoteSkillsAuthClient {
|
|
|
65062
65255
|
}
|
|
65063
65256
|
}
|
|
65064
65257
|
// src/lib/private-publication-recovery.ts
|
|
65065
|
-
import { constants as
|
|
65066
|
-
import { dirname as
|
|
65258
|
+
import { constants as constants7, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, lstatSync as lstatSync14, mkdirSync as mkdirSync12, openSync as openSync8, readSync as readSync6, realpathSync as realpathSync5, renameSync as renameSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
65259
|
+
import { dirname as dirname17, isAbsolute as isAbsolute10, join as join31, resolve as resolve11 } from "path";
|
|
65067
65260
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
65068
65261
|
var fail = () => {
|
|
65069
65262
|
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.");
|
|
65070
65263
|
};
|
|
65071
65264
|
function safeDirectory(directory) {
|
|
65072
|
-
if (!
|
|
65265
|
+
if (!isAbsolute10(directory) || directory !== resolve11(directory) || realpathSync5(directory) !== directory)
|
|
65073
65266
|
return fail();
|
|
65074
|
-
for (let path2 = directory;; path2 =
|
|
65075
|
-
const stat =
|
|
65267
|
+
for (let path2 = directory;; path2 = dirname17(path2)) {
|
|
65268
|
+
const stat = lstatSync14(path2);
|
|
65076
65269
|
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
65077
65270
|
return fail();
|
|
65078
|
-
if (path2 ===
|
|
65271
|
+
if (path2 === dirname17(path2))
|
|
65079
65272
|
break;
|
|
65080
65273
|
}
|
|
65081
|
-
const own =
|
|
65274
|
+
const own = lstatSync14(directory);
|
|
65082
65275
|
if ((own.mode & 63) !== 0 || process.getuid && own.uid !== process.getuid())
|
|
65083
65276
|
return fail();
|
|
65084
65277
|
return { dev: own.dev, ino: own.ino };
|
|
@@ -65089,36 +65282,36 @@ function unchangedDirectory(directory, identity2) {
|
|
|
65089
65282
|
return fail();
|
|
65090
65283
|
}
|
|
65091
65284
|
function readOwned(directory, name, max) {
|
|
65092
|
-
const identity2 = safeDirectory(directory), file =
|
|
65093
|
-
const fd =
|
|
65285
|
+
const identity2 = safeDirectory(directory), file = join31(directory, name);
|
|
65286
|
+
const fd = openSync8(file, constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
65094
65287
|
try {
|
|
65095
|
-
const stat =
|
|
65288
|
+
const stat = fstatSync8(fd);
|
|
65096
65289
|
if (!stat.isFile() || stat.nlink !== 1 || stat.size > max || stat.size < 1 || (stat.mode & 63) !== 0 || process.getuid && stat.uid !== process.getuid())
|
|
65097
65290
|
return fail();
|
|
65098
65291
|
const buffer = Buffer.alloc(Math.min(max, stat.size) + 1);
|
|
65099
65292
|
let length = 0;
|
|
65100
65293
|
while (length < buffer.length) {
|
|
65101
|
-
const count =
|
|
65294
|
+
const count = readSync6(fd, buffer, length, buffer.length - length, length);
|
|
65102
65295
|
if (count === 0)
|
|
65103
65296
|
break;
|
|
65104
65297
|
length += count;
|
|
65105
65298
|
}
|
|
65106
|
-
const bytes = buffer.subarray(0, length), after =
|
|
65299
|
+
const bytes = buffer.subarray(0, length), after = fstatSync8(fd);
|
|
65107
65300
|
unchangedDirectory(directory, identity2);
|
|
65108
65301
|
if (bytes.length !== stat.size || stat.size !== after.size || stat.mtimeMs !== after.mtimeMs || stat.ctimeMs !== after.ctimeMs)
|
|
65109
65302
|
return fail();
|
|
65110
65303
|
return bytes;
|
|
65111
65304
|
} finally {
|
|
65112
|
-
|
|
65305
|
+
closeSync8(fd);
|
|
65113
65306
|
}
|
|
65114
65307
|
}
|
|
65115
65308
|
function writeOwned(directory, name, bytes) {
|
|
65116
|
-
const fd =
|
|
65309
|
+
const fd = openSync8(join31(directory, name), constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW, 384);
|
|
65117
65310
|
try {
|
|
65118
65311
|
writeFileSync8(fd, bytes);
|
|
65119
65312
|
fsyncSync2(fd);
|
|
65120
65313
|
} finally {
|
|
65121
|
-
|
|
65314
|
+
closeSync8(fd);
|
|
65122
65315
|
}
|
|
65123
65316
|
}
|
|
65124
65317
|
function save(directory, value) {
|
|
@@ -65126,12 +65319,12 @@ function save(directory, value) {
|
|
|
65126
65319
|
writeOwned(directory, temporary, JSON.stringify(value) + `
|
|
65127
65320
|
`);
|
|
65128
65321
|
unchangedDirectory(directory, identity2);
|
|
65129
|
-
renameSync5(
|
|
65130
|
-
const fd =
|
|
65322
|
+
renameSync5(join31(directory, temporary), join31(directory, "receipt.json"));
|
|
65323
|
+
const fd = openSync8(directory, constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
65131
65324
|
try {
|
|
65132
65325
|
fsyncSync2(fd);
|
|
65133
65326
|
} finally {
|
|
65134
|
-
|
|
65327
|
+
closeSync8(fd);
|
|
65135
65328
|
}
|
|
65136
65329
|
}
|
|
65137
65330
|
function bind(client, receipt) {
|
|
@@ -65139,23 +65332,23 @@ function bind(client, receipt) {
|
|
|
65139
65332
|
throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
|
|
65140
65333
|
}
|
|
65141
65334
|
async function locked(directory, action) {
|
|
65142
|
-
const identity2 = safeDirectory(directory), file =
|
|
65335
|
+
const identity2 = safeDirectory(directory), file = join31(directory, "operation.lock");
|
|
65143
65336
|
let fd;
|
|
65144
65337
|
try {
|
|
65145
|
-
fd =
|
|
65338
|
+
fd = openSync8(file, constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW, 384);
|
|
65146
65339
|
} catch {
|
|
65147
65340
|
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.");
|
|
65148
65341
|
}
|
|
65149
|
-
const lock =
|
|
65342
|
+
const lock = fstatSync8(fd);
|
|
65150
65343
|
try {
|
|
65151
65344
|
writeFileSync8(fd, JSON.stringify({ pid: process.pid }) + `
|
|
65152
65345
|
`);
|
|
65153
65346
|
fsyncSync2(fd);
|
|
65154
65347
|
return await action();
|
|
65155
65348
|
} finally {
|
|
65156
|
-
|
|
65349
|
+
closeSync8(fd);
|
|
65157
65350
|
unchangedDirectory(directory, identity2);
|
|
65158
|
-
const now2 =
|
|
65351
|
+
const now2 = lstatSync14(file);
|
|
65159
65352
|
if (now2.dev !== lock.dev || now2.ino !== lock.ino || !now2.isFile() || now2.isSymbolicLink())
|
|
65160
65353
|
fail();
|
|
65161
65354
|
unlinkSync4(file);
|
|
@@ -65207,15 +65400,15 @@ async function preparePrivatePublication(client, sourceDirectory, recoveryDirect
|
|
|
65207
65400
|
phase: "prepared",
|
|
65208
65401
|
intent: null
|
|
65209
65402
|
};
|
|
65210
|
-
if (!
|
|
65403
|
+
if (!isAbsolute10(recoveryDirectory) || resolve11(recoveryDirectory) !== recoveryDirectory || realpathSync5(dirname17(recoveryDirectory)) !== dirname17(recoveryDirectory))
|
|
65211
65404
|
return fail();
|
|
65212
65405
|
mkdirSync12(recoveryDirectory, { mode: 448 });
|
|
65213
65406
|
safeDirectory(recoveryDirectory);
|
|
65214
|
-
const parent =
|
|
65407
|
+
const parent = openSync8(dirname17(recoveryDirectory), constants7.O_RDONLY | constants7.O_NOFOLLOW);
|
|
65215
65408
|
try {
|
|
65216
65409
|
fsyncSync2(parent);
|
|
65217
65410
|
} finally {
|
|
65218
|
-
|
|
65411
|
+
closeSync8(parent);
|
|
65219
65412
|
}
|
|
65220
65413
|
writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
|
|
65221
65414
|
save(recoveryDirectory, receipt);
|