@hasna/skills 0.9.10 → 0.9.11
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 +13 -0
- package/bin/index.js +736 -668
- package/bin/mcp.js +21 -6
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +268 -197
- package/dist/lib/agent-codex-trust.d.ts +13 -10
- package/dist/lib/agent-discovery.d.ts +2 -1
- package/dist/lib/agent-integration.d.ts +2 -0
- package/dist/lib/codex-hook-discovery-recovery.d.ts +25 -0
- package/dist/sdk/index.js +290 -219
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9657,7 +9657,7 @@ import { homedir as homedir5 } from "os";
|
|
|
9657
9657
|
import {
|
|
9658
9658
|
cpSync as cpSync2,
|
|
9659
9659
|
existsSync as existsSync12,
|
|
9660
|
-
lstatSync as
|
|
9660
|
+
lstatSync as lstatSync20,
|
|
9661
9661
|
mkdirSync as mkdirSync8,
|
|
9662
9662
|
mkdtempSync as mkdtempSync2,
|
|
9663
9663
|
readFileSync as readFileSync15,
|
|
@@ -9665,15 +9665,15 @@ import {
|
|
|
9665
9665
|
renameSync as renameSync5,
|
|
9666
9666
|
rmSync as rmSync3,
|
|
9667
9667
|
statSync as statSync9,
|
|
9668
|
-
writeFileSync as
|
|
9668
|
+
writeFileSync as writeFileSync7
|
|
9669
9669
|
} from "fs";
|
|
9670
9670
|
import { homedir as homedir4 } from "os";
|
|
9671
|
-
import { basename as basename9, dirname as
|
|
9671
|
+
import { basename as basename9, dirname as dirname17, join as join26 } from "path";
|
|
9672
9672
|
|
|
9673
9673
|
// src/lib/agent-integration.ts
|
|
9674
|
-
import { createHash as
|
|
9675
|
-
import { existsSync as existsSync11, lstatSync as
|
|
9676
|
-
import { dirname as
|
|
9674
|
+
import { createHash as createHash16, randomUUID as randomUUID3 } from "crypto";
|
|
9675
|
+
import { existsSync as existsSync11, lstatSync as lstatSync19, statSync as statSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync14, readdirSync as readdirSync10, opendirSync as opendirSync5, readlinkSync as readlinkSync2, realpathSync as realpathSync5, renameSync as renameSync4, rmdirSync as rmdirSync3, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync12, closeSync as closeSync12, fsyncSync as fsyncSync4, fstatSync as fstatSync12, readSync as readSync10, constants as constants11, linkSync as linkSync2 } from "fs";
|
|
9676
|
+
import { dirname as dirname16, join as join25, resolve as resolve15, relative as relative5, isAbsolute as isAbsolute15, sep as sep6 } from "path";
|
|
9677
9677
|
import { homedir as homedir3 } from "os";
|
|
9678
9678
|
|
|
9679
9679
|
// src/lib/managed-policy.ts
|
|
@@ -10970,9 +10970,9 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
10970
10970
|
}
|
|
10971
10971
|
|
|
10972
10972
|
// src/lib/agent-discovery.ts
|
|
10973
|
-
import { createHash as
|
|
10974
|
-
import { lstatSync as
|
|
10975
|
-
import { basename as basename8, dirname as
|
|
10973
|
+
import { createHash as createHash15 } from "crypto";
|
|
10974
|
+
import { lstatSync as lstatSync18, readFileSync as readFileSync13, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
|
|
10975
|
+
import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute14, join as join23, resolve as resolve14, sep as sep5 } from "path";
|
|
10976
10976
|
|
|
10977
10977
|
// node_modules/yaml/dist/index.js
|
|
10978
10978
|
var composer = require_composer();
|
|
@@ -11448,7 +11448,7 @@ import { isAbsolute as isAbsolute10, join as join20, resolve as resolve10 } from
|
|
|
11448
11448
|
// package.json
|
|
11449
11449
|
var package_default = {
|
|
11450
11450
|
name: "@hasna/skills",
|
|
11451
|
-
version: "0.9.
|
|
11451
|
+
version: "0.9.11",
|
|
11452
11452
|
description: "Skills library for AI coding agents",
|
|
11453
11453
|
type: "module",
|
|
11454
11454
|
bin: {
|
|
@@ -15161,8 +15161,76 @@ function captureClaudeSettings(path, budget = { remaining: 256 * 1024 * 1024 })
|
|
|
15161
15161
|
return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readSettings(path, budget)) };
|
|
15162
15162
|
}
|
|
15163
15163
|
|
|
15164
|
+
// src/lib/codex-hook-discovery-recovery.ts
|
|
15165
|
+
import { createHash as createHash14 } from "crypto";
|
|
15166
|
+
|
|
15167
|
+
// src/lib/codex-hook-trust-files.ts
|
|
15168
|
+
import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, fsyncSync as fsyncSync3, lstatSync as lstatSync17, openSync as openSync11, readSync as readSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
15169
|
+
import { createHash as createHash13 } from "crypto";
|
|
15170
|
+
import { dirname as dirname14 } from "path";
|
|
15171
|
+
function need4(value, code) {
|
|
15172
|
+
if (!value)
|
|
15173
|
+
throw new Error(`CODEX_HOOK_TRUST_${code}`);
|
|
15174
|
+
}
|
|
15175
|
+
var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
|
|
15176
|
+
function safeParents(file) {
|
|
15177
|
+
for (let p = dirname14(file);; p = dirname14(p)) {
|
|
15178
|
+
const s = lstatSync17(p);
|
|
15179
|
+
need4(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
|
|
15180
|
+
need4((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
|
|
15181
|
+
if (dirname14(p) === p)
|
|
15182
|
+
break;
|
|
15183
|
+
}
|
|
15184
|
+
}
|
|
15185
|
+
function snapshot(file, privateFile = false, options = {}) {
|
|
15186
|
+
const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
|
|
15187
|
+
safeParents(file);
|
|
15188
|
+
const initial = lstatSync17(file, { bigint: true });
|
|
15189
|
+
need4(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
|
|
15190
|
+
need4(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
|
|
15191
|
+
const fd = openSync11(file, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
|
|
15192
|
+
try {
|
|
15193
|
+
const before = fstatSync11(fd, { bigint: true });
|
|
15194
|
+
need4(sameStat(initial, before), "INPUT_CHANGED");
|
|
15195
|
+
const bytes = Buffer.alloc(Number(before.size) + 1);
|
|
15196
|
+
let length = 0;
|
|
15197
|
+
while (length < bytes.length) {
|
|
15198
|
+
const n2 = readSync9(fd, bytes, length, bytes.length - length, null);
|
|
15199
|
+
if (!n2)
|
|
15200
|
+
break;
|
|
15201
|
+
length += n2;
|
|
15202
|
+
}
|
|
15203
|
+
need4(length === Number(before.size) && sameStat(before, fstatSync11(fd, { bigint: true })) && sameStat(before, lstatSync17(file, { bigint: true })), "INPUT_CHANGED");
|
|
15204
|
+
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
15205
|
+
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash13("sha256").update(content).digest("hex") };
|
|
15206
|
+
} finally {
|
|
15207
|
+
closeSync11(fd);
|
|
15208
|
+
}
|
|
15209
|
+
}
|
|
15210
|
+
function unchanged2(before) {
|
|
15211
|
+
const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
|
|
15212
|
+
need4(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
|
|
15213
|
+
}
|
|
15214
|
+
|
|
15215
|
+
// src/lib/codex-hook-discovery-recovery.ts
|
|
15216
|
+
var sha = (text2) => createHash14("sha256").update(text2).digest("hex");
|
|
15217
|
+
var proofs = new WeakMap;
|
|
15218
|
+
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
15219
|
+
const proof = proofs.get(witness);
|
|
15220
|
+
need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
15221
|
+
unchanged2(proof.before);
|
|
15222
|
+
unchanged2(proof.current);
|
|
15223
|
+
}
|
|
15224
|
+
function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
|
|
15225
|
+
if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
|
|
15226
|
+
return false;
|
|
15227
|
+
assertCodexHookDiscoveryRecovery(binding, witness);
|
|
15228
|
+
need4(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
|
|
15229
|
+
return true;
|
|
15230
|
+
}
|
|
15231
|
+
|
|
15164
15232
|
// src/lib/agent-discovery.ts
|
|
15165
|
-
var digest = (text2) =>
|
|
15233
|
+
var digest = (text2) => createHash15("sha256").update(text2).digest("hex");
|
|
15166
15234
|
function parseConfig(text2, path, toml = false) {
|
|
15167
15235
|
try {
|
|
15168
15236
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -15176,10 +15244,10 @@ function parseConfig(text2, path, toml = false) {
|
|
|
15176
15244
|
function safe4(path) {
|
|
15177
15245
|
if (!isAbsolute14(path) || path.includes("\x00"))
|
|
15178
15246
|
throw new Error("Expected an absolute native discovery path");
|
|
15179
|
-
for (let cursor = path;; cursor =
|
|
15180
|
-
if (
|
|
15247
|
+
for (let cursor = path;; cursor = dirname15(cursor)) {
|
|
15248
|
+
if (lstatSync18(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
15181
15249
|
throw new Error(`Refusing symlink native discovery input: ${cursor}`);
|
|
15182
|
-
if (
|
|
15250
|
+
if (dirname15(cursor) === cursor)
|
|
15183
15251
|
break;
|
|
15184
15252
|
}
|
|
15185
15253
|
}
|
|
@@ -15187,7 +15255,7 @@ function read(path, changes) {
|
|
|
15187
15255
|
safe4(path);
|
|
15188
15256
|
if (changes?.has(path))
|
|
15189
15257
|
return changes.get(path);
|
|
15190
|
-
const stat2 =
|
|
15258
|
+
const stat2 = lstatSync18(path, { throwIfNoEntry: false });
|
|
15191
15259
|
if (!stat2)
|
|
15192
15260
|
return null;
|
|
15193
15261
|
if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
|
|
@@ -15237,7 +15305,9 @@ function projected(source, changes, budget = discoveryByteBudget()) {
|
|
|
15237
15305
|
throw new Error("Expected native discovery configuration object");
|
|
15238
15306
|
return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field2) => [field2, object4[field2] ?? null]))));
|
|
15239
15307
|
}
|
|
15240
|
-
function verifyAgentDiscovery(binding) {
|
|
15308
|
+
function verifyAgentDiscovery(binding, codexRecovery) {
|
|
15309
|
+
if (codexRecovery)
|
|
15310
|
+
assertCodexHookDiscoveryRecovery(binding, codexRecovery);
|
|
15241
15311
|
if (!binding || !Array.isArray(binding.sources) || !Array.isArray(binding.roots) || binding.sources.length > AGENT_POLICY_LIMITS.discoverySources || binding.roots.length > AGENT_POLICY_LIMITS.discoveryRoots)
|
|
15242
15312
|
throw new Error("Invalid native discovery binding");
|
|
15243
15313
|
if (binding.agent === "hermes" && !binding.directories?.length)
|
|
@@ -15253,7 +15323,8 @@ function verifyAgentDiscovery(binding) {
|
|
|
15253
15323
|
throw new Error("Invalid native discovery projection");
|
|
15254
15324
|
if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
15255
15325
|
throw new Error("Invalid native discovery digest");
|
|
15256
|
-
|
|
15326
|
+
const current = projected(source, undefined, budget);
|
|
15327
|
+
if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
|
|
15257
15328
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
15258
15329
|
}
|
|
15259
15330
|
for (const root2 of binding.roots)
|
|
@@ -15325,21 +15396,21 @@ function resolveAgentDiscovery(options) {
|
|
|
15325
15396
|
const executable = Bun.which("gemini");
|
|
15326
15397
|
if (executable) {
|
|
15327
15398
|
let packageRoot;
|
|
15328
|
-
for (let directory =
|
|
15399
|
+
for (let directory = dirname15(realpathSync4(executable)), depth = 0;depth < 8; directory = dirname15(directory), depth++) {
|
|
15329
15400
|
const path = join23(directory, "package.json"), raw = read(path);
|
|
15330
15401
|
if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
|
|
15331
15402
|
packageRoot = directory;
|
|
15332
15403
|
witness(path);
|
|
15333
15404
|
break;
|
|
15334
15405
|
}
|
|
15335
|
-
if (
|
|
15406
|
+
if (dirname15(directory) === directory)
|
|
15336
15407
|
break;
|
|
15337
15408
|
}
|
|
15338
15409
|
if (!packageRoot)
|
|
15339
15410
|
unresolved("installed Gemini package layout is not recognized");
|
|
15340
15411
|
const builtinRoot = join23(packageRoot, "bundle/builtin");
|
|
15341
15412
|
safe4(builtinRoot);
|
|
15342
|
-
if (!
|
|
15413
|
+
if (!lstatSync18(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
15343
15414
|
unresolved("installed Gemini builtin root is missing");
|
|
15344
15415
|
for (const name of readdirSync9(builtinRoot).sort()) {
|
|
15345
15416
|
const text2 = witness(join23(builtinRoot, name, "SKILL.md"));
|
|
@@ -15379,7 +15450,7 @@ function resolveAgentDiscovery(options) {
|
|
|
15379
15450
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
15380
15451
|
const commands = join23(root2, "commands");
|
|
15381
15452
|
safe4(commands);
|
|
15382
|
-
if (agent === "claude" && (manifest.commands !== undefined ||
|
|
15453
|
+
if (agent === "claude" && (manifest.commands !== undefined || lstatSync18(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length))
|
|
15383
15454
|
unresolved("legacy command discovery requires retirement or a dedicated format adapter");
|
|
15384
15455
|
const rootSkill = witness(join23(root2, "SKILL.md"));
|
|
15385
15456
|
if (rootSkill !== null)
|
|
@@ -15393,7 +15464,7 @@ function resolveAgentDiscovery(options) {
|
|
|
15393
15464
|
const path = resolve14(root2, value);
|
|
15394
15465
|
if (path !== root2 && !path.startsWith(root2 + sep5))
|
|
15395
15466
|
unresolved("plugin skill path escapes its root");
|
|
15396
|
-
roots.add(path.endsWith(`${sep5}SKILL.md`) ?
|
|
15467
|
+
roots.add(path.endsWith(`${sep5}SKILL.md`) ? dirname15(path) : path);
|
|
15397
15468
|
}
|
|
15398
15469
|
}
|
|
15399
15470
|
if (agent === "claude") {
|
|
@@ -15401,7 +15472,7 @@ function resolveAgentDiscovery(options) {
|
|
|
15401
15472
|
const installed = registrations === null ? {} : parseConfig(registrations, join23(home, ".claude/plugins/installed_plugins.json")).plugins;
|
|
15402
15473
|
const commands = canonical4(join23(home, ".claude/commands"));
|
|
15403
15474
|
safe4(commands);
|
|
15404
|
-
if (
|
|
15475
|
+
if (lstatSync18(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length)
|
|
15405
15476
|
unresolved("legacy home command discovery requires a dedicated format adapter");
|
|
15406
15477
|
for (const [id, enabled] of Object.entries(config.enabledPlugins ?? {})) {
|
|
15407
15478
|
if (enabled === false)
|
|
@@ -15443,12 +15514,12 @@ function resolveAgentDiscovery(options) {
|
|
|
15443
15514
|
}
|
|
15444
15515
|
const localPlugins = join23(home, ".config/opencode/plugins");
|
|
15445
15516
|
safe4(localPlugins);
|
|
15446
|
-
if (
|
|
15517
|
+
if (lstatSync18(localPlugins, { throwIfNoEntry: false }) && readdirSync9(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
15447
15518
|
unresolved("additional local plugin hooks require review");
|
|
15448
15519
|
} else if (agent === "hermes") {
|
|
15449
15520
|
for (const directory of [join23(home, ".hermes/plugins"), join23(home, ".hermes/hermes-agent")]) {
|
|
15450
15521
|
safe4(directory);
|
|
15451
|
-
if (
|
|
15522
|
+
if (lstatSync18(directory, { throwIfNoEntry: false }))
|
|
15452
15523
|
unresolved("installed runtime/plugin discovery requires reviewed source bindings");
|
|
15453
15524
|
}
|
|
15454
15525
|
if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
|
|
@@ -15458,11 +15529,11 @@ function resolveAgentDiscovery(options) {
|
|
|
15458
15529
|
unresolved("custom extension or skill paths");
|
|
15459
15530
|
const directory = join23(home, ".gemini/extensions");
|
|
15460
15531
|
safe4(directory);
|
|
15461
|
-
if (
|
|
15532
|
+
if (lstatSync18(directory, { throwIfNoEntry: false }))
|
|
15462
15533
|
for (const name of readdirSync9(directory)) {
|
|
15463
15534
|
const root2 = join23(directory, name);
|
|
15464
15535
|
safe4(root2);
|
|
15465
|
-
if (!
|
|
15536
|
+
if (!lstatSync18(root2)?.isDirectory())
|
|
15466
15537
|
continue;
|
|
15467
15538
|
const raw = witness(join23(root2, "gemini-extension.json"));
|
|
15468
15539
|
if (raw === null)
|
|
@@ -15617,7 +15688,7 @@ function disableCodexBundledSkills(text2) {
|
|
|
15617
15688
|
}
|
|
15618
15689
|
|
|
15619
15690
|
// src/lib/agent-integration.ts
|
|
15620
|
-
var
|
|
15691
|
+
var sha2 = (data) => createHash16("sha256").update(data).digest("hex");
|
|
15621
15692
|
var ROOTS = [
|
|
15622
15693
|
["claude", ".claude/skills"],
|
|
15623
15694
|
["codex", ".codex/skills"],
|
|
@@ -15638,9 +15709,9 @@ var ROOTS = [
|
|
|
15638
15709
|
function assertSafePath(path) {
|
|
15639
15710
|
let cursor = resolve15(path);
|
|
15640
15711
|
while (true) {
|
|
15641
|
-
if (
|
|
15712
|
+
if (lstatSync19(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
15642
15713
|
throw new Error(`Refusing symlink path: ${cursor}`);
|
|
15643
|
-
const parent =
|
|
15714
|
+
const parent = dirname16(cursor);
|
|
15644
15715
|
if (parent === cursor)
|
|
15645
15716
|
return;
|
|
15646
15717
|
cursor = parent;
|
|
@@ -15649,12 +15720,12 @@ function assertSafePath(path) {
|
|
|
15649
15720
|
function readRootAlias(home, agent) {
|
|
15650
15721
|
assertSafePath(home);
|
|
15651
15722
|
const realHome = realpathSync5(home), alias = join25(realHome, `.${agent}`);
|
|
15652
|
-
const stat2 =
|
|
15723
|
+
const stat2 = lstatSync19(alias, { throwIfNoEntry: false, bigint: true });
|
|
15653
15724
|
if (!stat2?.isSymbolicLink())
|
|
15654
15725
|
throw new Error(`Agent root alias changed after planning: ${alias}`);
|
|
15655
|
-
const link = readlinkSync2(alias), targetPath = resolve15(
|
|
15726
|
+
const link = readlinkSync2(alias), targetPath = resolve15(dirname16(alias), link);
|
|
15656
15727
|
assertSafePath(targetPath);
|
|
15657
|
-
const targetStat =
|
|
15728
|
+
const targetStat = lstatSync19(targetPath, { throwIfNoEntry: false });
|
|
15658
15729
|
if (!targetStat?.isDirectory())
|
|
15659
15730
|
throw new Error(`Agent root alias must target an existing directory: ${alias}`);
|
|
15660
15731
|
const target = realpathSync5(targetPath), inside = relative5(realHome, target);
|
|
@@ -15667,7 +15738,7 @@ function rootAliases(home, allowed = false) {
|
|
|
15667
15738
|
return [];
|
|
15668
15739
|
const result = [];
|
|
15669
15740
|
for (const agent of ["claude", "codex"])
|
|
15670
|
-
if (
|
|
15741
|
+
if (lstatSync19(join25(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
|
|
15671
15742
|
result.push(readRootAlias(home, agent));
|
|
15672
15743
|
if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep6) || other.target.startsWith(item.target + sep6))))
|
|
15673
15744
|
throw new Error("Agent root alias targets must not overlap");
|
|
@@ -15688,35 +15759,35 @@ function recheckRootAliases(aliases) {
|
|
|
15688
15759
|
}
|
|
15689
15760
|
function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
15690
15761
|
assertSafePath(path);
|
|
15691
|
-
const before =
|
|
15762
|
+
const before = lstatSync19(path);
|
|
15692
15763
|
if (!before.isFile() || before.size > maximum)
|
|
15693
15764
|
throw new Error("Unsupported or oversized native skill file");
|
|
15694
|
-
const descriptor =
|
|
15765
|
+
const descriptor = openSync12(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
|
|
15695
15766
|
try {
|
|
15696
|
-
const opened =
|
|
15767
|
+
const opened = fstatSync12(descriptor);
|
|
15697
15768
|
if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size)
|
|
15698
15769
|
throw new Error("Native skill file changed while reading");
|
|
15699
15770
|
const bytes = Buffer.allocUnsafe(before.size + 1);
|
|
15700
15771
|
let length = 0;
|
|
15701
15772
|
while (length < bytes.length) {
|
|
15702
|
-
const count =
|
|
15773
|
+
const count = readSync10(descriptor, bytes, length, bytes.length - length, null);
|
|
15703
15774
|
if (!count)
|
|
15704
15775
|
break;
|
|
15705
15776
|
length += count;
|
|
15706
15777
|
}
|
|
15707
|
-
const after =
|
|
15778
|
+
const after = fstatSync12(descriptor);
|
|
15708
15779
|
if (length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
|
|
15709
15780
|
throw new Error("Native skill file changed while reading");
|
|
15710
15781
|
return bytes.subarray(0, length);
|
|
15711
15782
|
} finally {
|
|
15712
|
-
|
|
15783
|
+
closeSync12(descriptor);
|
|
15713
15784
|
}
|
|
15714
15785
|
}
|
|
15715
15786
|
function treeHash(root2) {
|
|
15716
|
-
const hash2 =
|
|
15787
|
+
const hash2 = createHash16("sha256");
|
|
15717
15788
|
let bytes = 0, files = 0;
|
|
15718
15789
|
function visit2(path) {
|
|
15719
|
-
const stat2 =
|
|
15790
|
+
const stat2 = lstatSync19(path);
|
|
15720
15791
|
if (stat2.isSymbolicLink())
|
|
15721
15792
|
throw new Error(`Refusing symlink in skill: ${path}`);
|
|
15722
15793
|
const rel = relative5(root2, path);
|
|
@@ -15746,7 +15817,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
15746
15817
|
const selected = [];
|
|
15747
15818
|
for (const target of manifest.targets) {
|
|
15748
15819
|
const projectRoot = resolve15(target.projectRoot), normalized = relative5(projectRoot, resolve15(projectRoot, target.path));
|
|
15749
|
-
if (!isAbsolute15(target.projectRoot) || projectRoot !== target.projectRoot || !existsSync11(projectRoot) || !
|
|
15820
|
+
if (!isAbsolute15(target.projectRoot) || projectRoot !== target.projectRoot || !existsSync11(projectRoot) || !lstatSync19(projectRoot).isDirectory())
|
|
15750
15821
|
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
15751
15822
|
assertSafePath(projectRoot);
|
|
15752
15823
|
if (isAbsolute15(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep6}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
|
|
@@ -15775,7 +15846,7 @@ function projectAncestorDirectories(projects) {
|
|
|
15775
15846
|
if (depth >= 100)
|
|
15776
15847
|
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
15777
15848
|
directories.add(path);
|
|
15778
|
-
const parent =
|
|
15849
|
+
const parent = dirname16(path);
|
|
15779
15850
|
if (parent === path)
|
|
15780
15851
|
break;
|
|
15781
15852
|
path = parent;
|
|
@@ -15806,9 +15877,9 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
15806
15877
|
}
|
|
15807
15878
|
function emptySiblingCacheAlias(path, parent) {
|
|
15808
15879
|
const link = readlinkSync2(path), target = resolve15(parent, link);
|
|
15809
|
-
if (
|
|
15880
|
+
if (dirname16(target) !== parent || (isAbsolute15(link) ? link !== target : dirname16(link) !== "."))
|
|
15810
15881
|
return false;
|
|
15811
|
-
if (!
|
|
15882
|
+
if (!lstatSync19(target, { throwIfNoEntry: false })?.isDirectory())
|
|
15812
15883
|
return false;
|
|
15813
15884
|
const scan = cacheScans.get(target);
|
|
15814
15885
|
return Boolean(scan?.complete && !scan.hasSkills && scan.entries <= maxAliasProofEntries);
|
|
@@ -15820,7 +15891,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
15820
15891
|
assertSafePath(path);
|
|
15821
15892
|
if (!existsSync11(path))
|
|
15822
15893
|
return scan;
|
|
15823
|
-
const stat2 =
|
|
15894
|
+
const stat2 = lstatSync19(path);
|
|
15824
15895
|
if (stat2.isFile())
|
|
15825
15896
|
return scan;
|
|
15826
15897
|
if (!stat2.isDirectory())
|
|
@@ -15866,7 +15937,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
15866
15937
|
if (isVendor && !options.includeVendor)
|
|
15867
15938
|
continue;
|
|
15868
15939
|
const child = join25(path, name);
|
|
15869
|
-
if (isVendor &&
|
|
15940
|
+
if (isVendor && lstatSync19(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
15870
15941
|
const target = statSync8(child, { throwIfNoEntry: false });
|
|
15871
15942
|
if (target?.isFile() || pluginCache && target?.isDirectory() && emptySiblingCacheAlias(child, path)) {
|
|
15872
15943
|
scan.entries++;
|
|
@@ -16076,7 +16147,7 @@ function planAgentIntegration(options) {
|
|
|
16076
16147
|
throw new Error("Refusing to overwrite an unrecognized Hermes supervisor");
|
|
16077
16148
|
if (supervisorBefore !== supervisorAfter)
|
|
16078
16149
|
changes.push({ path: supervisorPath, before: supervisorBefore, after: supervisorAfter });
|
|
16079
|
-
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256:
|
|
16150
|
+
const supervisor = { path: supervisorPath, runtime: process.execPath, sha256: sha2(supervisorAfter) };
|
|
16080
16151
|
const after2 = configureHermesHooks(before, supervisor, previous);
|
|
16081
16152
|
if (before !== after2)
|
|
16082
16153
|
changes.push({ path, before, after: after2 });
|
|
@@ -16123,7 +16194,7 @@ function planAgentIntegration(options) {
|
|
|
16123
16194
|
agents: [...new Set([...priorAgents, ...options.agents])].sort(),
|
|
16124
16195
|
home: resolve15(home),
|
|
16125
16196
|
includeVendor: true,
|
|
16126
|
-
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join25(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256:
|
|
16197
|
+
...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join25(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha2(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
|
|
16127
16198
|
commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
|
|
16128
16199
|
profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
|
|
16129
16200
|
disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join25(home, ".codex", "skills", ".system"), aliases) + sep6)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
|
|
@@ -16138,10 +16209,10 @@ function planAgentIntegration(options) {
|
|
|
16138
16209
|
}
|
|
16139
16210
|
function atomicWrite2(path, content) {
|
|
16140
16211
|
assertSafePath(path);
|
|
16141
|
-
mkdirSync7(
|
|
16212
|
+
mkdirSync7(dirname16(path), { recursive: true, mode: 448 });
|
|
16142
16213
|
const temporary = `${path}.skills-${randomUUID3()}`;
|
|
16143
16214
|
try {
|
|
16144
|
-
|
|
16215
|
+
writeFileSync6(temporary, content, { mode: 384, flag: "wx" });
|
|
16145
16216
|
renameSync4(temporary, path);
|
|
16146
16217
|
} finally {
|
|
16147
16218
|
if (existsSync11(temporary))
|
|
@@ -16175,8 +16246,8 @@ function applyAgentIntegration(plan) {
|
|
|
16175
16246
|
chmodSync(backupRoot, 448);
|
|
16176
16247
|
for (const [index, change] of plan.changes.entries())
|
|
16177
16248
|
if (change.before !== null) {
|
|
16178
|
-
const backup = join25(backupRoot, `${index}-${
|
|
16179
|
-
|
|
16249
|
+
const backup = join25(backupRoot, `${index}-${sha2(change.path).slice(0, 12)}.backup`);
|
|
16250
|
+
writeFileSync6(backup, change.before, { mode: 384, flag: "wx" });
|
|
16180
16251
|
backups.push(backup);
|
|
16181
16252
|
}
|
|
16182
16253
|
try {
|
|
@@ -16184,7 +16255,7 @@ function applyAgentIntegration(plan) {
|
|
|
16184
16255
|
recheckRootAliases(aliases);
|
|
16185
16256
|
if (currentText(change.path) !== change.before)
|
|
16186
16257
|
throw new Error(`Configuration changed after planning: ${change.path}`);
|
|
16187
|
-
for (let directory =
|
|
16258
|
+
for (let directory = dirname16(change.path);!existsSync11(directory); directory = dirname16(directory))
|
|
16188
16259
|
createdDirectories.add(directory);
|
|
16189
16260
|
const after = change.after;
|
|
16190
16261
|
atomicWrite2(change.path, after);
|
|
@@ -16193,7 +16264,7 @@ function applyAgentIntegration(plan) {
|
|
|
16193
16264
|
recheckRootAliases(aliases);
|
|
16194
16265
|
for (const binding of plan.discoveryAfter ?? [])
|
|
16195
16266
|
verifyAgentDiscovery(binding);
|
|
16196
|
-
atomicWrite2(join25(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null :
|
|
16267
|
+
atomicWrite2(join25(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha2(change.before), afterHash: sha2(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
|
|
16197
16268
|
`);
|
|
16198
16269
|
} catch (error) {
|
|
16199
16270
|
for (const change of written.reverse()) {
|
|
@@ -16224,11 +16295,11 @@ function applyAgentIntegration(plan) {
|
|
|
16224
16295
|
}
|
|
16225
16296
|
function syncArchiveDirectory(path) {
|
|
16226
16297
|
assertSafePath(path);
|
|
16227
|
-
const descriptor =
|
|
16298
|
+
const descriptor = openSync12(path, constants11.O_RDONLY | constants11.O_DIRECTORY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
|
|
16228
16299
|
try {
|
|
16229
|
-
|
|
16300
|
+
fsyncSync4(descriptor);
|
|
16230
16301
|
} finally {
|
|
16231
|
-
|
|
16302
|
+
closeSync12(descriptor);
|
|
16232
16303
|
}
|
|
16233
16304
|
}
|
|
16234
16305
|
function writeArchiveJournal(path, value) {
|
|
@@ -16236,18 +16307,18 @@ function writeArchiveJournal(path, value) {
|
|
|
16236
16307
|
const temporary = `${path}.skills-${randomUUID3()}`;
|
|
16237
16308
|
let descriptor;
|
|
16238
16309
|
try {
|
|
16239
|
-
descriptor =
|
|
16240
|
-
|
|
16310
|
+
descriptor = openSync12(temporary, constants11.O_WRONLY | constants11.O_CREAT | constants11.O_EXCL | constants11.O_NOFOLLOW, 384);
|
|
16311
|
+
writeFileSync6(descriptor, `${JSON.stringify(value)}
|
|
16241
16312
|
`);
|
|
16242
|
-
|
|
16243
|
-
|
|
16313
|
+
fsyncSync4(descriptor);
|
|
16314
|
+
closeSync12(descriptor);
|
|
16244
16315
|
descriptor = undefined;
|
|
16245
16316
|
renameSync4(temporary, path);
|
|
16246
|
-
syncArchiveDirectory(
|
|
16317
|
+
syncArchiveDirectory(dirname16(path));
|
|
16247
16318
|
} finally {
|
|
16248
16319
|
if (descriptor !== undefined)
|
|
16249
|
-
|
|
16250
|
-
if (
|
|
16320
|
+
closeSync12(descriptor);
|
|
16321
|
+
if (lstatSync19(temporary, { throwIfNoEntry: false }))
|
|
16251
16322
|
unlinkSync4(temporary);
|
|
16252
16323
|
}
|
|
16253
16324
|
}
|
|
@@ -16272,8 +16343,8 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16272
16343
|
const operationId = randomUUID3(), archiveRoot = join25(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join25(archiveRoot, "receipt.json");
|
|
16273
16344
|
const entries = selected.map((entry, index) => ({
|
|
16274
16345
|
source: entry.vendor ? join25(entry.path, "SKILL.md") : entry.path,
|
|
16275
|
-
archive: join25(archiveRoot, `${index}-${
|
|
16276
|
-
hash: entry.vendor ?
|
|
16346
|
+
archive: join25(archiveRoot, `${index}-${sha2(entry.path).slice(0, 12)}`),
|
|
16347
|
+
hash: entry.vendor ? sha2(readNativeBytes(join25(entry.path, "SKILL.md"))) : entry.hash,
|
|
16277
16348
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
16278
16349
|
status: "planned"
|
|
16279
16350
|
}));
|
|
@@ -16281,22 +16352,22 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16281
16352
|
const moved = [];
|
|
16282
16353
|
const verifyArchive = (entry) => {
|
|
16283
16354
|
assertSafePath(entry.archive);
|
|
16284
|
-
const stat2 =
|
|
16355
|
+
const stat2 = lstatSync19(entry.archive);
|
|
16285
16356
|
if (entry.discoveryOnly ? !stat2.isFile() : !stat2.isDirectory())
|
|
16286
16357
|
throw new Error("Archive type changed");
|
|
16287
|
-
if ((entry.discoveryOnly ?
|
|
16358
|
+
if ((entry.discoveryOnly ? sha2(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
|
|
16288
16359
|
throw new Error("Archive bytes changed");
|
|
16289
16360
|
};
|
|
16290
16361
|
assertSafePath(archiveRoot);
|
|
16291
16362
|
const created = [];
|
|
16292
|
-
for (let path = archiveRoot;!existsSync11(path); path =
|
|
16363
|
+
for (let path = archiveRoot;!existsSync11(path); path = dirname16(path))
|
|
16293
16364
|
created.push(path);
|
|
16294
16365
|
mkdirSync7(archiveRoot, { recursive: true, mode: 448 });
|
|
16295
16366
|
try {
|
|
16296
16367
|
for (const directory of created)
|
|
16297
16368
|
syncArchiveDirectory(directory);
|
|
16298
16369
|
if (created.length)
|
|
16299
|
-
syncArchiveDirectory(
|
|
16370
|
+
syncArchiveDirectory(dirname16(created.at(-1)));
|
|
16300
16371
|
writeArchiveJournal(receiptPath, journal);
|
|
16301
16372
|
for (const [index, entry] of selected.entries()) {
|
|
16302
16373
|
recheckRootAliases(aliases);
|
|
@@ -16308,7 +16379,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16308
16379
|
writeArchiveJournal(receiptPath, journal);
|
|
16309
16380
|
renameSync4(move.source, move.archive);
|
|
16310
16381
|
moved.push(move);
|
|
16311
|
-
syncArchiveDirectory(
|
|
16382
|
+
syncArchiveDirectory(dirname16(move.source));
|
|
16312
16383
|
syncArchiveDirectory(archiveRoot);
|
|
16313
16384
|
verifyArchive(move);
|
|
16314
16385
|
move.status = "archived";
|
|
@@ -16329,7 +16400,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16329
16400
|
recheckRootAliases(aliases);
|
|
16330
16401
|
assertSafePath(entry.source);
|
|
16331
16402
|
assertSafePath(entry.archive);
|
|
16332
|
-
if (
|
|
16403
|
+
if (lstatSync19(entry.source, { throwIfNoEntry: false })) {
|
|
16333
16404
|
entry.status = "recovery-required";
|
|
16334
16405
|
entry.conflict = "source-occupied";
|
|
16335
16406
|
continue;
|
|
@@ -16343,10 +16414,10 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16343
16414
|
}
|
|
16344
16415
|
if (entry.discoveryOnly) {
|
|
16345
16416
|
linkSync2(entry.archive, entry.source);
|
|
16346
|
-
syncArchiveDirectory(
|
|
16417
|
+
syncArchiveDirectory(dirname16(entry.source));
|
|
16347
16418
|
unlinkSync4(entry.archive);
|
|
16348
16419
|
} else {
|
|
16349
|
-
if (
|
|
16420
|
+
if (lstatSync19(entry.source, { throwIfNoEntry: false })) {
|
|
16350
16421
|
entry.status = "recovery-required";
|
|
16351
16422
|
entry.conflict = "source-occupied";
|
|
16352
16423
|
continue;
|
|
@@ -16354,7 +16425,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
16354
16425
|
renameSync4(entry.archive, entry.source);
|
|
16355
16426
|
}
|
|
16356
16427
|
entry.status = "restored";
|
|
16357
|
-
syncArchiveDirectory(
|
|
16428
|
+
syncArchiveDirectory(dirname16(entry.source));
|
|
16358
16429
|
syncArchiveDirectory(archiveRoot);
|
|
16359
16430
|
} catch {
|
|
16360
16431
|
entry.status = "recovery-required";
|
|
@@ -16393,7 +16464,7 @@ function isSkillsOwnershipMarker(marker) {
|
|
|
16393
16464
|
function hasSkillsOwnershipMarker(dir) {
|
|
16394
16465
|
const path = join26(dir, SYNC_MARKER_FILE);
|
|
16395
16466
|
try {
|
|
16396
|
-
if (!
|
|
16467
|
+
if (!lstatSync20(path).isFile())
|
|
16397
16468
|
return false;
|
|
16398
16469
|
const marker = JSON.parse(readFileSync15(path, "utf8"));
|
|
16399
16470
|
return isSkillsOwnershipMarker(marker);
|
|
@@ -16619,7 +16690,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
16619
16690
|
const action = dirExists ? "update" : "create";
|
|
16620
16691
|
if (options.dryRun)
|
|
16621
16692
|
return { action, path: skillMdPath };
|
|
16622
|
-
const parentDir =
|
|
16693
|
+
const parentDir = dirname17(dir);
|
|
16623
16694
|
mkdirSync8(parentDir, { recursive: true });
|
|
16624
16695
|
const transactionDir = mkdtempSync2(join26(parentDir, `.hasna-skills-write-${basename9(dir)}-`));
|
|
16625
16696
|
const candidateDir = join26(transactionDir, "candidate");
|
|
@@ -16641,10 +16712,10 @@ function writeManagedSkillDir(dir, skillMd, options) {
|
|
|
16641
16712
|
} else {
|
|
16642
16713
|
mkdirSync8(candidateDir, { recursive: true });
|
|
16643
16714
|
}
|
|
16644
|
-
|
|
16715
|
+
writeFileSync7(candidateSkillMdPath, skillMd.endsWith(`
|
|
16645
16716
|
`) ? skillMd : `${skillMd}
|
|
16646
16717
|
`);
|
|
16647
|
-
|
|
16718
|
+
writeFileSync7(candidateMarkerPath, `${JSON.stringify(marker, null, 2)}
|
|
16648
16719
|
`);
|
|
16649
16720
|
if (dirExists) {
|
|
16650
16721
|
originalMoved = true;
|
|
@@ -16709,7 +16780,7 @@ function normalizeRequested(names) {
|
|
|
16709
16780
|
}
|
|
16710
16781
|
|
|
16711
16782
|
// src/lib/project-state.ts
|
|
16712
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as
|
|
16783
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
|
|
16713
16784
|
import { join as join27 } from "path";
|
|
16714
16785
|
var VALID_PIN_SOURCES = [
|
|
16715
16786
|
"official",
|
|
@@ -16758,7 +16829,7 @@ function saveProjectConfig(config, targetDir = process.cwd()) {
|
|
|
16758
16829
|
const dir = getProjectStateDir(targetDir);
|
|
16759
16830
|
mkdirSync9(dir, { recursive: true });
|
|
16760
16831
|
const normalized = normalizeProjectConfig({ ...config, updatedAt: new Date().toISOString() });
|
|
16761
|
-
|
|
16832
|
+
writeFileSync8(getProjectConfigPath(targetDir), JSON.stringify(normalized, null, 2) + `
|
|
16762
16833
|
`);
|
|
16763
16834
|
}
|
|
16764
16835
|
function pinProjectSkill(name, details = {}, targetDir = process.cwd()) {
|
|
@@ -17110,8 +17181,8 @@ function loadProjectConfigCompat(targetDir) {
|
|
|
17110
17181
|
return loadProjectConfig(targetDir);
|
|
17111
17182
|
}
|
|
17112
17183
|
// src/lib/run-state.ts
|
|
17113
|
-
import { createHash as
|
|
17114
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync18, readdirSync as readdirSync12, statSync as statSync10, writeFileSync as
|
|
17184
|
+
import { createHash as createHash17, randomBytes } from "crypto";
|
|
17185
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync18, readdirSync as readdirSync12, statSync as statSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
17115
17186
|
import { extname, join as join29, relative as relative6 } from "path";
|
|
17116
17187
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
17117
17188
|
const now = new Date;
|
|
@@ -17176,15 +17247,15 @@ function updateSkillRun(context, patch) {
|
|
|
17176
17247
|
return context.record;
|
|
17177
17248
|
}
|
|
17178
17249
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
17179
|
-
|
|
17180
|
-
|
|
17250
|
+
writeFileSync9(join29(context.logsDir, "stdout.log"), stdout);
|
|
17251
|
+
writeFileSync9(join29(context.logsDir, "stderr.log"), stderr);
|
|
17181
17252
|
}
|
|
17182
17253
|
function appendRunEvent(context, event, data = {}) {
|
|
17183
17254
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
17184
17255
|
`;
|
|
17185
17256
|
const path = join29(context.runDir, "events.ndjson");
|
|
17186
17257
|
const previous = existsSync15(path) ? readFileSync18(path, "utf-8") : "";
|
|
17187
|
-
|
|
17258
|
+
writeFileSync9(path, previous + line);
|
|
17188
17259
|
}
|
|
17189
17260
|
function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
17190
17261
|
const runsRoot = join29(getProjectStateDir(targetDir), "runs");
|
|
@@ -17220,11 +17291,11 @@ function getRunExportDir(runId, skill, targetDir = process.cwd()) {
|
|
|
17220
17291
|
return join29(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
|
|
17221
17292
|
}
|
|
17222
17293
|
function writeRunRecord(context) {
|
|
17223
|
-
|
|
17294
|
+
writeFileSync9(join29(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
17224
17295
|
`);
|
|
17225
17296
|
}
|
|
17226
17297
|
function writeArtifactsManifest(context, artifacts) {
|
|
17227
|
-
|
|
17298
|
+
writeFileSync9(join29(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
17228
17299
|
`);
|
|
17229
17300
|
}
|
|
17230
17301
|
function collectRunArtifacts(context) {
|
|
@@ -17237,7 +17308,7 @@ function collectRunArtifacts(context) {
|
|
|
17237
17308
|
artifacts.push({
|
|
17238
17309
|
path: toProjectRelative(context.targetDir, path),
|
|
17239
17310
|
mime: mimeForPath(path),
|
|
17240
|
-
sha256:
|
|
17311
|
+
sha256: createHash17("sha256").update(bytes).digest("hex"),
|
|
17241
17312
|
sizeBytes: stat2.size
|
|
17242
17313
|
});
|
|
17243
17314
|
}
|
|
@@ -36656,7 +36727,7 @@ function clone2(value) {
|
|
|
36656
36727
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
36657
36728
|
}
|
|
36658
36729
|
// src/lib/canonical-json.ts
|
|
36659
|
-
import { createHash as
|
|
36730
|
+
import { createHash as createHash18 } from "crypto";
|
|
36660
36731
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
36661
36732
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
36662
36733
|
}
|
|
@@ -36700,7 +36771,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
36700
36771
|
return visit3(value, 0);
|
|
36701
36772
|
}
|
|
36702
36773
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
36703
|
-
return
|
|
36774
|
+
return createHash18("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
36704
36775
|
}
|
|
36705
36776
|
|
|
36706
36777
|
// src/lib/remote-recurring-contract.ts
|
|
@@ -36843,8 +36914,8 @@ var inputs = {
|
|
|
36843
36914
|
};
|
|
36844
36915
|
function recurringInput(action, raw) {
|
|
36845
36916
|
try {
|
|
36846
|
-
const
|
|
36847
|
-
return inputs[action].parse(
|
|
36917
|
+
const snapshot2 = JSON.parse(canonicalJson2(raw, action === "preview" ? 1048576 : 4096));
|
|
36918
|
+
return inputs[action].parse(snapshot2);
|
|
36848
36919
|
} catch {
|
|
36849
36920
|
throw new RecurringInputError;
|
|
36850
36921
|
}
|
|
@@ -38750,7 +38821,7 @@ async function createRemoteSkillsClient(env = process.env) {
|
|
|
38750
38821
|
return connection ? new RemoteSkillsClient(connection.apiKey, connection.apiOrigin) : null;
|
|
38751
38822
|
}
|
|
38752
38823
|
// src/lib/scheduler.ts
|
|
38753
|
-
import { existsSync as existsSync16, readFileSync as readFileSync19, writeFileSync as
|
|
38824
|
+
import { existsSync as existsSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11 } from "fs";
|
|
38754
38825
|
import { join as join30 } from "path";
|
|
38755
38826
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
38756
38827
|
return join30(targetDir, ".skills", "schedules.json");
|
|
@@ -38769,7 +38840,7 @@ function saveSchedules(data, targetDir = process.cwd()) {
|
|
|
38769
38840
|
const dir = join30(targetDir, ".skills");
|
|
38770
38841
|
if (!existsSync16(dir))
|
|
38771
38842
|
mkdirSync11(dir, { recursive: true });
|
|
38772
|
-
|
|
38843
|
+
writeFileSync10(path, JSON.stringify(data, null, 2));
|
|
38773
38844
|
}
|
|
38774
38845
|
function validateCronField(expr, min, max, label) {
|
|
38775
38846
|
for (const part of expr.split(",")) {
|
|
@@ -38954,7 +39025,7 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
38954
39025
|
saveSchedules(data, targetDir);
|
|
38955
39026
|
}
|
|
38956
39027
|
// src/lib/revision.ts
|
|
38957
|
-
import { createHash as
|
|
39028
|
+
import { createHash as createHash19 } from "crypto";
|
|
38958
39029
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
38959
39030
|
function revisionIdOf(content) {
|
|
38960
39031
|
const canonicalContent = {
|
|
@@ -38976,14 +39047,14 @@ function revisionIdOf(content) {
|
|
|
38976
39047
|
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
38977
39048
|
}
|
|
38978
39049
|
const canonical4 = JSON.stringify(canonicalContent);
|
|
38979
|
-
return
|
|
39050
|
+
return createHash19("sha256").update(canonical4).digest("hex");
|
|
38980
39051
|
}
|
|
38981
39052
|
function revisionIdOfRecord(record7) {
|
|
38982
39053
|
return revisionIdOf(record7);
|
|
38983
39054
|
}
|
|
38984
39055
|
// src/lib/pull.ts
|
|
38985
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync20, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as
|
|
38986
|
-
import { dirname as
|
|
39056
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync20, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync11 } from "fs";
|
|
39057
|
+
import { dirname as dirname18, join as join31 } from "path";
|
|
38987
39058
|
|
|
38988
39059
|
// src/lib/skill-bundles.ts
|
|
38989
39060
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -39325,8 +39396,8 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
39325
39396
|
try {
|
|
39326
39397
|
for (const entry of entries) {
|
|
39327
39398
|
const destination = join31(staging, entry.path);
|
|
39328
|
-
mkdirSync12(
|
|
39329
|
-
|
|
39399
|
+
mkdirSync12(dirname18(destination), { recursive: true });
|
|
39400
|
+
writeFileSync11(destination, entry.bytes, { mode: entry.mode });
|
|
39330
39401
|
}
|
|
39331
39402
|
writePullMarker(staging, {
|
|
39332
39403
|
skill: name,
|
|
@@ -39367,7 +39438,7 @@ function writePullMarker(dir, record7) {
|
|
|
39367
39438
|
...record7.revisionId ? { revisionId: record7.revisionId } : {},
|
|
39368
39439
|
syncedAt: new Date().toISOString()
|
|
39369
39440
|
};
|
|
39370
|
-
|
|
39441
|
+
writeFileSync11(join31(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
39371
39442
|
`);
|
|
39372
39443
|
}
|
|
39373
39444
|
async function safeMeta(client2, slug2) {
|
|
@@ -39517,7 +39588,7 @@ async function requestInvitationEmail(origin, action, input2) {
|
|
|
39517
39588
|
}
|
|
39518
39589
|
|
|
39519
39590
|
// src/lib/remote-private-publications.ts
|
|
39520
|
-
import { createHash as
|
|
39591
|
+
import { createHash as createHash20 } from "crypto";
|
|
39521
39592
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
39522
39593
|
var failures2 = {
|
|
39523
39594
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -39564,7 +39635,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
|
39564
39635
|
var record8 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
39565
39636
|
var exact = (v, keys) => record8(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
|
|
39566
39637
|
var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
|
|
39567
|
-
var publicationSha256 = (bytes) =>
|
|
39638
|
+
var publicationSha256 = (bytes) => createHash20("sha256").update(bytes).digest("hex");
|
|
39568
39639
|
function checkedPublicationDeclaration(value) {
|
|
39569
39640
|
if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash3(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
|
|
39570
39641
|
return bad();
|
|
@@ -40048,7 +40119,7 @@ class RemoteSkillsAuthClient {
|
|
|
40048
40119
|
|
|
40049
40120
|
// src/lib/recurring-recovery.ts
|
|
40050
40121
|
var uuid12 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
40051
|
-
var
|
|
40122
|
+
var sha3 = exports_external.string().regex(/^[a-f0-9]{64}$/);
|
|
40052
40123
|
var common = exports_external.object({
|
|
40053
40124
|
contractVersion: exports_external.literal(1),
|
|
40054
40125
|
apiOrigin: exports_external.string().min(1).max(4096),
|
|
@@ -40061,7 +40132,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
|
|
|
40061
40132
|
common.extend({
|
|
40062
40133
|
operation: exports_external.literal("activate"),
|
|
40063
40134
|
draftId: uuid12,
|
|
40064
|
-
termsSha256:
|
|
40135
|
+
termsSha256: sha3,
|
|
40065
40136
|
approvalDeadline: exports_external.string().datetime(),
|
|
40066
40137
|
approval: recurringActivationSchema
|
|
40067
40138
|
}).strict(),
|
|
@@ -40069,7 +40140,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
|
|
|
40069
40140
|
]);
|
|
40070
40141
|
var stateSchema = exports_external.object({
|
|
40071
40142
|
contractVersion: exports_external.literal(1),
|
|
40072
|
-
intentSha256:
|
|
40143
|
+
intentSha256: sha3,
|
|
40073
40144
|
phase: exports_external.enum(["prepared", "pending", "observed"]),
|
|
40074
40145
|
consentId: uuid12.nullable()
|
|
40075
40146
|
}).strict();
|
|
@@ -40228,8 +40299,8 @@ function findSkillsParityForMcpTool(tool) {
|
|
|
40228
40299
|
return SKILLS_CLI_MCP_PARITY.find((entry) => entry.mcpTools.includes(tool));
|
|
40229
40300
|
}
|
|
40230
40301
|
// src/lib/registry-sync.ts
|
|
40231
|
-
import { mkdirSync as mkdirSync13, writeFileSync as
|
|
40232
|
-
import { dirname as
|
|
40302
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync12 } from "fs";
|
|
40303
|
+
import { dirname as dirname19, relative as relative7 } from "path";
|
|
40233
40304
|
function createRegistrySyncArtifact(options = {}) {
|
|
40234
40305
|
const profile = options.profile ?? "all";
|
|
40235
40306
|
const includeDocs = options.includeDocs ?? true;
|
|
@@ -40286,8 +40357,8 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
40286
40357
|
};
|
|
40287
40358
|
}
|
|
40288
40359
|
function writeRegistrySyncArtifact(path, artifact) {
|
|
40289
|
-
mkdirSync13(
|
|
40290
|
-
|
|
40360
|
+
mkdirSync13(dirname19(path), { recursive: true });
|
|
40361
|
+
writeFileSync12(path, `${JSON.stringify(artifact, null, 2)}
|
|
40291
40362
|
`);
|
|
40292
40363
|
}
|
|
40293
40364
|
function buildDocs(name) {
|
|
@@ -41371,14 +41442,14 @@ function clone3(value) {
|
|
|
41371
41442
|
}
|
|
41372
41443
|
// src/lib/feedback.ts
|
|
41373
41444
|
import { appendFileSync, existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
|
|
41374
|
-
import { dirname as
|
|
41445
|
+
import { dirname as dirname20, join as join32 } from "path";
|
|
41375
41446
|
import { Database } from "bun:sqlite";
|
|
41376
41447
|
function getFeedbackDbPath() {
|
|
41377
41448
|
return join32(getDataDir(), "skills.db");
|
|
41378
41449
|
}
|
|
41379
41450
|
function getFeedbackDb() {
|
|
41380
41451
|
const dbPath = getFeedbackDbPath();
|
|
41381
|
-
const dir =
|
|
41452
|
+
const dir = dirname20(dbPath);
|
|
41382
41453
|
if (!existsSync18(dir))
|
|
41383
41454
|
mkdirSync14(dir, { recursive: true });
|
|
41384
41455
|
const db = new Database(dbPath);
|
|
@@ -41407,7 +41478,7 @@ function saveFeedback(input2) {
|
|
|
41407
41478
|
const category = input2.category ?? "general";
|
|
41408
41479
|
if (isApiMode()) {
|
|
41409
41480
|
const path = join32(getDataDir(), "feedback.jsonl");
|
|
41410
|
-
const dir =
|
|
41481
|
+
const dir = dirname20(path);
|
|
41411
41482
|
if (!existsSync18(dir))
|
|
41412
41483
|
mkdirSync14(dir, { recursive: true });
|
|
41413
41484
|
appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
@@ -41430,16 +41501,16 @@ function isApiMode(env = process.env) {
|
|
|
41430
41501
|
}
|
|
41431
41502
|
}
|
|
41432
41503
|
// src/lib/native-storage.ts
|
|
41433
|
-
import { createHash as
|
|
41504
|
+
import { createHash as createHash21, createHmac as createHmac2 } from "crypto";
|
|
41434
41505
|
import {
|
|
41435
41506
|
existsSync as existsSync19,
|
|
41436
41507
|
mkdirSync as mkdirSync15,
|
|
41437
41508
|
readFileSync as readFileSync21,
|
|
41438
41509
|
readdirSync as readdirSync13,
|
|
41439
41510
|
statSync as statSync11,
|
|
41440
|
-
writeFileSync as
|
|
41511
|
+
writeFileSync as writeFileSync13
|
|
41441
41512
|
} from "fs";
|
|
41442
|
-
import { dirname as
|
|
41513
|
+
import { dirname as dirname21, join as join33, normalize as normalize3, relative as relative8, sep as sep7 } from "path";
|
|
41443
41514
|
var SKILLS_STORAGE_TABLES = [
|
|
41444
41515
|
"skills_sync_records",
|
|
41445
41516
|
"skills_sync_cursors"
|
|
@@ -41606,7 +41677,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
41606
41677
|
files.push({
|
|
41607
41678
|
path: relativePath,
|
|
41608
41679
|
sizeBytes: bytes.byteLength,
|
|
41609
|
-
sha256:
|
|
41680
|
+
sha256: createHash21("sha256").update(bytes).digest("hex"),
|
|
41610
41681
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
41611
41682
|
});
|
|
41612
41683
|
}
|
|
@@ -41617,10 +41688,10 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
41617
41688
|
files: files.sort((a, b) => a.path.localeCompare(b.path))
|
|
41618
41689
|
};
|
|
41619
41690
|
}
|
|
41620
|
-
function importSkillsLocalSnapshot(
|
|
41691
|
+
function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options = {}) {
|
|
41621
41692
|
let written = 0;
|
|
41622
41693
|
let skipped = 0;
|
|
41623
|
-
for (const file2 of
|
|
41694
|
+
for (const file2 of snapshot2.files) {
|
|
41624
41695
|
if (!file2.contentBase64) {
|
|
41625
41696
|
skipped += 1;
|
|
41626
41697
|
continue;
|
|
@@ -41631,12 +41702,12 @@ function importSkillsLocalSnapshot(snapshot, targetDir = process.cwd(), options
|
|
|
41631
41702
|
continue;
|
|
41632
41703
|
}
|
|
41633
41704
|
const bytes = Buffer.from(file2.contentBase64, "base64");
|
|
41634
|
-
const hash4 =
|
|
41705
|
+
const hash4 = createHash21("sha256").update(bytes).digest("hex");
|
|
41635
41706
|
if (hash4 !== file2.sha256) {
|
|
41636
41707
|
throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
|
|
41637
41708
|
}
|
|
41638
|
-
mkdirSync15(
|
|
41639
|
-
|
|
41709
|
+
mkdirSync15(dirname21(absolutePath3), { recursive: true });
|
|
41710
|
+
writeFileSync13(absolutePath3, bytes);
|
|
41640
41711
|
written += 1;
|
|
41641
41712
|
}
|
|
41642
41713
|
return { written, skipped };
|
|
@@ -41733,14 +41804,14 @@ class SkillsPostgresSyncStore {
|
|
|
41733
41804
|
function createSkillsPostgresSyncStore(client2) {
|
|
41734
41805
|
return new SkillsPostgresSyncStore(client2);
|
|
41735
41806
|
}
|
|
41736
|
-
function createSkillsSnapshotSyncRecord(
|
|
41807
|
+
function createSkillsSnapshotSyncRecord(snapshot2, options = {}) {
|
|
41737
41808
|
return {
|
|
41738
41809
|
scope: options.scope ?? "default",
|
|
41739
41810
|
kind: "local-snapshot",
|
|
41740
41811
|
id: options.id ?? "project-state",
|
|
41741
|
-
updatedAt:
|
|
41812
|
+
updatedAt: snapshot2.exportedAt,
|
|
41742
41813
|
source: options.source ?? "skills",
|
|
41743
|
-
payload:
|
|
41814
|
+
payload: snapshot2
|
|
41744
41815
|
};
|
|
41745
41816
|
}
|
|
41746
41817
|
|
|
@@ -41824,18 +41895,18 @@ class SkillsS3ObjectStore {
|
|
|
41824
41895
|
function createSkillsS3ObjectStore(options) {
|
|
41825
41896
|
return new SkillsS3ObjectStore(options);
|
|
41826
41897
|
}
|
|
41827
|
-
function planSkillsS3SnapshotUpload(
|
|
41898
|
+
function planSkillsS3SnapshotUpload(snapshot2, options = {}) {
|
|
41828
41899
|
const prefix = normalizeS3Prefix(options.prefix);
|
|
41829
|
-
return
|
|
41900
|
+
return snapshot2.files.map((file2) => ({
|
|
41830
41901
|
path: file2.path,
|
|
41831
41902
|
key: [prefix, file2.path.replace(/^\.?\//, "")].filter(Boolean).join("/"),
|
|
41832
41903
|
sizeBytes: file2.sizeBytes,
|
|
41833
41904
|
sha256: file2.sha256
|
|
41834
41905
|
}));
|
|
41835
41906
|
}
|
|
41836
|
-
async function uploadSkillsSnapshotFilesToS3(
|
|
41907
|
+
async function uploadSkillsSnapshotFilesToS3(snapshot2, store) {
|
|
41837
41908
|
const uploaded = [];
|
|
41838
|
-
for (const file2 of
|
|
41909
|
+
for (const file2 of snapshot2.files) {
|
|
41839
41910
|
if (!file2.contentBase64)
|
|
41840
41911
|
continue;
|
|
41841
41912
|
uploaded.push(await store.putObject({
|
|
@@ -41963,7 +42034,7 @@ function toIsoString(value) {
|
|
|
41963
42034
|
return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
|
|
41964
42035
|
}
|
|
41965
42036
|
function sha256Hex2(value) {
|
|
41966
|
-
return
|
|
42037
|
+
return createHash21("sha256").update(value).digest("hex");
|
|
41967
42038
|
}
|
|
41968
42039
|
function normalizeHeaders(headers) {
|
|
41969
42040
|
const result = {};
|
|
@@ -42015,15 +42086,15 @@ function toArrayBuffer(bytes) {
|
|
|
42015
42086
|
return buffer;
|
|
42016
42087
|
}
|
|
42017
42088
|
// src/lib/station-snapshot.ts
|
|
42018
|
-
import { createHash as
|
|
42089
|
+
import { createHash as createHash22 } from "crypto";
|
|
42019
42090
|
import {
|
|
42020
42091
|
copyFileSync,
|
|
42021
42092
|
mkdirSync as mkdirSync16,
|
|
42022
42093
|
readFileSync as readFileSync23,
|
|
42023
42094
|
statSync as statSync13,
|
|
42024
|
-
writeFileSync as
|
|
42095
|
+
writeFileSync as writeFileSync14
|
|
42025
42096
|
} from "fs";
|
|
42026
|
-
import { dirname as
|
|
42097
|
+
import { dirname as dirname22, isAbsolute as isAbsolute17, relative as relative9, resolve as resolve16, sep as sep9 } from "path";
|
|
42027
42098
|
|
|
42028
42099
|
// src/lib/portable-snapshot-filter.ts
|
|
42029
42100
|
import { readdirSync as readdirSync14, statSync as statSync12 } from "fs";
|
|
@@ -42190,7 +42261,7 @@ function validateStationId(stationId) {
|
|
|
42190
42261
|
}
|
|
42191
42262
|
}
|
|
42192
42263
|
function sha256File(filePath) {
|
|
42193
|
-
return
|
|
42264
|
+
return createHash22("sha256").update(readFileSync23(filePath)).digest("hex");
|
|
42194
42265
|
}
|
|
42195
42266
|
function scanHome(definition, homesRoot) {
|
|
42196
42267
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -42311,11 +42382,11 @@ function writeStationSnapshot(options) {
|
|
|
42311
42382
|
let written = 0;
|
|
42312
42383
|
for (const plan of untouched) {
|
|
42313
42384
|
const destination = resolve16(repoRoot, plan.destination);
|
|
42314
|
-
mkdirSync16(
|
|
42385
|
+
mkdirSync16(dirname22(destination), { recursive: true });
|
|
42315
42386
|
copyFileSync(plan.source.fullPath, destination);
|
|
42316
42387
|
written += 1;
|
|
42317
42388
|
}
|
|
42318
|
-
const
|
|
42389
|
+
const unchanged3 = plans.length - untouched.length;
|
|
42319
42390
|
const manifest = {
|
|
42320
42391
|
schema: STATION_SYNC_MANIFEST_SCHEMA,
|
|
42321
42392
|
stationId: options.stationId,
|
|
@@ -42323,34 +42394,34 @@ function writeStationSnapshot(options) {
|
|
|
42323
42394
|
producer: STATION_SNAPSHOT_PRODUCER,
|
|
42324
42395
|
stats: {
|
|
42325
42396
|
written,
|
|
42326
|
-
unchanged:
|
|
42397
|
+
unchanged: unchanged3,
|
|
42327
42398
|
files: plans.length,
|
|
42328
42399
|
bytes: totalBytes
|
|
42329
42400
|
},
|
|
42330
42401
|
files: manifestFiles
|
|
42331
42402
|
};
|
|
42332
42403
|
const manifestPath = resolve16(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
|
|
42333
|
-
mkdirSync16(
|
|
42334
|
-
|
|
42404
|
+
mkdirSync16(dirname22(manifestPath), { recursive: true });
|
|
42405
|
+
writeFileSync14(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
42335
42406
|
`);
|
|
42336
42407
|
return {
|
|
42337
42408
|
...base,
|
|
42338
42409
|
mode: "populate",
|
|
42339
|
-
stats: { files: plans.length, bytes: totalBytes, written, unchanged:
|
|
42410
|
+
stats: { files: plans.length, bytes: totalBytes, written, unchanged: unchanged3 },
|
|
42340
42411
|
manifestPath
|
|
42341
42412
|
};
|
|
42342
42413
|
}
|
|
42343
42414
|
// src/lib/station-hydrate.ts
|
|
42344
|
-
import { createHash as
|
|
42415
|
+
import { createHash as createHash23 } from "crypto";
|
|
42345
42416
|
import {
|
|
42346
42417
|
copyFileSync as copyFileSync2,
|
|
42347
42418
|
mkdirSync as mkdirSync17,
|
|
42348
42419
|
readdirSync as readdirSync15,
|
|
42349
42420
|
readFileSync as readFileSync25,
|
|
42350
42421
|
statSync as statSync14,
|
|
42351
|
-
writeFileSync as
|
|
42422
|
+
writeFileSync as writeFileSync15
|
|
42352
42423
|
} from "fs";
|
|
42353
|
-
import { dirname as
|
|
42424
|
+
import { dirname as dirname23, join as join35, resolve as resolve17, sep as sep10 } from "path";
|
|
42354
42425
|
var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
|
|
42355
42426
|
var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
|
|
42356
42427
|
var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
|
|
@@ -42547,7 +42618,7 @@ function skillSha256(skill) {
|
|
|
42547
42618
|
return sha256File(skill.files[0].winner.fullPath);
|
|
42548
42619
|
}
|
|
42549
42620
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
42550
|
-
return
|
|
42621
|
+
return createHash23("sha256").update(joined.sort().join(`
|
|
42551
42622
|
`)).digest("hex");
|
|
42552
42623
|
}
|
|
42553
42624
|
function writeStationHydration(options) {
|
|
@@ -42606,11 +42677,11 @@ function writeStationHydration(options) {
|
|
|
42606
42677
|
}
|
|
42607
42678
|
let written = 0;
|
|
42608
42679
|
for (const entry of toWrite) {
|
|
42609
|
-
mkdirSync17(
|
|
42680
|
+
mkdirSync17(dirname23(entry.destination), { recursive: true });
|
|
42610
42681
|
copyFileSync2(entry.fullPath, entry.destination);
|
|
42611
42682
|
written += 1;
|
|
42612
42683
|
}
|
|
42613
|
-
const
|
|
42684
|
+
const unchanged3 = plan.totalFiles - written;
|
|
42614
42685
|
const hydration = {
|
|
42615
42686
|
schema: STATION_HYDRATION_MANIFEST_SCHEMA,
|
|
42616
42687
|
stationId: options.stationId,
|
|
@@ -42621,26 +42692,26 @@ function writeStationHydration(options) {
|
|
|
42621
42692
|
stats: {
|
|
42622
42693
|
idents: plan.winners.length,
|
|
42623
42694
|
written,
|
|
42624
|
-
unchanged:
|
|
42695
|
+
unchanged: unchanged3,
|
|
42625
42696
|
files: plan.totalFiles,
|
|
42626
42697
|
bytes: plan.totalBytes
|
|
42627
42698
|
},
|
|
42628
42699
|
skills: resultSkills
|
|
42629
42700
|
};
|
|
42630
|
-
const hydrationManifestPath = join35(
|
|
42631
|
-
mkdirSync17(
|
|
42632
|
-
|
|
42701
|
+
const hydrationManifestPath = join35(dirname23(cacheRoot), `hydration-${options.stationId}.json`);
|
|
42702
|
+
mkdirSync17(dirname23(hydrationManifestPath), { recursive: true });
|
|
42703
|
+
writeFileSync15(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
42633
42704
|
`);
|
|
42634
42705
|
return {
|
|
42635
42706
|
...base,
|
|
42636
42707
|
mode: "apply",
|
|
42637
|
-
stats: { ...base.stats, written, unchanged:
|
|
42708
|
+
stats: { ...base.stats, written, unchanged: unchanged3 },
|
|
42638
42709
|
manifestPath: hydrationManifestPath
|
|
42639
42710
|
};
|
|
42640
42711
|
}
|
|
42641
42712
|
// src/lib/private-publication-recovery.ts
|
|
42642
|
-
import { constants as
|
|
42643
|
-
import { dirname as
|
|
42713
|
+
import { constants as constants12, closeSync as closeSync13, fsyncSync as fsyncSync5, fstatSync as fstatSync13, lstatSync as lstatSync21, mkdirSync as mkdirSync18, openSync as openSync13, readSync as readSync11, realpathSync as realpathSync6, renameSync as renameSync7, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "fs";
|
|
42714
|
+
import { dirname as dirname24, isAbsolute as isAbsolute18, join as join36, resolve as resolve18 } from "path";
|
|
42644
42715
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
42645
42716
|
var fail2 = () => {
|
|
42646
42717
|
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.");
|
|
@@ -42648,14 +42719,14 @@ var fail2 = () => {
|
|
|
42648
42719
|
function safeDirectory(directory2) {
|
|
42649
42720
|
if (!isAbsolute18(directory2) || directory2 !== resolve18(directory2) || realpathSync6(directory2) !== directory2)
|
|
42650
42721
|
return fail2();
|
|
42651
|
-
for (let path = directory2;; path =
|
|
42652
|
-
const stat2 =
|
|
42722
|
+
for (let path = directory2;; path = dirname24(path)) {
|
|
42723
|
+
const stat2 = lstatSync21(path);
|
|
42653
42724
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
42654
42725
|
return fail2();
|
|
42655
|
-
if (path ===
|
|
42726
|
+
if (path === dirname24(path))
|
|
42656
42727
|
break;
|
|
42657
42728
|
}
|
|
42658
|
-
const own2 =
|
|
42729
|
+
const own2 = lstatSync21(directory2);
|
|
42659
42730
|
if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
|
|
42660
42731
|
return fail2();
|
|
42661
42732
|
return { dev: own2.dev, ino: own2.ino };
|
|
@@ -42667,35 +42738,35 @@ function unchangedDirectory(directory2, identity3) {
|
|
|
42667
42738
|
}
|
|
42668
42739
|
function readOwned(directory2, name, max) {
|
|
42669
42740
|
const identity3 = safeDirectory(directory2), file2 = join36(directory2, name);
|
|
42670
|
-
const fd =
|
|
42741
|
+
const fd = openSync13(file2, constants12.O_RDONLY | constants12.O_NOFOLLOW);
|
|
42671
42742
|
try {
|
|
42672
|
-
const stat2 =
|
|
42743
|
+
const stat2 = fstatSync13(fd);
|
|
42673
42744
|
if (!stat2.isFile() || stat2.nlink !== 1 || stat2.size > max || stat2.size < 1 || (stat2.mode & 63) !== 0 || process.getuid && stat2.uid !== process.getuid())
|
|
42674
42745
|
return fail2();
|
|
42675
42746
|
const buffer = Buffer.alloc(Math.min(max, stat2.size) + 1);
|
|
42676
42747
|
let length = 0;
|
|
42677
42748
|
while (length < buffer.length) {
|
|
42678
|
-
const count =
|
|
42749
|
+
const count = readSync11(fd, buffer, length, buffer.length - length, length);
|
|
42679
42750
|
if (count === 0)
|
|
42680
42751
|
break;
|
|
42681
42752
|
length += count;
|
|
42682
42753
|
}
|
|
42683
|
-
const bytes = buffer.subarray(0, length), after =
|
|
42754
|
+
const bytes = buffer.subarray(0, length), after = fstatSync13(fd);
|
|
42684
42755
|
unchangedDirectory(directory2, identity3);
|
|
42685
42756
|
if (bytes.length !== stat2.size || stat2.size !== after.size || stat2.mtimeMs !== after.mtimeMs || stat2.ctimeMs !== after.ctimeMs)
|
|
42686
42757
|
return fail2();
|
|
42687
42758
|
return bytes;
|
|
42688
42759
|
} finally {
|
|
42689
|
-
|
|
42760
|
+
closeSync13(fd);
|
|
42690
42761
|
}
|
|
42691
42762
|
}
|
|
42692
42763
|
function writeOwned(directory2, name, bytes) {
|
|
42693
|
-
const fd =
|
|
42764
|
+
const fd = openSync13(join36(directory2, name), constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW, 384);
|
|
42694
42765
|
try {
|
|
42695
|
-
|
|
42696
|
-
|
|
42766
|
+
writeFileSync16(fd, bytes);
|
|
42767
|
+
fsyncSync5(fd);
|
|
42697
42768
|
} finally {
|
|
42698
|
-
|
|
42769
|
+
closeSync13(fd);
|
|
42699
42770
|
}
|
|
42700
42771
|
}
|
|
42701
42772
|
function save(directory2, value) {
|
|
@@ -42704,11 +42775,11 @@ function save(directory2, value) {
|
|
|
42704
42775
|
`);
|
|
42705
42776
|
unchangedDirectory(directory2, identity3);
|
|
42706
42777
|
renameSync7(join36(directory2, temporary), join36(directory2, "receipt.json"));
|
|
42707
|
-
const fd =
|
|
42778
|
+
const fd = openSync13(directory2, constants12.O_RDONLY | constants12.O_NOFOLLOW);
|
|
42708
42779
|
try {
|
|
42709
|
-
|
|
42780
|
+
fsyncSync5(fd);
|
|
42710
42781
|
} finally {
|
|
42711
|
-
|
|
42782
|
+
closeSync13(fd);
|
|
42712
42783
|
}
|
|
42713
42784
|
}
|
|
42714
42785
|
function bind(client2, receipt) {
|
|
@@ -42719,20 +42790,20 @@ async function locked(directory2, action) {
|
|
|
42719
42790
|
const identity3 = safeDirectory(directory2), file2 = join36(directory2, "operation.lock");
|
|
42720
42791
|
let fd;
|
|
42721
42792
|
try {
|
|
42722
|
-
fd =
|
|
42793
|
+
fd = openSync13(file2, constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW, 384);
|
|
42723
42794
|
} catch {
|
|
42724
42795
|
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.");
|
|
42725
42796
|
}
|
|
42726
|
-
const lock =
|
|
42797
|
+
const lock = fstatSync13(fd);
|
|
42727
42798
|
try {
|
|
42728
|
-
|
|
42799
|
+
writeFileSync16(fd, JSON.stringify({ pid: process.pid }) + `
|
|
42729
42800
|
`);
|
|
42730
|
-
|
|
42801
|
+
fsyncSync5(fd);
|
|
42731
42802
|
return await action();
|
|
42732
42803
|
} finally {
|
|
42733
|
-
|
|
42804
|
+
closeSync13(fd);
|
|
42734
42805
|
unchangedDirectory(directory2, identity3);
|
|
42735
|
-
const now =
|
|
42806
|
+
const now = lstatSync21(file2);
|
|
42736
42807
|
if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
|
|
42737
42808
|
fail2();
|
|
42738
42809
|
unlinkSync5(file2);
|
|
@@ -42784,15 +42855,15 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
|
|
|
42784
42855
|
phase: "prepared",
|
|
42785
42856
|
intent: null
|
|
42786
42857
|
};
|
|
42787
|
-
if (!isAbsolute18(recoveryDirectory) || resolve18(recoveryDirectory) !== recoveryDirectory || realpathSync6(
|
|
42858
|
+
if (!isAbsolute18(recoveryDirectory) || resolve18(recoveryDirectory) !== recoveryDirectory || realpathSync6(dirname24(recoveryDirectory)) !== dirname24(recoveryDirectory))
|
|
42788
42859
|
return fail2();
|
|
42789
42860
|
mkdirSync18(recoveryDirectory, { mode: 448 });
|
|
42790
42861
|
safeDirectory(recoveryDirectory);
|
|
42791
|
-
const parent =
|
|
42862
|
+
const parent = openSync13(dirname24(recoveryDirectory), constants12.O_RDONLY | constants12.O_NOFOLLOW);
|
|
42792
42863
|
try {
|
|
42793
|
-
|
|
42864
|
+
fsyncSync5(parent);
|
|
42794
42865
|
} finally {
|
|
42795
|
-
|
|
42866
|
+
closeSync13(parent);
|
|
42796
42867
|
}
|
|
42797
42868
|
writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
|
|
42798
42869
|
save(recoveryDirectory, receipt);
|
|
@@ -42875,7 +42946,7 @@ async function inspectLocked(client2, directory2, cancel) {
|
|
|
42875
42946
|
return privatePublicationResult(directory2, receipt);
|
|
42876
42947
|
}
|
|
42877
42948
|
// src/lib/skill-context.ts
|
|
42878
|
-
import { createHash as
|
|
42949
|
+
import { createHash as createHash24 } from "crypto";
|
|
42879
42950
|
import { resolve as resolve19 } from "path";
|
|
42880
42951
|
function words(value) {
|
|
42881
42952
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
@@ -42964,7 +43035,7 @@ ${content}`;
|
|
|
42964
43035
|
`);
|
|
42965
43036
|
const receipt = {
|
|
42966
43037
|
schemaVersion: 1,
|
|
42967
|
-
id:
|
|
43038
|
+
id: createHash24("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
42968
43039
|
profileId: profile.profileId,
|
|
42969
43040
|
profileRevision: profile.profileRevision,
|
|
42970
43041
|
authority: profile.authority,
|
|
@@ -42980,9 +43051,9 @@ ${content}`;
|
|
|
42980
43051
|
return { context: context2, selections, omitted, receipt };
|
|
42981
43052
|
}
|
|
42982
43053
|
// src/lib/session-reconciliation.ts
|
|
42983
|
-
import { createHash as
|
|
43054
|
+
import { createHash as createHash25 } from "crypto";
|
|
42984
43055
|
var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
|
|
42985
|
-
var digest3 = (value) =>
|
|
43056
|
+
var digest3 = (value) => createHash25("sha256").update(JSON.stringify(value)).digest("hex");
|
|
42986
43057
|
function exactTimestamp(value) {
|
|
42987
43058
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
42988
43059
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -43000,12 +43071,12 @@ function assertPlanWindow(issuedAt, expiresAt, now) {
|
|
|
43000
43071
|
}
|
|
43001
43072
|
}
|
|
43002
43073
|
function inspectSkillSession(sessionId, options = {}) {
|
|
43003
|
-
const
|
|
43074
|
+
const snapshot2 = readSkillSessionSnapshot(sessionId, options), { receipt } = snapshot2;
|
|
43004
43075
|
return {
|
|
43005
43076
|
sessionId,
|
|
43006
|
-
path:
|
|
43007
|
-
receiptSha256:
|
|
43008
|
-
generation:
|
|
43077
|
+
path: snapshot2.path,
|
|
43078
|
+
receiptSha256: snapshot2.sha256,
|
|
43079
|
+
generation: snapshot2.generation,
|
|
43009
43080
|
verifiedAt: receipt.verifiedAt,
|
|
43010
43081
|
authority: receipt.profile.authority,
|
|
43011
43082
|
workspaceId: receipt.profile.workspaceId,
|
|
@@ -43030,8 +43101,8 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
43030
43101
|
const expiresAt = input2.apply ? input2.planExpiresAt : new Date(now + SESSION_RECONCILIATION_PLAN_TTL_MS).toISOString();
|
|
43031
43102
|
if (input2.apply)
|
|
43032
43103
|
assertPlanWindow(issuedAt, expiresAt, now);
|
|
43033
|
-
const
|
|
43034
|
-
if (
|
|
43104
|
+
const snapshot2 = readSkillSessionSnapshot(input2.sessionId, options), old = snapshot2.receipt;
|
|
43105
|
+
if (snapshot2.sha256 !== input2.receiptSha256 || old.profile.profileId !== input2.fromProfile || old.profile.profileRevision !== input2.fromRevision) {
|
|
43035
43106
|
throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The current session receipt does not match the reviewed old bytes, profile and revision.");
|
|
43036
43107
|
}
|
|
43037
43108
|
const client2 = options.client ?? await createProfileClient();
|
|
@@ -43045,14 +43116,14 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
43045
43116
|
}
|
|
43046
43117
|
const targetKeys = new Set(target.selections.map(selectionKey));
|
|
43047
43118
|
const loaded = old.loaded.filter((key) => targetKeys.has(key));
|
|
43048
|
-
const replacementGeneration = nextSkillSessionGeneration(
|
|
43119
|
+
const replacementGeneration = nextSkillSessionGeneration(snapshot2.generation);
|
|
43049
43120
|
const plan = {
|
|
43050
43121
|
schemaVersion: 1,
|
|
43051
43122
|
issuedAt,
|
|
43052
43123
|
expiresAt,
|
|
43053
43124
|
sessionId: input2.sessionId,
|
|
43054
|
-
sessionPath:
|
|
43055
|
-
before: { receiptSha256:
|
|
43125
|
+
sessionPath: snapshot2.path,
|
|
43126
|
+
before: { receiptSha256: snapshot2.sha256, generation: snapshot2.generation, profileId: old.profile.profileId, profileRevision: old.profile.profileRevision, selectionCount: old.profile.selections.length },
|
|
43056
43127
|
target: { authority: target.authority, workspaceId: target.workspaceId, profileId: target.profileId, profileRevision: target.profileRevision, profileSha256: digest3(target), selectionCount: target.selections.length },
|
|
43057
43128
|
replacementGeneration,
|
|
43058
43129
|
retainedLoadedCount: loaded.length,
|
|
@@ -43063,7 +43134,7 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
43063
43134
|
if (input2.planDigest !== undefined && input2.planDigest !== planDigest)
|
|
43064
43135
|
throw new SkillSelectionError("SESSION_PLAN_CHANGED", "The session reconciliation plan differs from the reviewed plan.");
|
|
43065
43136
|
if (!input2.apply)
|
|
43066
|
-
return { applied: false, plan, planDigest, archivePath: undefined, receiptPath: undefined, beforeSha256:
|
|
43137
|
+
return { applied: false, plan, planDigest, archivePath: undefined, receiptPath: undefined, beforeSha256: snapshot2.sha256, afterSha256: undefined };
|
|
43067
43138
|
assertPlanWindow(issuedAt, expiresAt, (options.now ?? Date.now)());
|
|
43068
43139
|
const replacement = {
|
|
43069
43140
|
...old,
|
|
@@ -43072,7 +43143,7 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
43072
43143
|
profile: target,
|
|
43073
43144
|
loaded
|
|
43074
43145
|
};
|
|
43075
|
-
const result = replaceSkillSession(skillSessionSnapshotBinding(
|
|
43146
|
+
const result = replaceSkillSession(skillSessionSnapshotBinding(snapshot2), replacement, { ...plan, planDigest }, options, () => assertPlanWindow(issuedAt, expiresAt, (options.now ?? Date.now)()));
|
|
43076
43147
|
return { applied: true, plan, planDigest, ...result };
|
|
43077
43148
|
}
|
|
43078
43149
|
export {
|