@hasna/skills 0.10.45 → 0.10.46
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 +17 -0
- package/bin/index.js +757 -659
- package/bin/mcp.js +553 -497
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +755 -659
- package/dist/lib/agent-discovery.d.ts +1 -1
- package/dist/lib/agent-integration.d.ts +2 -2
- package/dist/lib/agent-sumi.d.ts +3 -0
- package/dist/lib/claude-settings-witness.d.ts +5 -1
- package/dist/lib/sumi-settings-witness.d.ts +14 -0
- package/dist/sdk/index.js +353 -259
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6940,7 +6940,7 @@ var require_public_api = __commonJS((exports) => {
|
|
|
6940
6940
|
}
|
|
6941
6941
|
return doc;
|
|
6942
6942
|
}
|
|
6943
|
-
function
|
|
6943
|
+
function parse2(src, reviver, options) {
|
|
6944
6944
|
let _reviver = undefined;
|
|
6945
6945
|
if (typeof reviver === "function") {
|
|
6946
6946
|
_reviver = reviver;
|
|
@@ -6981,7 +6981,7 @@ var require_public_api = __commonJS((exports) => {
|
|
|
6981
6981
|
return value.toString(options);
|
|
6982
6982
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
6983
6983
|
}
|
|
6984
|
-
exports.parse =
|
|
6984
|
+
exports.parse = parse2;
|
|
6985
6985
|
exports.parseAllDocuments = parseAllDocuments;
|
|
6986
6986
|
exports.parseDocument = parseDocument;
|
|
6987
6987
|
exports.stringify = stringify;
|
|
@@ -8991,8 +8991,371 @@ export default async ({ directory }) => {
|
|
|
8991
8991
|
`;
|
|
8992
8992
|
}
|
|
8993
8993
|
|
|
8994
|
+
// src/lib/sumi-settings-witness.ts
|
|
8995
|
+
import { createHash as createHash3 } from "crypto";
|
|
8996
|
+
|
|
8997
|
+
// src/lib/claude-settings-witness.ts
|
|
8998
|
+
import { createHash as createHash2 } from "crypto";
|
|
8999
|
+
import { closeSync, constants, fstatSync, lstatSync as lstatSync3, openSync, readSync } from "fs";
|
|
9000
|
+
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
|
|
9001
|
+
var CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
9002
|
+
function need(value, reason) {
|
|
9003
|
+
if (!value)
|
|
9004
|
+
throw new Error(`Claude settings witness ${reason}`);
|
|
9005
|
+
}
|
|
9006
|
+
function absolutePath(value) {
|
|
9007
|
+
need(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute2(value) && resolve2(value) === value, "requires a normalized absolute path");
|
|
9008
|
+
}
|
|
9009
|
+
var sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
|
|
9010
|
+
function parents(path) {
|
|
9011
|
+
const result = [];
|
|
9012
|
+
for (let at = dirname3(path);; at = dirname3(at)) {
|
|
9013
|
+
const stat = lstatSync3(at, { bigint: true });
|
|
9014
|
+
need(stat.isDirectory() && !stat.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
9015
|
+
result.push([at, stat]);
|
|
9016
|
+
if (at === dirname3(at))
|
|
9017
|
+
return result;
|
|
9018
|
+
}
|
|
9019
|
+
}
|
|
9020
|
+
function readNativeSettingsWitnessFile(path, budget, filename = "settings.json") {
|
|
9021
|
+
absolutePath(path);
|
|
9022
|
+
need(basename2(path) === filename, `requires ${filename}`);
|
|
9023
|
+
need(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
|
|
9024
|
+
const ancestors = parents(path), initial = lstatSync3(path, { bigint: true });
|
|
9025
|
+
need(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_SETTINGS_WITNESS_LIMITS.bytes), "is not a bounded regular file");
|
|
9026
|
+
need(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
|
|
9027
|
+
const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
9028
|
+
try {
|
|
9029
|
+
const opened = fstatSync(fd, { bigint: true });
|
|
9030
|
+
need(opened.isFile() && sameFile(initial, opened), "changed during open");
|
|
9031
|
+
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
9032
|
+
let length = 0;
|
|
9033
|
+
while (length < bytes.length) {
|
|
9034
|
+
const count = readSync(fd, bytes, length, bytes.length - length, null);
|
|
9035
|
+
if (!count)
|
|
9036
|
+
break;
|
|
9037
|
+
length += count;
|
|
9038
|
+
}
|
|
9039
|
+
const after = fstatSync(fd, { bigint: true }), current = lstatSync3(path, { bigint: true });
|
|
9040
|
+
need(current.isFile() && sameFile(opened, after) && sameFile(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
9041
|
+
for (const [ancestor, before] of ancestors) {
|
|
9042
|
+
const now = lstatSync3(ancestor, { bigint: true });
|
|
9043
|
+
need(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
9044
|
+
}
|
|
9045
|
+
budget.remaining -= length;
|
|
9046
|
+
try {
|
|
9047
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
|
|
9048
|
+
} catch {
|
|
9049
|
+
throw new Error("Claude settings witness is not strict UTF-8");
|
|
9050
|
+
}
|
|
9051
|
+
} finally {
|
|
9052
|
+
closeSync(fd);
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
9055
|
+
function parse(text) {
|
|
9056
|
+
let at = 0, nodes = 0;
|
|
9057
|
+
const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
|
|
9058
|
+
const space = () => {
|
|
9059
|
+
while (at < text.length && /[\x20\t\r\n]/.test(text[at]))
|
|
9060
|
+
at++;
|
|
9061
|
+
};
|
|
9062
|
+
function string() {
|
|
9063
|
+
need(text[at] === '"', "contains invalid JSON");
|
|
9064
|
+
const start = at++;
|
|
9065
|
+
while (at < text.length) {
|
|
9066
|
+
const character = text[at++];
|
|
9067
|
+
if (character === '"') {
|
|
9068
|
+
let value2;
|
|
9069
|
+
try {
|
|
9070
|
+
value2 = JSON.parse(text.slice(start, at));
|
|
9071
|
+
} catch {
|
|
9072
|
+
throw new Error("Claude settings witness contains invalid JSON string");
|
|
9073
|
+
}
|
|
9074
|
+
need(value2.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.stringCharacters, "exceeds its string limit");
|
|
9075
|
+
return value2;
|
|
9076
|
+
}
|
|
9077
|
+
if (character === "\\")
|
|
9078
|
+
at++;
|
|
9079
|
+
}
|
|
9080
|
+
throw new Error("Claude settings witness contains an unterminated JSON string");
|
|
9081
|
+
}
|
|
9082
|
+
function value(depth) {
|
|
9083
|
+
need(depth <= CLAUDE_SETTINGS_WITNESS_LIMITS.depth && ++nodes <= CLAUDE_SETTINGS_WITNESS_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
9084
|
+
space();
|
|
9085
|
+
const character = text[at];
|
|
9086
|
+
if (character === '"')
|
|
9087
|
+
return { kind: "string", value: string() };
|
|
9088
|
+
if (character === "{" || character === "[") {
|
|
9089
|
+
at++;
|
|
9090
|
+
space();
|
|
9091
|
+
const object = character === "{", end = object ? "}" : "]", entries = [], items = [], keys = new Set;
|
|
9092
|
+
if (text[at] !== end)
|
|
9093
|
+
while (true) {
|
|
9094
|
+
if (object) {
|
|
9095
|
+
space();
|
|
9096
|
+
const key = string();
|
|
9097
|
+
need(!keys.has(key), "contains a duplicate JSON key");
|
|
9098
|
+
keys.add(key);
|
|
9099
|
+
space();
|
|
9100
|
+
need(text[at++] === ":", "contains invalid JSON");
|
|
9101
|
+
entries.push([key, value(depth + 1)]);
|
|
9102
|
+
} else
|
|
9103
|
+
items.push(value(depth + 1));
|
|
9104
|
+
space();
|
|
9105
|
+
if (text[at] === end)
|
|
9106
|
+
break;
|
|
9107
|
+
need(text[at++] === ",", "contains invalid JSON");
|
|
9108
|
+
}
|
|
9109
|
+
at++;
|
|
9110
|
+
return object ? { kind: "object", entries } : { kind: "array", items };
|
|
9111
|
+
}
|
|
9112
|
+
for (const literal of ["true", "false", "null"])
|
|
9113
|
+
if (text.startsWith(literal, at)) {
|
|
9114
|
+
at += literal.length;
|
|
9115
|
+
return { kind: "literal", value: literal };
|
|
9116
|
+
}
|
|
9117
|
+
numberPattern.lastIndex = at;
|
|
9118
|
+
const number = numberPattern.exec(text);
|
|
9119
|
+
need(number, "contains invalid JSON");
|
|
9120
|
+
at += number[0].length;
|
|
9121
|
+
return { kind: "number", value: number[0] };
|
|
9122
|
+
}
|
|
9123
|
+
const root = value(0);
|
|
9124
|
+
space();
|
|
9125
|
+
need(at === text.length && root.kind === "object", "requires a JSON object without trailing content");
|
|
9126
|
+
need(root.entries.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.rows, "exceeds its settings field limit");
|
|
9127
|
+
return root;
|
|
9128
|
+
}
|
|
9129
|
+
var BOOLEAN_PREFERENCES = new Set([
|
|
9130
|
+
"autoScrollEnabled",
|
|
9131
|
+
"axScreenReader",
|
|
9132
|
+
"emojiCompletionEnabled",
|
|
9133
|
+
"prefersReducedMotion",
|
|
9134
|
+
"showTurnDuration",
|
|
9135
|
+
"spinnerTipsEnabled",
|
|
9136
|
+
"syntaxHighlightingDisabled",
|
|
9137
|
+
"terminalProgressBarEnabled",
|
|
9138
|
+
"terminalTitleFromRename",
|
|
9139
|
+
"verbose",
|
|
9140
|
+
"wheelScrollAccelerationEnabled"
|
|
9141
|
+
]);
|
|
9142
|
+
var ENUM_PREFERENCES = Object.freeze({
|
|
9143
|
+
editorMode: ["normal", "vim"],
|
|
9144
|
+
tui: ["default", "fullscreen"],
|
|
9145
|
+
viewMode: ["default", "verbose", "focus"]
|
|
9146
|
+
});
|
|
9147
|
+
var BUILTIN_MODEL_SELECTIONS = new Set([
|
|
9148
|
+
"default",
|
|
9149
|
+
"best",
|
|
9150
|
+
"fable",
|
|
9151
|
+
"fable[1m]",
|
|
9152
|
+
"sonnet",
|
|
9153
|
+
"sonnet[1m]",
|
|
9154
|
+
"opus",
|
|
9155
|
+
"opus[1m]",
|
|
9156
|
+
"haiku",
|
|
9157
|
+
"opusplan",
|
|
9158
|
+
"claude-fable-5-1",
|
|
9159
|
+
"claude-fable-5",
|
|
9160
|
+
"claude-fable-5[1m]",
|
|
9161
|
+
"claude-opus-5",
|
|
9162
|
+
"claude-sonnet-5",
|
|
9163
|
+
"claude-haiku-4-5-20251001",
|
|
9164
|
+
"claude-opus-4-6",
|
|
9165
|
+
"claude-sonnet-4-5",
|
|
9166
|
+
"claude-sonnet-4-5-20250929",
|
|
9167
|
+
"claude-opus-4-8",
|
|
9168
|
+
"claude-opus-4-8[1m]",
|
|
9169
|
+
"claude-opus-4-7",
|
|
9170
|
+
"claude-sonnet-4-6",
|
|
9171
|
+
"claude-opus-4-5-20251101",
|
|
9172
|
+
"claude-opus-4-5",
|
|
9173
|
+
"claude-haiku-4-5",
|
|
9174
|
+
"claude-fable-5-1[1m]",
|
|
9175
|
+
"claude-opus-5[1m]",
|
|
9176
|
+
"claude-opus-4-7[1m]",
|
|
9177
|
+
"claude-opus-4-6[1m]",
|
|
9178
|
+
"claude-sonnet-4-6[1m]"
|
|
9179
|
+
]);
|
|
9180
|
+
function canonical(value) {
|
|
9181
|
+
if (value.kind === "object")
|
|
9182
|
+
return { kind: "object", entries: value.entries.map(([key, child]) => [key, canonical(child)]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0) };
|
|
9183
|
+
if (value.kind === "array")
|
|
9184
|
+
return { kind: "array", items: value.items.map(canonical) };
|
|
9185
|
+
return value;
|
|
9186
|
+
}
|
|
9187
|
+
var CLAUDE_COORDINATED_HOOK_EVENTS = ["PreToolUse", "PostToolUse", "Stop", "Notification", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart"];
|
|
9188
|
+
function assertClaudeHookEventsReplacement(before, replacement, events) {
|
|
9189
|
+
need(Array.isArray(events) && events.length > 0 && events.length <= CLAUDE_COORDINATED_HOOK_EVENTS.length && new Set(events).size === events.length && events.every((event) => CLAUDE_COORDINATED_HOOK_EVENTS.includes(event)), "requires explicit supported hook events");
|
|
9190
|
+
const label = events.join(",");
|
|
9191
|
+
function bounded(text) {
|
|
9192
|
+
need(typeof text === "string" && Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, `${label} update exceeds its byte limit`);
|
|
9193
|
+
return parse(text);
|
|
9194
|
+
}
|
|
9195
|
+
const original = bounded(before ?? "{}"), next = bounded(replacement);
|
|
9196
|
+
const hadHooks = original.entries.some(([key]) => key === "hooks");
|
|
9197
|
+
function withoutOwnedEvents(value) {
|
|
9198
|
+
const hooks = value.entries.find(([key]) => key === "hooks")?.[1];
|
|
9199
|
+
if (hooks !== undefined) {
|
|
9200
|
+
need(hooks.kind === "object", `${label} update requires an object of hooks`);
|
|
9201
|
+
for (const event of events) {
|
|
9202
|
+
const selected = hooks.entries.find(([key]) => key === event)?.[1];
|
|
9203
|
+
need(selected === undefined || selected.kind === "array", `${event} update requires a ${event} array`);
|
|
9204
|
+
}
|
|
9205
|
+
hooks.entries = hooks.entries.filter(([key]) => !events.includes(key));
|
|
9206
|
+
if (!hadHooks && hooks.entries.length === 0)
|
|
9207
|
+
value.entries = value.entries.filter(([key]) => key !== "hooks");
|
|
9208
|
+
}
|
|
9209
|
+
return canonical(value);
|
|
9210
|
+
}
|
|
9211
|
+
need(JSON.stringify(withoutOwnedEvents(original)) === JSON.stringify(withoutOwnedEvents(next)), `${label} update changed settings outside hooks.${label}`);
|
|
9212
|
+
}
|
|
9213
|
+
var PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
|
|
9214
|
+
function inferencePreference(key, child, version) {
|
|
9215
|
+
if (key === "skipDangerousModePermissionPrompt") {
|
|
9216
|
+
need(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid dialog acknowledgment");
|
|
9217
|
+
return false;
|
|
9218
|
+
}
|
|
9219
|
+
if (key === "effortLevel") {
|
|
9220
|
+
need(child.kind === "string" && PERSISTED_EFFORT_LEVELS.has(child.value), "contains an invalid persisted effort level");
|
|
9221
|
+
return true;
|
|
9222
|
+
}
|
|
9223
|
+
if (key !== "modelSettings")
|
|
9224
|
+
return false;
|
|
9225
|
+
need(child.kind === "object", "requires an object of model preferences");
|
|
9226
|
+
child.entries = child.entries.filter(([model, preferences]) => {
|
|
9227
|
+
if (version === 2 && !BUILTIN_MODEL_SELECTIONS.has(model) || preferences.kind !== "object" || preferences.entries.length !== 1 || preferences.entries[0][0] !== "effortLevel")
|
|
9228
|
+
return true;
|
|
9229
|
+
const effort = preferences.entries[0][1];
|
|
9230
|
+
need(effort.kind === "string" && PERSISTED_EFFORT_LEVELS.has(effort.value), "contains an invalid persisted model effort level");
|
|
9231
|
+
return false;
|
|
9232
|
+
});
|
|
9233
|
+
return child.entries.length === 0;
|
|
9234
|
+
}
|
|
9235
|
+
function settingsDigest(text, version = 1) {
|
|
9236
|
+
const value = parse(text);
|
|
9237
|
+
value.entries = value.entries.filter(([key, child]) => {
|
|
9238
|
+
if (version !== 1 && inferencePreference(key, child, version))
|
|
9239
|
+
return false;
|
|
9240
|
+
if (key === "model") {
|
|
9241
|
+
need(child.kind === "string", "contains an invalid model selection");
|
|
9242
|
+
return !BUILTIN_MODEL_SELECTIONS.has(child.value);
|
|
9243
|
+
}
|
|
9244
|
+
if (BOOLEAN_PREFERENCES.has(key)) {
|
|
9245
|
+
need(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid display preference");
|
|
9246
|
+
return false;
|
|
9247
|
+
}
|
|
9248
|
+
if (Object.hasOwn(ENUM_PREFERENCES, key)) {
|
|
9249
|
+
need(child.kind === "string" && ENUM_PREFERENCES[key].includes(child.value), "contains an invalid display preference");
|
|
9250
|
+
return false;
|
|
9251
|
+
}
|
|
9252
|
+
return true;
|
|
9253
|
+
});
|
|
9254
|
+
return createHash2("sha256").update(`hasna.skills.claude-settings.v${version}\x00`).update(JSON.stringify(canonical(value))).digest("hex");
|
|
9255
|
+
}
|
|
9256
|
+
function hashClaudeSettingsReplacement(text, budget) {
|
|
9257
|
+
need(typeof text === "string", "requires settings text");
|
|
9258
|
+
const bytes = Buffer.byteLength(text);
|
|
9259
|
+
need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
9260
|
+
budget.remaining -= bytes;
|
|
9261
|
+
return settingsDigest(text);
|
|
9262
|
+
}
|
|
9263
|
+
function captureClaudeSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
9264
|
+
return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget)) };
|
|
9265
|
+
}
|
|
9266
|
+
function captureClaudeSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
9267
|
+
return { path, hashMode: "claude-settings-v2", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 2) };
|
|
9268
|
+
}
|
|
9269
|
+
function hashClaudeSettingsReplacementV2(text, budget) {
|
|
9270
|
+
need(typeof text === "string", "requires settings text");
|
|
9271
|
+
const bytes = Buffer.byteLength(text);
|
|
9272
|
+
need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
9273
|
+
budget.remaining -= bytes;
|
|
9274
|
+
return settingsDigest(text, 2);
|
|
9275
|
+
}
|
|
9276
|
+
function captureClaudeSettingsV3(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
9277
|
+
return { path, hashMode: "claude-settings-v3", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 3) };
|
|
9278
|
+
}
|
|
9279
|
+
function hashClaudeSettingsReplacementV3(text, budget) {
|
|
9280
|
+
need(typeof text === "string", "requires settings text");
|
|
9281
|
+
const bytes = Buffer.byteLength(text);
|
|
9282
|
+
need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
9283
|
+
budget.remaining -= bytes;
|
|
9284
|
+
return settingsDigest(text, 3);
|
|
9285
|
+
}
|
|
9286
|
+
function upgradeClaudeSettingsWitness(previous, reviewedSettingsPath) {
|
|
9287
|
+
need(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "claude-settings-v1" || previous.hashMode === "claude-settings-v2") && typeof previous.sha256 === "string" && /^[a-f0-9]{64}$/.test(previous.sha256), "requires an exact legacy settings witness");
|
|
9288
|
+
const budget = { remaining: 2 * CLAUDE_SETTINGS_WITNESS_LIMITS.bytes };
|
|
9289
|
+
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget), current = readNativeSettingsWitnessFile(previous.path, budget);
|
|
9290
|
+
const beforeDigest = previous.hashMode === undefined || previous.hashMode === "bytes" ? createHash2("sha256").update(before).digest("hex") : settingsDigest(before, previous.hashMode === "claude-settings-v1" ? 1 : 2);
|
|
9291
|
+
need(beforeDigest === previous.sha256, "reviewed preimage does not match the legacy witness");
|
|
9292
|
+
const sha256 = settingsDigest(current, 3);
|
|
9293
|
+
need(settingsDigest(before, 3) === sha256, "non-preference settings changed; explicit discovery review required");
|
|
9294
|
+
return { path: previous.path, hashMode: "claude-settings-v3", sha256 };
|
|
9295
|
+
}
|
|
9296
|
+
function hashNativeJsonControls(text, excludedMetadata) {
|
|
9297
|
+
need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
|
|
9298
|
+
const value = parse(text);
|
|
9299
|
+
value.entries = value.entries.filter(([key, child]) => {
|
|
9300
|
+
if (key !== excludedMetadata)
|
|
9301
|
+
return true;
|
|
9302
|
+
need(child.kind === "string" && child.value.length <= 128, "has invalid version metadata");
|
|
9303
|
+
return false;
|
|
9304
|
+
});
|
|
9305
|
+
return createHash2("sha256").update("hasna.skills.native-json-controls.v1\x00").update(JSON.stringify(canonical(value))).digest("hex");
|
|
9306
|
+
}
|
|
9307
|
+
function hashSumiNativeJsonControls(text) {
|
|
9308
|
+
need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
|
|
9309
|
+
const value = parse(text);
|
|
9310
|
+
value.entries = value.entries.filter(([key, child]) => {
|
|
9311
|
+
if (key === "$schema" || key === "username") {
|
|
9312
|
+
need(child.kind === "string", "has invalid Sumi display metadata");
|
|
9313
|
+
return false;
|
|
9314
|
+
}
|
|
9315
|
+
if (key !== "experimental")
|
|
9316
|
+
return true;
|
|
9317
|
+
need(child.kind === "object", "requires Sumi experimental controls to be an object");
|
|
9318
|
+
child.entries = child.entries.filter(([name, option]) => {
|
|
9319
|
+
if (name !== "statusline" && name !== "compact_tools")
|
|
9320
|
+
return true;
|
|
9321
|
+
need(option.kind === "literal" && (option.value === "true" || option.value === "false"), "has invalid Sumi display preference");
|
|
9322
|
+
return false;
|
|
9323
|
+
});
|
|
9324
|
+
return child.entries.length > 0;
|
|
9325
|
+
});
|
|
9326
|
+
return createHash2("sha256").update("hasna.skills.sumi-settings.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
9327
|
+
}
|
|
9328
|
+
|
|
9329
|
+
// src/lib/sumi-settings-witness.ts
|
|
9330
|
+
var SUMI_DISCOVERY_PROJECTION_FIELDS = ["skills", "plugin", "plugins", "permissions"];
|
|
9331
|
+
var budget = () => ({ remaining: 256 * 1024 * 1024 });
|
|
9332
|
+
function readSumiSettingsPreimage(path) {
|
|
9333
|
+
return readNativeSettingsWitnessFile(path, budget(), "sumi.json");
|
|
9334
|
+
}
|
|
9335
|
+
function hashSumiSettingsReplacement(text, limits) {
|
|
9336
|
+
const bytes = Buffer.byteLength(text);
|
|
9337
|
+
if (bytes > CLAUDE_SETTINGS_WITNESS_LIMITS.bytes || !Number.isSafeInteger(limits.remaining) || limits.remaining < bytes)
|
|
9338
|
+
throw new Error("Sumi settings witness exceeds its byte limit");
|
|
9339
|
+
limits.remaining -= bytes;
|
|
9340
|
+
return hashSumiNativeJsonControls(text);
|
|
9341
|
+
}
|
|
9342
|
+
function captureSumiSettings(path, limits = budget()) {
|
|
9343
|
+
return { path, hashMode: "sumi-settings-v1", sha256: hashSumiNativeJsonControls(readNativeSettingsWitnessFile(path, limits, "sumi.json")) };
|
|
9344
|
+
}
|
|
9345
|
+
function upgradeSumiSettingsWitness(previous, reviewedSettingsPath) {
|
|
9346
|
+
if (!previous || Object.keys(previous).some((key) => !["path", "hashMode", "sha256"].includes(key)) || ![undefined, "bytes"].includes(previous.hashMode) || !/^[a-f0-9]{64}$/.test(previous.sha256))
|
|
9347
|
+
throw new Error("Sumi settings upgrade requires one exact preserved byte witness");
|
|
9348
|
+
const before = readSumiSettingsPreimage(reviewedSettingsPath);
|
|
9349
|
+
if (createHash3("sha256").update(before).digest("hex") !== previous.sha256)
|
|
9350
|
+
throw new Error("Reviewed Sumi preimage does not match its byte witness");
|
|
9351
|
+
const next = captureSumiSettings(previous.path);
|
|
9352
|
+
if (hashSumiNativeJsonControls(before) !== next.sha256)
|
|
9353
|
+
throw new Error("Sumi non-display settings changed; explicit discovery review required");
|
|
9354
|
+
return next;
|
|
9355
|
+
}
|
|
9356
|
+
|
|
8994
9357
|
// src/lib/agent-policy-limits.ts
|
|
8995
|
-
import { basename as
|
|
9358
|
+
import { basename as basename3, isAbsolute as isAbsolute3, resolve as resolve3 } from "path";
|
|
8996
9359
|
var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
|
|
8997
9360
|
function object(value) {
|
|
8998
9361
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -9046,7 +9409,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9046
9409
|
text(control.pluginId, 1024);
|
|
9047
9410
|
text(control.namespace, 64);
|
|
9048
9411
|
text(control.pluginParent);
|
|
9049
|
-
requireBound(
|
|
9412
|
+
requireBound(isAbsolute3(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
|
|
9050
9413
|
if (control.remotePluginId !== undefined)
|
|
9051
9414
|
requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
|
|
9052
9415
|
if (control.skillsOnly !== undefined)
|
|
@@ -9064,7 +9427,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9064
9427
|
text(control.pluginId, 1024);
|
|
9065
9428
|
text(control.namespace, 64);
|
|
9066
9429
|
text(control.pluginParent);
|
|
9067
|
-
requireBound(!ids.has(control.pluginId) &&
|
|
9430
|
+
requireBound(!ids.has(control.pluginId) && isAbsolute3(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
|
|
9068
9431
|
ids.add(control.pluginId);
|
|
9069
9432
|
}
|
|
9070
9433
|
}
|
|
@@ -9089,15 +9452,15 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9089
9452
|
requireBound(object(value));
|
|
9090
9453
|
text(value.agent, 128);
|
|
9091
9454
|
if (value.codexRetiredMaterializations !== undefined) {
|
|
9092
|
-
const proof = record(value.codexRetiredMaterializations, 3), roots = array(proof.roots, AGENT_POLICY_LIMITS.discoveryRoots),
|
|
9093
|
-
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).every((key) => ["roots", "parents", "directories"].includes(key)) && roots.length +
|
|
9455
|
+
const proof = record(value.codexRetiredMaterializations, 3), roots = array(proof.roots, AGENT_POLICY_LIMITS.discoveryRoots), parents2 = proof.parents === undefined ? [] : array(proof.parents, AGENT_POLICY_LIMITS.discoveryRoots);
|
|
9456
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).every((key) => ["roots", "parents", "directories"].includes(key)) && roots.length + parents2.length > 0 && new Set(roots).size === roots.length && new Set(parents2).size === parents2.length);
|
|
9094
9457
|
for (const root of roots) {
|
|
9095
9458
|
text(root);
|
|
9096
|
-
requireBound(
|
|
9459
|
+
requireBound(isAbsolute3(root) && resolve3(root) === root);
|
|
9097
9460
|
}
|
|
9098
|
-
for (const parent of
|
|
9461
|
+
for (const parent of parents2) {
|
|
9099
9462
|
text(parent);
|
|
9100
|
-
requireBound(
|
|
9463
|
+
requireBound(isAbsolute3(parent) && resolve3(parent) === parent);
|
|
9101
9464
|
}
|
|
9102
9465
|
for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
|
|
9103
9466
|
requireBound(object(directory));
|
|
@@ -9117,7 +9480,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9117
9480
|
text(input.namespace, 64);
|
|
9118
9481
|
text(input.pluginParent);
|
|
9119
9482
|
text(input.sourceRoot);
|
|
9120
|
-
requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) =>
|
|
9483
|
+
requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute3(path) && resolve3(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
|
|
9121
9484
|
ids.add(input.pluginId);
|
|
9122
9485
|
}
|
|
9123
9486
|
if (proof.directories !== undefined)
|
|
@@ -9134,13 +9497,15 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9134
9497
|
text(source.path);
|
|
9135
9498
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
9136
9499
|
if (source.hashMode !== undefined)
|
|
9137
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9500
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
9501
|
+
if (source.hashMode === "sumi-settings-v1")
|
|
9502
|
+
requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "sumi.json");
|
|
9138
9503
|
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
|
|
9139
|
-
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) &&
|
|
9504
|
+
requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "config.toml");
|
|
9140
9505
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
|
|
9141
|
-
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) &&
|
|
9506
|
+
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "settings.json");
|
|
9142
9507
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
|
|
9143
|
-
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) &&
|
|
9508
|
+
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "known_marketplaces.json");
|
|
9144
9509
|
if (source.hashMode === "claude-plugin-registry") {
|
|
9145
9510
|
requireBound(agent === "claude");
|
|
9146
9511
|
const managed = array(source.managedPlugins, 64);
|
|
@@ -9171,7 +9536,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9171
9536
|
}
|
|
9172
9537
|
|
|
9173
9538
|
// src/lib/agent-discovery.ts
|
|
9174
|
-
import { createHash as
|
|
9539
|
+
import { createHash as createHash22 } from "crypto";
|
|
9175
9540
|
import { lstatSync as lstatSync24, readFileSync as readFileSync19, readdirSync as readdirSync10, realpathSync as realpathSync10 } from "fs";
|
|
9176
9541
|
import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute18, join as join31, resolve as resolve18, sep as sep7 } from "path";
|
|
9177
9542
|
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
@@ -9223,8 +9588,8 @@ var $visit = visit.visit;
|
|
|
9223
9588
|
var $visitAsync = visit.visitAsync;
|
|
9224
9589
|
|
|
9225
9590
|
// src/lib/agent-hermes.ts
|
|
9226
|
-
import { dirname as
|
|
9227
|
-
import { lstatSync as
|
|
9591
|
+
import { dirname as dirname4, isAbsolute as isAbsolute4, join as join7, resolve as resolve4 } from "path";
|
|
9592
|
+
import { lstatSync as lstatSync4, readFileSync as readFileSync6, opendirSync } from "fs";
|
|
9228
9593
|
var HERMES_OPT_OUT = ".no-bundled-skills";
|
|
9229
9594
|
var EVENTS = ["pre_llm_call", "pre_tool_call"];
|
|
9230
9595
|
function object2(value) {
|
|
@@ -9261,7 +9626,7 @@ function quoted(value) {
|
|
|
9261
9626
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
9262
9627
|
}
|
|
9263
9628
|
function hermesHookDefinitions(supervisor) {
|
|
9264
|
-
if (!
|
|
9629
|
+
if (!isAbsolute4(supervisor.runtime) || !isAbsolute4(supervisor.path))
|
|
9265
9630
|
throw new Error("Expected absolute Hermes supervisor paths");
|
|
9266
9631
|
const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
|
|
9267
9632
|
return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
|
|
@@ -9297,19 +9662,19 @@ function assertHermesEnvironment(home) {
|
|
|
9297
9662
|
throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
|
|
9298
9663
|
}
|
|
9299
9664
|
const selected = process.env.HERMES_HOME?.trim();
|
|
9300
|
-
if (selected && (!
|
|
9665
|
+
if (selected && (!isAbsolute4(selected) || resolve4(selected) !== join7(resolve4(home), ".hermes")))
|
|
9301
9666
|
throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
|
|
9302
9667
|
if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
|
|
9303
9668
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
|
|
9304
9669
|
}
|
|
9305
9670
|
function read(path) {
|
|
9306
|
-
for (let current = path;; current =
|
|
9307
|
-
if (
|
|
9671
|
+
for (let current = path;; current = dirname4(current)) {
|
|
9672
|
+
if (lstatSync4(current, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
9308
9673
|
throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes protection input");
|
|
9309
|
-
if (
|
|
9674
|
+
if (dirname4(current) === current)
|
|
9310
9675
|
break;
|
|
9311
9676
|
}
|
|
9312
|
-
const stat =
|
|
9677
|
+
const stat = lstatSync4(path, { throwIfNoEntry: false });
|
|
9313
9678
|
if (!stat)
|
|
9314
9679
|
return null;
|
|
9315
9680
|
if (!stat.isFile() || stat.size > 1024 * 1024)
|
|
@@ -9320,7 +9685,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
|
|
|
9320
9685
|
assertHermesEnvironment(home);
|
|
9321
9686
|
if (read(join7(home, ".hermes", HERMES_OPT_OUT)) === null)
|
|
9322
9687
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
|
|
9323
|
-
if (!supervisor || !
|
|
9688
|
+
if (!supervisor || !isAbsolute4(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
|
|
9324
9689
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
|
|
9325
9690
|
const raw = read(join7(home, ".hermes/shell-hooks-allowlist.json"));
|
|
9326
9691
|
let trust;
|
|
@@ -9346,16 +9711,16 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
|
|
|
9346
9711
|
}
|
|
9347
9712
|
function assertNoHermesLegacyShadow(root) {
|
|
9348
9713
|
let entries = 0;
|
|
9349
|
-
for (let cursor =
|
|
9350
|
-
if (
|
|
9714
|
+
for (let cursor = resolve4(root);; cursor = dirname4(cursor)) {
|
|
9715
|
+
if (lstatSync4(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
9351
9716
|
throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes legacy discovery root");
|
|
9352
|
-
if (
|
|
9717
|
+
if (dirname4(cursor) === cursor)
|
|
9353
9718
|
break;
|
|
9354
9719
|
}
|
|
9355
9720
|
function scan(path, depth) {
|
|
9356
9721
|
if (++entries > 1e4 || depth > 64)
|
|
9357
9722
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes legacy discovery limit exceeded; review the root before continuing");
|
|
9358
|
-
const stat =
|
|
9723
|
+
const stat = lstatSync4(path, { throwIfNoEntry: false });
|
|
9359
9724
|
if (!stat)
|
|
9360
9725
|
return;
|
|
9361
9726
|
if (stat.isSymbolicLink())
|
|
@@ -9365,9 +9730,9 @@ function assertNoHermesLegacyShadow(root) {
|
|
|
9365
9730
|
throw new Error("NATIVE_SKILL_DRIFT: unsupported Hermes legacy discovery input");
|
|
9366
9731
|
return;
|
|
9367
9732
|
}
|
|
9368
|
-
if (
|
|
9733
|
+
if (lstatSync4(join7(path, "skills-cli.md"), { throwIfNoEntry: false }))
|
|
9369
9734
|
throw new Error("NATIVE_SKILL_DRIFT: legacy Hermes skills-cli.md can shadow the owned bridge; preserve and retire it before continuing");
|
|
9370
|
-
const hasDescriptor = Boolean(
|
|
9735
|
+
const hasDescriptor = Boolean(lstatSync4(join7(path, "SKILL.md"), { throwIfNoEntry: false }));
|
|
9371
9736
|
const directory = opendirSync(path);
|
|
9372
9737
|
try {
|
|
9373
9738
|
for (let child = directory.readSync();child; child = directory.readSync()) {
|
|
@@ -9430,193 +9795,193 @@ if (failed) process.exitCode = 2;
|
|
|
9430
9795
|
}
|
|
9431
9796
|
|
|
9432
9797
|
// src/lib/agent-discovery-directories.ts
|
|
9433
|
-
import { createHash as
|
|
9434
|
-
import { lstatSync as
|
|
9435
|
-
import { dirname as
|
|
9436
|
-
function
|
|
9798
|
+
import { createHash as createHash4 } from "crypto";
|
|
9799
|
+
import { lstatSync as lstatSync5, opendirSync as opendirSync2 } from "fs";
|
|
9800
|
+
import { dirname as dirname5, isAbsolute as isAbsolute5, join as join8, resolve as resolve5 } from "path";
|
|
9801
|
+
function need2(value, message) {
|
|
9437
9802
|
if (!value)
|
|
9438
9803
|
throw new Error(message);
|
|
9439
9804
|
}
|
|
9440
9805
|
function safe(path) {
|
|
9441
|
-
|
|
9442
|
-
for (let cursor = path;; cursor =
|
|
9443
|
-
|
|
9444
|
-
if (
|
|
9806
|
+
need2(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute5(path) && resolve5(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
|
|
9807
|
+
for (let cursor = path;; cursor = dirname5(cursor)) {
|
|
9808
|
+
need2(!lstatSync5(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
|
|
9809
|
+
if (dirname5(cursor) === cursor)
|
|
9445
9810
|
break;
|
|
9446
9811
|
}
|
|
9447
9812
|
}
|
|
9448
9813
|
function same(before, after) {
|
|
9449
9814
|
return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
|
|
9450
9815
|
}
|
|
9451
|
-
function capture(path,
|
|
9816
|
+
function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
|
|
9452
9817
|
safe(path);
|
|
9453
9818
|
if (omittedRoots.some((root2) => path === root2 || path.startsWith(root2 + "/")))
|
|
9454
9819
|
return { path, sha256: null };
|
|
9455
|
-
const root =
|
|
9820
|
+
const root = lstatSync5(path, { throwIfNoEntry: false });
|
|
9456
9821
|
if (!root)
|
|
9457
9822
|
return { path, sha256: null };
|
|
9458
|
-
|
|
9459
|
-
const hash =
|
|
9823
|
+
need2(root.isDirectory(), "Unsupported native discovery directory input");
|
|
9824
|
+
const hash = createHash4("sha256");
|
|
9460
9825
|
function walk(current, relative2, before, depth) {
|
|
9461
|
-
|
|
9826
|
+
need2(depth <= 64, "Native discovery directory depth limit exceeded");
|
|
9462
9827
|
const names = [], directory = opendirSync2(current);
|
|
9463
9828
|
try {
|
|
9464
9829
|
for (let entry = directory.readSync();entry; entry = directory.readSync()) {
|
|
9465
|
-
|
|
9830
|
+
need2(++budget2.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
|
|
9466
9831
|
names.push(entry.name);
|
|
9467
9832
|
}
|
|
9468
9833
|
} finally {
|
|
9469
9834
|
directory.closeSync();
|
|
9470
9835
|
}
|
|
9471
|
-
|
|
9836
|
+
need2(new Set(names).size === names.length, "Ambiguous native discovery directory names");
|
|
9472
9837
|
for (const name of names.sort()) {
|
|
9473
9838
|
const child = join8(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
|
|
9474
9839
|
if (omittedRoots.includes(child))
|
|
9475
9840
|
continue;
|
|
9476
|
-
|
|
9477
|
-
const stat =
|
|
9478
|
-
|
|
9841
|
+
need2(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
|
|
9842
|
+
const stat = lstatSync5(child);
|
|
9843
|
+
need2(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
|
|
9479
9844
|
const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
|
|
9480
9845
|
`;
|
|
9481
|
-
|
|
9482
|
-
|
|
9846
|
+
budget2.bytes += Buffer.byteLength(row);
|
|
9847
|
+
need2(budget2.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
|
|
9483
9848
|
hash.update(row);
|
|
9484
9849
|
rows?.push(row);
|
|
9485
9850
|
if (stat.isDirectory() && !excludedRoots.includes(child))
|
|
9486
9851
|
walk(child, childRelative, stat, depth + 1);
|
|
9487
9852
|
}
|
|
9488
|
-
|
|
9853
|
+
need2(same(before, lstatSync5(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
|
|
9489
9854
|
}
|
|
9490
9855
|
walk(path, "", root, 0);
|
|
9491
9856
|
safe(path);
|
|
9492
9857
|
return { path, sha256: hash.digest("hex") };
|
|
9493
9858
|
}
|
|
9494
9859
|
function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
|
|
9495
|
-
|
|
9496
|
-
|
|
9860
|
+
need2(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid projected discovery directory collection");
|
|
9861
|
+
need2(omittedRoots.length <= AGENT_POLICY_LIMITS.discoveryRoots && new Set(omittedRoots).size === omittedRoots.length, "Invalid inactive discovery roots");
|
|
9497
9862
|
for (const root of omittedRoots)
|
|
9498
9863
|
safe(root);
|
|
9499
|
-
const
|
|
9864
|
+
const budget2 = { entries: 0, bytes: 0 };
|
|
9500
9865
|
return paths.map((path) => {
|
|
9501
9866
|
const entries = [];
|
|
9502
|
-
return { ...capture(path,
|
|
9867
|
+
return { ...capture(path, budget2, [], omittedRoots, entries), entries };
|
|
9503
9868
|
});
|
|
9504
9869
|
}
|
|
9505
9870
|
function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
|
|
9506
|
-
|
|
9871
|
+
need2(directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid projected discovery directory collection");
|
|
9507
9872
|
let count = 0, bytes = 0;
|
|
9508
9873
|
const expected = directories.map((directory) => {
|
|
9509
9874
|
safe(directory.path);
|
|
9510
|
-
|
|
9511
|
-
const hash =
|
|
9875
|
+
need2(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
|
|
9876
|
+
const hash = createHash4("sha256");
|
|
9512
9877
|
const kept = [];
|
|
9513
9878
|
for (const row of directory.entries) {
|
|
9514
|
-
|
|
9879
|
+
need2(typeof row === "string" && ++count <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Invalid reviewed directory membership rows");
|
|
9515
9880
|
bytes += Buffer.byteLength(row);
|
|
9516
|
-
|
|
9881
|
+
need2(bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Reviewed directory membership byte limit exceeded");
|
|
9517
9882
|
const value = JSON.parse(row);
|
|
9518
|
-
|
|
9883
|
+
need2(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute5(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
|
|
9519
9884
|
`, "Invalid reviewed directory member");
|
|
9520
9885
|
hash.update(row);
|
|
9521
9886
|
const path = join8(directory.path, value[0]);
|
|
9522
9887
|
if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
|
|
9523
9888
|
kept.push(row);
|
|
9524
9889
|
}
|
|
9525
|
-
|
|
9890
|
+
need2(directory.sha256 === null ? directory.entries.length === 0 : hash.digest("hex") === directory.sha256, "Reviewed directory inventory does not match its fingerprint");
|
|
9526
9891
|
const absent = omittedRoots.some((root) => directory.path === root || directory.path.startsWith(root + "/"));
|
|
9527
|
-
return { path: directory.path, sha256: absent || directory.sha256 === null ? null :
|
|
9892
|
+
return { path: directory.path, sha256: absent || directory.sha256 === null ? null : createHash4("sha256").update(kept.join("")).digest("hex") };
|
|
9528
9893
|
});
|
|
9529
9894
|
const current = captureDiscoveryDirectoryProjection(directories.map((item) => item.path), omittedRoots);
|
|
9530
|
-
|
|
9895
|
+
need2(current.every((item, index) => item.sha256 === expected[index].sha256), "Native discovery directory membership changed outside retired Codex roots");
|
|
9531
9896
|
}
|
|
9532
9897
|
function captureDiscoveryDirectories(paths, excludedRoots = []) {
|
|
9533
|
-
|
|
9534
|
-
const
|
|
9898
|
+
need2(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
|
|
9899
|
+
const budget2 = { entries: 0, bytes: 0 };
|
|
9535
9900
|
for (const root of excludedRoots)
|
|
9536
9901
|
safe(root);
|
|
9537
|
-
return paths.map((path) => capture(path,
|
|
9902
|
+
return paths.map((path) => capture(path, budget2, excludedRoots));
|
|
9538
9903
|
}
|
|
9539
9904
|
function verifyDiscoveryDirectories(directories, excludedRoots = []) {
|
|
9540
|
-
|
|
9905
|
+
need2(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
|
|
9541
9906
|
for (const directory of directories)
|
|
9542
|
-
|
|
9907
|
+
need2(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
|
|
9543
9908
|
const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
|
|
9544
9909
|
for (let i = 0;i < directories.length; i++)
|
|
9545
|
-
|
|
9910
|
+
need2(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
|
|
9546
9911
|
}
|
|
9547
9912
|
|
|
9548
9913
|
// src/lib/agent-discovery-bytes.ts
|
|
9549
|
-
import { createHash as
|
|
9550
|
-
import { closeSync, constants, fstatSync, lstatSync as
|
|
9551
|
-
import { dirname as
|
|
9914
|
+
import { createHash as createHash5 } from "crypto";
|
|
9915
|
+
import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync6, openSync as openSync2, readSync as readSync2 } from "fs";
|
|
9916
|
+
import { dirname as dirname6, isAbsolute as isAbsolute6, resolve as resolve6 } from "path";
|
|
9552
9917
|
function discoveryByteBudget() {
|
|
9553
9918
|
return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
|
|
9554
9919
|
}
|
|
9555
9920
|
function safe2(path) {
|
|
9556
|
-
if (typeof path !== "string" || !
|
|
9921
|
+
if (typeof path !== "string" || !isAbsolute6(path) || path !== resolve6(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
|
|
9557
9922
|
throw new Error("Expected a canonical absolute raw discovery path");
|
|
9558
|
-
for (let cursor = path;; cursor =
|
|
9559
|
-
if (
|
|
9923
|
+
for (let cursor = path;; cursor = dirname6(cursor)) {
|
|
9924
|
+
if (lstatSync6(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
9560
9925
|
throw new Error("Refusing symlink raw discovery input");
|
|
9561
|
-
if (
|
|
9926
|
+
if (dirname6(cursor) === cursor)
|
|
9562
9927
|
break;
|
|
9563
9928
|
}
|
|
9564
9929
|
}
|
|
9565
9930
|
function same2(left, right) {
|
|
9566
9931
|
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
|
|
9567
9932
|
}
|
|
9568
|
-
function consume(
|
|
9569
|
-
if (bytes >
|
|
9933
|
+
function consume(budget2, bytes) {
|
|
9934
|
+
if (bytes > budget2.remaining)
|
|
9570
9935
|
throw new Error("Raw discovery aggregate byte limit exceeded");
|
|
9571
|
-
|
|
9936
|
+
budget2.remaining -= bytes;
|
|
9572
9937
|
}
|
|
9573
|
-
function hashRawDiscoveryFile(path,
|
|
9938
|
+
function hashRawDiscoveryFile(path, budget2, changes) {
|
|
9574
9939
|
safe2(path);
|
|
9575
9940
|
if (changes?.has(path)) {
|
|
9576
9941
|
const text2 = changes.get(path), bytes = Buffer.byteLength(text2);
|
|
9577
9942
|
if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
9578
9943
|
throw new Error("Raw discovery file byte limit exceeded");
|
|
9579
|
-
consume(
|
|
9580
|
-
return
|
|
9944
|
+
consume(budget2, bytes);
|
|
9945
|
+
return createHash5("sha256").update(text2).digest("hex");
|
|
9581
9946
|
}
|
|
9582
|
-
const initial =
|
|
9947
|
+
const initial = lstatSync6(path, { throwIfNoEntry: false });
|
|
9583
9948
|
if (!initial)
|
|
9584
9949
|
return null;
|
|
9585
9950
|
if (!initial.isFile() || initial.size > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
9586
9951
|
throw new Error("Unsupported or oversized raw discovery input");
|
|
9587
|
-
if (initial.size >
|
|
9952
|
+
if (initial.size > budget2.remaining)
|
|
9588
9953
|
throw new Error("Raw discovery aggregate byte limit exceeded");
|
|
9589
|
-
const fd =
|
|
9954
|
+
const fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
|
|
9590
9955
|
try {
|
|
9591
|
-
const opened =
|
|
9956
|
+
const opened = fstatSync2(fd);
|
|
9592
9957
|
if (!opened.isFile() || !same2(initial, opened))
|
|
9593
9958
|
throw new Error("Raw discovery input changed during open");
|
|
9594
|
-
const hash =
|
|
9959
|
+
const hash = createHash5("sha256"), buffer = Buffer.alloc(64 * 1024);
|
|
9595
9960
|
let bytes = 0;
|
|
9596
9961
|
while (true) {
|
|
9597
|
-
const count =
|
|
9962
|
+
const count = readSync2(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
|
|
9598
9963
|
if (!count)
|
|
9599
9964
|
break;
|
|
9600
9965
|
bytes += count;
|
|
9601
9966
|
if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
|
|
9602
9967
|
throw new Error("Raw discovery file byte limit exceeded");
|
|
9603
|
-
consume(
|
|
9968
|
+
consume(budget2, count);
|
|
9604
9969
|
hash.update(buffer.subarray(0, count));
|
|
9605
9970
|
}
|
|
9606
|
-
const final =
|
|
9971
|
+
const final = fstatSync2(fd), current = lstatSync6(path, { throwIfNoEntry: false });
|
|
9607
9972
|
safe2(path);
|
|
9608
9973
|
if (!current?.isFile() || !same2(opened, final) || !same2(final, current) || bytes !== final.size)
|
|
9609
9974
|
throw new Error("Raw discovery input changed during read");
|
|
9610
9975
|
return hash.digest("hex");
|
|
9611
9976
|
} finally {
|
|
9612
|
-
|
|
9977
|
+
closeSync2(fd);
|
|
9613
9978
|
}
|
|
9614
9979
|
}
|
|
9615
9980
|
|
|
9616
9981
|
// src/lib/agent-discovery-path-bytes.ts
|
|
9617
|
-
import { createHash as
|
|
9618
|
-
import { lstatSync as
|
|
9619
|
-
import { dirname as
|
|
9982
|
+
import { createHash as createHash6 } from "crypto";
|
|
9983
|
+
import { lstatSync as lstatSync7, readlinkSync } from "fs";
|
|
9984
|
+
import { dirname as dirname7, isAbsolute as isAbsolute7, join as join9, parse as parse2, resolve as resolve7, sep as sep2 } from "path";
|
|
9620
9985
|
function identity2(stat, mutable = false) {
|
|
9621
9986
|
return {
|
|
9622
9987
|
dev: String(stat.dev),
|
|
@@ -9627,14 +9992,14 @@ function identity2(stat, mutable = false) {
|
|
|
9627
9992
|
...mutable ? { size: String(stat.size), mtimeNs: String(stat.mtimeNs), ctimeNs: String(stat.ctimeNs) } : {}
|
|
9628
9993
|
};
|
|
9629
9994
|
}
|
|
9630
|
-
function
|
|
9631
|
-
if (typeof path !== "string" || !
|
|
9995
|
+
function canonical2(path) {
|
|
9996
|
+
if (typeof path !== "string" || !isAbsolute7(path) || path !== resolve7(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00") || Buffer.from(path).toString("utf8") !== path)
|
|
9632
9997
|
throw new Error("Expected a canonical absolute discovery path");
|
|
9633
9998
|
}
|
|
9634
9999
|
function trace(path) {
|
|
9635
|
-
|
|
10000
|
+
canonical2(path);
|
|
9636
10001
|
const rows = [], paths = [];
|
|
9637
|
-
let metadataBytes = 0, steps = 0, links = 0, cursor =
|
|
10002
|
+
let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(sep2);
|
|
9638
10003
|
function record2(path2, row) {
|
|
9639
10004
|
const value = { path: path2, ...row };
|
|
9640
10005
|
metadataBytes += Buffer.byteLength(JSON.stringify(value));
|
|
@@ -9644,7 +10009,7 @@ function trace(path) {
|
|
|
9644
10009
|
paths.push(path2);
|
|
9645
10010
|
}
|
|
9646
10011
|
function root() {
|
|
9647
|
-
const stat =
|
|
10012
|
+
const stat = lstatSync7(cursor, { bigint: true });
|
|
9648
10013
|
if (!stat.isDirectory())
|
|
9649
10014
|
throw new Error("Unsupported discovery path root");
|
|
9650
10015
|
record2(cursor, { type: "directory", ...identity2(stat) });
|
|
@@ -9657,13 +10022,13 @@ function trace(path) {
|
|
|
9657
10022
|
if (!component || component === ".")
|
|
9658
10023
|
continue;
|
|
9659
10024
|
if (component === "..") {
|
|
9660
|
-
cursor =
|
|
10025
|
+
cursor = dirname7(cursor);
|
|
9661
10026
|
continue;
|
|
9662
10027
|
}
|
|
9663
10028
|
const candidate = join9(cursor, component);
|
|
9664
10029
|
if (candidate.length > AGENT_POLICY_LIMITS.pathCharacters)
|
|
9665
10030
|
throw new Error("Discovery resolved path length limit exceeded");
|
|
9666
|
-
const stat =
|
|
10031
|
+
const stat = lstatSync7(candidate, { bigint: true, throwIfNoEntry: false });
|
|
9667
10032
|
if (!stat) {
|
|
9668
10033
|
record2(candidate, { type: "missing", remaining: pending });
|
|
9669
10034
|
return { rows, paths, file: null, missing: candidate, metadataBytes };
|
|
@@ -9671,17 +10036,17 @@ function trace(path) {
|
|
|
9671
10036
|
if (stat.isSymbolicLink()) {
|
|
9672
10037
|
if (++links > AGENT_POLICY_LIMITS.discoveryPathLinks || stat.size > BigInt(AGENT_POLICY_LIMITS.pathCharacters))
|
|
9673
10038
|
throw new Error("Discovery path link limit exceeded");
|
|
9674
|
-
const rawTarget = readlinkSync(candidate, { encoding: "buffer" }), target = rawTarget.toString("utf8"), after =
|
|
10039
|
+
const rawTarget = readlinkSync(candidate, { encoding: "buffer" }), target = rawTarget.toString("utf8"), after = lstatSync7(candidate, { bigint: true });
|
|
9675
10040
|
if (!rawTarget.equals(Buffer.from(target, "utf8")))
|
|
9676
10041
|
throw new Error("Unsupported non-UTF8 discovery link target");
|
|
9677
10042
|
if (!target || target.includes("\x00") || Buffer.byteLength(target) > AGENT_POLICY_LIMITS.pathCharacters || !after.isSymbolicLink() || JSON.stringify(identity2(stat, true)) !== JSON.stringify(identity2(after, true)))
|
|
9678
10043
|
throw new Error("Discovery link changed during read");
|
|
9679
10044
|
record2(candidate, { type: "link", target, ...identity2(stat, true) });
|
|
9680
|
-
if (
|
|
9681
|
-
cursor =
|
|
10045
|
+
if (isAbsolute7(target)) {
|
|
10046
|
+
cursor = parse2(target).root;
|
|
9682
10047
|
root();
|
|
9683
10048
|
}
|
|
9684
|
-
const remainder =
|
|
10049
|
+
const remainder = isAbsolute7(target) ? target.slice(parse2(target).root.length) : target;
|
|
9685
10050
|
pending = [...remainder.split(sep2), ...pending];
|
|
9686
10051
|
if (pending.length + steps > AGENT_POLICY_LIMITS.discoveryPathSteps)
|
|
9687
10052
|
throw new Error("Discovery path step limit exceeded");
|
|
@@ -9701,7 +10066,7 @@ function trace(path) {
|
|
|
9701
10066
|
}
|
|
9702
10067
|
throw new Error("Unsupported directory discovery path input");
|
|
9703
10068
|
}
|
|
9704
|
-
function hashDiscoveryPathFile(path,
|
|
10069
|
+
function hashDiscoveryPathFile(path, budget2, changes) {
|
|
9705
10070
|
const before = trace(path);
|
|
9706
10071
|
let createsMissingPath = false;
|
|
9707
10072
|
if (before.missing !== null && changes) {
|
|
@@ -9714,17 +10079,17 @@ function hashDiscoveryPathFile(path, budget, changes) {
|
|
|
9714
10079
|
}
|
|
9715
10080
|
if (createsMissingPath || changes?.has(path) || before.paths.some((path2) => changes?.has(path2)))
|
|
9716
10081
|
throw new Error("Discovery path witness intersects a planned write; use bytes mode for owned configuration");
|
|
9717
|
-
const remaining =
|
|
10082
|
+
const remaining = budget2.pathMetadataRemaining ?? AGENT_POLICY_LIMITS.discoveryPathTotalMetadataBytes;
|
|
9718
10083
|
if (before.metadataBytes > remaining)
|
|
9719
10084
|
throw new Error("Discovery path aggregate metadata limit exceeded");
|
|
9720
|
-
|
|
9721
|
-
const content = before.file === null ? null : hashRawDiscoveryFile(before.file,
|
|
10085
|
+
budget2.pathMetadataRemaining = remaining - before.metadataBytes;
|
|
10086
|
+
const content = before.file === null ? null : hashRawDiscoveryFile(before.file, budget2);
|
|
9722
10087
|
if (before.file !== null && content === null)
|
|
9723
10088
|
throw new Error("Discovery path input changed during read");
|
|
9724
10089
|
const after = trace(path);
|
|
9725
10090
|
if (JSON.stringify(before.rows) !== JSON.stringify(after.rows) || before.file !== after.file)
|
|
9726
10091
|
throw new Error("Discovery path input changed during read");
|
|
9727
|
-
return
|
|
10092
|
+
return createHash6("sha256").update(JSON.stringify({ version: 1, path, rows: before.rows, content })).digest("hex");
|
|
9728
10093
|
}
|
|
9729
10094
|
|
|
9730
10095
|
// src/lib/plugin-discovery.ts
|
|
@@ -9736,15 +10101,15 @@ import { homedir as homedir5 } from "os";
|
|
|
9736
10101
|
import { isAbsolute as isAbsolute15, join as join29, resolve as resolve15 } from "path";
|
|
9737
10102
|
|
|
9738
10103
|
// src/lib/selection-cache.ts
|
|
9739
|
-
import { createHash as
|
|
10104
|
+
import { createHash as createHash8, randomUUID } from "crypto";
|
|
9740
10105
|
import { spawnSync } from "child_process";
|
|
9741
|
-
import { closeSync as
|
|
10106
|
+
import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, fsyncSync, linkSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync8, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
9742
10107
|
import { hostname } from "os";
|
|
9743
|
-
import { dirname as
|
|
10108
|
+
import { dirname as dirname8, join as join11, parse as parse3, resolve as resolve8 } from "path";
|
|
9744
10109
|
import { isDeepStrictEqual } from "util";
|
|
9745
10110
|
|
|
9746
10111
|
// src/lib/skill-bundle.ts
|
|
9747
|
-
import { createHash as
|
|
10112
|
+
import { createHash as createHash7 } from "crypto";
|
|
9748
10113
|
import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
|
|
9749
10114
|
import { join as join10, relative as relative2 } from "path";
|
|
9750
10115
|
import { createGunzip } from "zlib";
|
|
@@ -9899,7 +10264,7 @@ function ownBytes(view) {
|
|
|
9899
10264
|
return out;
|
|
9900
10265
|
}
|
|
9901
10266
|
function sha256Hex(bytes) {
|
|
9902
|
-
return
|
|
10267
|
+
return createHash7("sha256").update(bytes).digest("hex");
|
|
9903
10268
|
}
|
|
9904
10269
|
function collectSkillBundleEntries(dir) {
|
|
9905
10270
|
const entries = [];
|
|
@@ -10145,7 +10510,7 @@ async function inspectSkillBundle(bundle, options = {}) {
|
|
|
10145
10510
|
parser2.push(chunk);
|
|
10146
10511
|
bytesSinceYield += chunk.byteLength;
|
|
10147
10512
|
if (bytesSinceYield >= 256 * 1024) {
|
|
10148
|
-
await new Promise((
|
|
10513
|
+
await new Promise((resolve8) => setTimeout(resolve8, 0));
|
|
10149
10514
|
bytesSinceYield = 0;
|
|
10150
10515
|
check();
|
|
10151
10516
|
}
|
|
@@ -10383,14 +10748,14 @@ class SkillSelectionError extends Error {
|
|
|
10383
10748
|
var MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
10384
10749
|
var SELECTION_LOCK_FILE = "selection.lock.json";
|
|
10385
10750
|
function selectionCacheRoot(options = {}) {
|
|
10386
|
-
return
|
|
10751
|
+
return resolve8(options.cacheDir ?? join11(getDataDirReadOnly(), "selection-cache"));
|
|
10387
10752
|
}
|
|
10388
10753
|
function selectionKey(selection) {
|
|
10389
10754
|
validateSelection(selection);
|
|
10390
10755
|
return hash([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
|
|
10391
10756
|
}
|
|
10392
10757
|
function hash(value) {
|
|
10393
|
-
return
|
|
10758
|
+
return createHash8("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10394
10759
|
}
|
|
10395
10760
|
function validateSelection(selection) {
|
|
10396
10761
|
if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
|
|
@@ -10430,14 +10795,14 @@ function selectionBundlePath(selection, options = {}) {
|
|
|
10430
10795
|
return join11(selectionCacheRoot(options), "objects", hash(selection.authority), hash(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
|
|
10431
10796
|
}
|
|
10432
10797
|
function assertRegularPath(path, createParents = false) {
|
|
10433
|
-
const absolute =
|
|
10434
|
-
const root =
|
|
10798
|
+
const absolute = resolve8(path);
|
|
10799
|
+
const root = parse3(absolute).root;
|
|
10435
10800
|
let cursor = root;
|
|
10436
10801
|
const segments = absolute.slice(root.length).split("/");
|
|
10437
10802
|
for (let index = 0;index < segments.length; index++) {
|
|
10438
10803
|
cursor = join11(cursor, segments[index]);
|
|
10439
10804
|
try {
|
|
10440
|
-
const stat =
|
|
10805
|
+
const stat = lstatSync8(cursor);
|
|
10441
10806
|
if (stat.isSymbolicLink() || index < segments.length - 1 && !stat.isDirectory()) {
|
|
10442
10807
|
throw new SkillSelectionError("UNSAFE_CACHE_PATH", "Skills cache paths must not traverse symlinks or non-directories.");
|
|
10443
10808
|
}
|
|
@@ -10453,20 +10818,20 @@ function readRegularFile(path, limit) {
|
|
|
10453
10818
|
assertRegularPath(path);
|
|
10454
10819
|
let fd;
|
|
10455
10820
|
try {
|
|
10456
|
-
fd =
|
|
10821
|
+
fd = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
|
|
10457
10822
|
} catch (error) {
|
|
10458
10823
|
if (error.code === "ENOENT")
|
|
10459
10824
|
return null;
|
|
10460
10825
|
throw error;
|
|
10461
10826
|
}
|
|
10462
10827
|
try {
|
|
10463
|
-
const stat =
|
|
10828
|
+
const stat = fstatSync3(fd);
|
|
10464
10829
|
if (!stat.isFile() || stat.size > limit)
|
|
10465
10830
|
throw new SkillSelectionError("INVALID_CACHE_FILE", "The Skills cache contains an invalid or oversized file.");
|
|
10466
10831
|
const bytes = Buffer.alloc(stat.size + 1);
|
|
10467
10832
|
let size = 0;
|
|
10468
10833
|
while (size < bytes.length) {
|
|
10469
|
-
const read2 =
|
|
10834
|
+
const read2 = readSync3(fd, bytes, size, bytes.length - size, null);
|
|
10470
10835
|
if (!read2)
|
|
10471
10836
|
break;
|
|
10472
10837
|
size += read2;
|
|
@@ -10475,7 +10840,7 @@ function readRegularFile(path, limit) {
|
|
|
10475
10840
|
throw new SkillSelectionError("INVALID_CACHE_FILE", "The Skills cache file grew during its bounded read.");
|
|
10476
10841
|
return bytes.subarray(0, size);
|
|
10477
10842
|
} finally {
|
|
10478
|
-
|
|
10843
|
+
closeSync3(fd);
|
|
10479
10844
|
}
|
|
10480
10845
|
}
|
|
10481
10846
|
function writeSelectionJson(path, value) {
|
|
@@ -10487,19 +10852,19 @@ function writeSelectionJson(path, value) {
|
|
|
10487
10852
|
}
|
|
10488
10853
|
function atomicWrite(path, bytes, mode, durable = false) {
|
|
10489
10854
|
assertRegularPath(path, true);
|
|
10490
|
-
const temporary = join11(
|
|
10855
|
+
const temporary = join11(dirname8(path), `.selection-${randomUUID()}.tmp`);
|
|
10491
10856
|
try {
|
|
10492
|
-
const fd =
|
|
10857
|
+
const fd = openSync3(temporary, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, mode);
|
|
10493
10858
|
try {
|
|
10494
10859
|
writeFileSync3(fd, bytes);
|
|
10495
10860
|
if (durable)
|
|
10496
10861
|
fsyncSync(fd);
|
|
10497
10862
|
} finally {
|
|
10498
|
-
|
|
10863
|
+
closeSync3(fd);
|
|
10499
10864
|
}
|
|
10500
10865
|
renameSync(temporary, path);
|
|
10501
10866
|
if (durable)
|
|
10502
|
-
syncDirectory(
|
|
10867
|
+
syncDirectory(dirname8(path));
|
|
10503
10868
|
} finally {
|
|
10504
10869
|
try {
|
|
10505
10870
|
unlinkSync(temporary);
|
|
@@ -10609,7 +10974,7 @@ function assertFreshCachedProfile(receipt, options = {}) {
|
|
|
10609
10974
|
}
|
|
10610
10975
|
}
|
|
10611
10976
|
function projectSelectionLockPath(projectDir) {
|
|
10612
|
-
return join11(
|
|
10977
|
+
return join11(resolve8(projectDir), ".skills", SELECTION_LOCK_FILE);
|
|
10613
10978
|
}
|
|
10614
10979
|
function readProjectSelection(projectDir) {
|
|
10615
10980
|
const receipt = readSelectionJson(projectSelectionLockPath(projectDir));
|
|
@@ -10725,14 +11090,14 @@ function localLockIdentity() {
|
|
|
10725
11090
|
}
|
|
10726
11091
|
function readSessionLock(sessionId, options) {
|
|
10727
11092
|
const path = `${sessionReceiptPath(sessionId, options)}.write-lock`;
|
|
10728
|
-
const before =
|
|
11093
|
+
const before = lstatSync8(path, { throwIfNoEntry: false });
|
|
10729
11094
|
if (!before)
|
|
10730
11095
|
return null;
|
|
10731
11096
|
if (!before.isFile() || ![1, 2].includes(before.nlink) || (before.mode & 63) !== 0 || process.getuid && before.uid !== process.getuid()) {
|
|
10732
11097
|
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is not a private regular file; recovery requires review.");
|
|
10733
11098
|
}
|
|
10734
11099
|
const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
|
|
10735
|
-
const after =
|
|
11100
|
+
const after = lstatSync8(path, { throwIfNoEntry: false });
|
|
10736
11101
|
if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.mode !== after.mode || before.uid !== after.uid || before.gid !== after.gid || before.nlink !== after.nlink) {
|
|
10737
11102
|
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock changed during inspection; recovery requires review.");
|
|
10738
11103
|
}
|
|
@@ -10749,7 +11114,7 @@ function readSessionLock(sessionId, options) {
|
|
|
10749
11114
|
if (after.nlink === 2) {
|
|
10750
11115
|
stagingPath = `${path}.${marker.operationId}.prepared`;
|
|
10751
11116
|
assertRegularPath(stagingPath);
|
|
10752
|
-
const staged =
|
|
11117
|
+
const staged = lstatSync8(stagingPath, { throwIfNoEntry: false });
|
|
10753
11118
|
if (marker.publication !== "hard-link-v1" || !staged?.isFile() || staged.dev !== after.dev || staged.ino !== after.ino || staged.nlink !== 2 || staged.mode !== after.mode || staged.uid !== after.uid || staged.gid !== after.gid) {
|
|
10754
11119
|
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock has an unbound publication link; recovery requires review.");
|
|
10755
11120
|
}
|
|
@@ -10760,33 +11125,33 @@ function publishSessionLock(path, marker) {
|
|
|
10760
11125
|
const stagingPath = `${path}.${marker.operationId}.prepared`;
|
|
10761
11126
|
const bytes = Buffer.from(`${JSON.stringify({ ...marker, publication: "hard-link-v1" })}
|
|
10762
11127
|
`);
|
|
10763
|
-
const fd =
|
|
10764
|
-
const stat =
|
|
11128
|
+
const fd = openSync3(stagingPath, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
11129
|
+
const stat = fstatSync3(fd);
|
|
10765
11130
|
let published = false, handedOff = false;
|
|
10766
11131
|
try {
|
|
10767
11132
|
writeFileSync3(fd, bytes);
|
|
10768
11133
|
fsyncSync(fd);
|
|
10769
11134
|
linkSync(stagingPath, path);
|
|
10770
11135
|
published = true;
|
|
10771
|
-
syncDirectory(
|
|
10772
|
-
const staged =
|
|
11136
|
+
syncDirectory(dirname8(path));
|
|
11137
|
+
const staged = lstatSync8(stagingPath);
|
|
10773
11138
|
if (!staged.isFile() || staged.dev !== stat.dev || staged.ino !== stat.ino) {
|
|
10774
11139
|
throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "The initialized lock staging inode changed before publication completed.");
|
|
10775
11140
|
}
|
|
10776
11141
|
unlinkSync(stagingPath);
|
|
10777
|
-
syncDirectory(
|
|
11142
|
+
syncDirectory(dirname8(path));
|
|
10778
11143
|
handedOff = true;
|
|
10779
11144
|
return { path, stagingPath, fd, dev: stat.dev, ino: stat.ino };
|
|
10780
11145
|
} finally {
|
|
10781
11146
|
if (!handedOff) {
|
|
10782
11147
|
try {
|
|
10783
11148
|
for (const target of [...published ? [path] : [], stagingPath]) {
|
|
10784
|
-
const current =
|
|
11149
|
+
const current = lstatSync8(target, { throwIfNoEntry: false });
|
|
10785
11150
|
if (current?.isFile() && current.dev === stat.dev && current.ino === stat.ino)
|
|
10786
11151
|
unlinkSync(target);
|
|
10787
11152
|
}
|
|
10788
11153
|
} finally {
|
|
10789
|
-
|
|
11154
|
+
closeSync3(fd);
|
|
10790
11155
|
}
|
|
10791
11156
|
}
|
|
10792
11157
|
}
|
|
@@ -10808,25 +11173,25 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
10808
11173
|
const guardPath = `${lock.path}.recovery-guard`;
|
|
10809
11174
|
let guardFd;
|
|
10810
11175
|
try {
|
|
10811
|
-
guardFd =
|
|
11176
|
+
guardFd = openSync3(guardPath, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
10812
11177
|
} catch (error) {
|
|
10813
11178
|
if (error.code === "EEXIST")
|
|
10814
11179
|
return null;
|
|
10815
11180
|
throw error;
|
|
10816
11181
|
}
|
|
10817
|
-
const guardStat =
|
|
11182
|
+
const guardStat = fstatSync3(guardFd);
|
|
10818
11183
|
try {
|
|
10819
11184
|
writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
|
|
10820
11185
|
`);
|
|
10821
11186
|
fsyncSync(guardFd);
|
|
10822
|
-
const cacheOptions = { cacheDir:
|
|
11187
|
+
const cacheOptions = { cacheDir: dirname8(dirname8(lock.path)) };
|
|
10823
11188
|
const current = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
10824
11189
|
if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
|
|
10825
11190
|
return null;
|
|
10826
11191
|
const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
|
|
10827
11192
|
if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
|
|
10828
11193
|
return null;
|
|
10829
|
-
const directory = join11(
|
|
11194
|
+
const directory = join11(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
|
|
10830
11195
|
const archivePath = join11(directory, "original.write-lock");
|
|
10831
11196
|
const recordPath = join11(directory, "recovery.json");
|
|
10832
11197
|
assertRegularPath(archivePath, true);
|
|
@@ -10843,7 +11208,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
10843
11208
|
})}
|
|
10844
11209
|
`));
|
|
10845
11210
|
syncDirectory(directory);
|
|
10846
|
-
syncDirectory(
|
|
11211
|
+
syncDirectory(dirname8(directory));
|
|
10847
11212
|
const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
10848
11213
|
if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
|
|
10849
11214
|
return null;
|
|
@@ -10858,11 +11223,11 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
10858
11223
|
archivedPaths.push(publicationPath);
|
|
10859
11224
|
}
|
|
10860
11225
|
syncDirectory(directory);
|
|
10861
|
-
syncDirectory(
|
|
11226
|
+
syncDirectory(dirname8(lock.path));
|
|
10862
11227
|
for (const path of archivedPaths) {
|
|
10863
|
-
const before =
|
|
11228
|
+
const before = lstatSync8(path, { throwIfNoEntry: false });
|
|
10864
11229
|
const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
|
|
10865
|
-
const after =
|
|
11230
|
+
const after = lstatSync8(path, { throwIfNoEntry: false });
|
|
10866
11231
|
if (!before?.isFile() || before.dev !== lock.dev || before.ino !== lock.ino || before.nlink !== archivedPaths.length || before.mtimeMs !== lock.mtimeMs || (before.mode & 63) !== 0 || process.getuid && before.uid !== process.getuid() || !bytes || sha256Hex(bytes) !== lock.sha256 || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.mode !== after.mode || before.uid !== after.uid || before.gid !== after.gid || before.nlink !== after.nlink) {
|
|
10867
11232
|
throw new Error("Archived lock ownership or bytes changed");
|
|
10868
11233
|
}
|
|
@@ -10874,8 +11239,8 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
|
10874
11239
|
}
|
|
10875
11240
|
return archivePath;
|
|
10876
11241
|
} finally {
|
|
10877
|
-
|
|
10878
|
-
const current =
|
|
11242
|
+
closeSync3(guardFd);
|
|
11243
|
+
const current = lstatSync8(guardPath, { throwIfNoEntry: false });
|
|
10879
11244
|
if (current?.isFile() && current.dev === guardStat.dev && current.ino === guardStat.ino)
|
|
10880
11245
|
unlinkSync(guardPath);
|
|
10881
11246
|
}
|
|
@@ -10887,7 +11252,7 @@ function withSessionWriteLocks(sessionIds, options, action) {
|
|
|
10887
11252
|
const owned = [];
|
|
10888
11253
|
const assertOwned = () => {
|
|
10889
11254
|
for (const lock of owned) {
|
|
10890
|
-
const current =
|
|
11255
|
+
const current = lstatSync8(lock.path, { throwIfNoEntry: false });
|
|
10891
11256
|
if (!current?.isFile() || current.dev !== lock.dev || current.ino !== lock.ino) {
|
|
10892
11257
|
throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "A session write lock changed during the operation; no replacement is permitted.");
|
|
10893
11258
|
}
|
|
@@ -10931,12 +11296,12 @@ function withSessionWriteLocks(sessionIds, options, action) {
|
|
|
10931
11296
|
for (const lock of owned.reverse()) {
|
|
10932
11297
|
try {
|
|
10933
11298
|
for (const path of [lock.path, lock.stagingPath]) {
|
|
10934
|
-
const current =
|
|
11299
|
+
const current = lstatSync8(path, { throwIfNoEntry: false });
|
|
10935
11300
|
if (current?.isFile() && current.dev === lock.dev && current.ino === lock.ino)
|
|
10936
11301
|
unlinkSync(path);
|
|
10937
11302
|
}
|
|
10938
11303
|
} finally {
|
|
10939
|
-
|
|
11304
|
+
closeSync3(lock.fd);
|
|
10940
11305
|
}
|
|
10941
11306
|
}
|
|
10942
11307
|
}
|
|
@@ -10987,21 +11352,21 @@ function writeSkillSession(receipt, expected, options = {}) {
|
|
|
10987
11352
|
}
|
|
10988
11353
|
function syncDirectory(path) {
|
|
10989
11354
|
assertRegularPath(path);
|
|
10990
|
-
const fd =
|
|
11355
|
+
const fd = openSync3(path, constants3.O_RDONLY | constants3.O_DIRECTORY | constants3.O_NOFOLLOW);
|
|
10991
11356
|
try {
|
|
10992
11357
|
fsyncSync(fd);
|
|
10993
11358
|
} finally {
|
|
10994
|
-
|
|
11359
|
+
closeSync3(fd);
|
|
10995
11360
|
}
|
|
10996
11361
|
}
|
|
10997
11362
|
function archiveBytes(path, bytes) {
|
|
10998
11363
|
assertRegularPath(path, true);
|
|
10999
|
-
const fd =
|
|
11364
|
+
const fd = openSync3(path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
11000
11365
|
try {
|
|
11001
11366
|
writeFileSync3(fd, bytes);
|
|
11002
11367
|
fsyncSync(fd);
|
|
11003
11368
|
} finally {
|
|
11004
|
-
|
|
11369
|
+
closeSync3(fd);
|
|
11005
11370
|
}
|
|
11006
11371
|
}
|
|
11007
11372
|
function replaceSkillSession(expected, replacement, plan, options = {}, beforeCommit = () => {}) {
|
|
@@ -11028,7 +11393,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11028
11393
|
archiveBytes(receiptPath, Buffer.from(`${JSON.stringify(receipt)}
|
|
11029
11394
|
`));
|
|
11030
11395
|
syncDirectory(directory);
|
|
11031
|
-
syncDirectory(
|
|
11396
|
+
syncDirectory(dirname8(directory));
|
|
11032
11397
|
syncDirectory(selectionCacheRoot(options));
|
|
11033
11398
|
if (sha256Hex(readRegularFile(archivePath, MAX_PROFILE_DOCUMENT_BYTES)) !== before.sha256 || !sameSnapshot(readSkillSessionSnapshot(replacement.sessionId, options), expected)) {
|
|
11034
11399
|
throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The session or its preservation archive changed before replacement; retained evidence must be reviewed.");
|
|
@@ -11048,7 +11413,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11048
11413
|
// package.json
|
|
11049
11414
|
var package_default = {
|
|
11050
11415
|
name: "@hasna/skills",
|
|
11051
|
-
version: "0.10.
|
|
11416
|
+
version: "0.10.46",
|
|
11052
11417
|
description: "Skills library for AI coding agents",
|
|
11053
11418
|
type: "module",
|
|
11054
11419
|
bin: {
|
|
@@ -11185,12 +11550,12 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
11185
11550
|
import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
|
|
11186
11551
|
import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
|
|
11187
11552
|
import { hostname as osHostname } from "os";
|
|
11188
|
-
import { isAbsolute as
|
|
11553
|
+
import { isAbsolute as isAbsolute8, join as join22 } from "path";
|
|
11189
11554
|
import { createRequire } from "module";
|
|
11190
11555
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11191
|
-
import { closeSync as
|
|
11556
|
+
import { closeSync as closeSync4, fstatSync as fstatSync4, lstatSync as lstatSync9, openSync as openSync4, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync6 } from "fs";
|
|
11192
11557
|
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
11193
|
-
import { basename as
|
|
11558
|
+
import { basename as basename4, dirname as dirname9, join as join12, relative as relative3, sep as sep3 } from "path";
|
|
11194
11559
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
11195
11560
|
function envToken(name) {
|
|
11196
11561
|
return name.toUpperCase().replace(/-/g, "_");
|
|
@@ -11268,15 +11633,15 @@ var PACKAGE = "@hasna/secrets";
|
|
|
11268
11633
|
var MAX_PACKAGE_BYTES = 1024 * 1024;
|
|
11269
11634
|
function entryPoint(directory) {
|
|
11270
11635
|
const root = realpathSync2(directory);
|
|
11271
|
-
const fd =
|
|
11636
|
+
const fd = openSync4(join12(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
|
|
11272
11637
|
let pkg;
|
|
11273
11638
|
try {
|
|
11274
|
-
const stat =
|
|
11639
|
+
const stat = fstatSync4(fd);
|
|
11275
11640
|
if (!stat.isFile() || stat.size > MAX_PACKAGE_BYTES)
|
|
11276
11641
|
throw new Error("Invalid Secrets package metadata");
|
|
11277
11642
|
pkg = JSON.parse(readFileSync9(fd, "utf8"));
|
|
11278
11643
|
} finally {
|
|
11279
|
-
|
|
11644
|
+
closeSync4(fd);
|
|
11280
11645
|
}
|
|
11281
11646
|
if (!pkg || typeof pkg !== "object" || !("name" in pkg) || pkg.name !== PACKAGE) {
|
|
11282
11647
|
throw new Error("Invalid Secrets package identity");
|
|
@@ -11296,13 +11661,13 @@ function entryPoint(directory) {
|
|
|
11296
11661
|
return pathToFileURL(file).href;
|
|
11297
11662
|
}
|
|
11298
11663
|
function resolveInstalledSecrets(parentUrl) {
|
|
11299
|
-
let directory =
|
|
11664
|
+
let directory = dirname9(fileURLToPath(parentUrl));
|
|
11300
11665
|
for (;; ) {
|
|
11301
|
-
if (
|
|
11666
|
+
if (basename4(directory) !== "node_modules") {
|
|
11302
11667
|
const candidate = join12(directory, "node_modules", PACKAGE);
|
|
11303
11668
|
let absent = false;
|
|
11304
11669
|
try {
|
|
11305
|
-
|
|
11670
|
+
lstatSync9(candidate);
|
|
11306
11671
|
} catch (error) {
|
|
11307
11672
|
if (error.code !== "ENOENT")
|
|
11308
11673
|
throw error;
|
|
@@ -11311,7 +11676,7 @@ function resolveInstalledSecrets(parentUrl) {
|
|
|
11311
11676
|
if (!absent)
|
|
11312
11677
|
return entryPoint(candidate);
|
|
11313
11678
|
}
|
|
11314
|
-
const parent =
|
|
11679
|
+
const parent = dirname9(directory);
|
|
11315
11680
|
if (parent === directory)
|
|
11316
11681
|
throw new Error("Secrets SDK is not installed for this consumer");
|
|
11317
11682
|
directory = parent;
|
|
@@ -11358,7 +11723,7 @@ function homeDir(env) {
|
|
|
11358
11723
|
}
|
|
11359
11724
|
function absoluteOverride(env, key) {
|
|
11360
11725
|
const value = env[key]?.trim();
|
|
11361
|
-
return value &&
|
|
11726
|
+
return value && isAbsolute8(value) ? value : null;
|
|
11362
11727
|
}
|
|
11363
11728
|
function hasnaHomeDir(env) {
|
|
11364
11729
|
const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
|
|
@@ -12009,7 +12374,7 @@ var AUTHORITY_OVERRIDE_HEADERS = new Set([
|
|
|
12009
12374
|
]);
|
|
12010
12375
|
|
|
12011
12376
|
// src/lib/instance-credentials.ts
|
|
12012
|
-
import { closeSync as
|
|
12377
|
+
import { closeSync as closeSync5, constants as constants4, fstatSync as fstatSync5, lstatSync as lstatSync10, openSync as openSync5, readSync as readSync4 } from "fs";
|
|
12013
12378
|
|
|
12014
12379
|
// src/lib/cli-credential-profile.ts
|
|
12015
12380
|
var cliCredentialProfile;
|
|
@@ -12033,7 +12398,7 @@ function skillsProfileCredentialFiles(env, explicit) {
|
|
|
12033
12398
|
}
|
|
12034
12399
|
function fileIdentity(file) {
|
|
12035
12400
|
try {
|
|
12036
|
-
return
|
|
12401
|
+
return lstatSync10(file);
|
|
12037
12402
|
} catch (error) {
|
|
12038
12403
|
if (["ENOENT", "ENOTDIR"].includes(error.code ?? ""))
|
|
12039
12404
|
return null;
|
|
@@ -12054,14 +12419,14 @@ function captureSkillsCredentialFiles(files) {
|
|
|
12054
12419
|
function readMetadataText(file) {
|
|
12055
12420
|
let fd;
|
|
12056
12421
|
try {
|
|
12057
|
-
fd =
|
|
12422
|
+
fd = openSync5(file, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
|
|
12058
12423
|
} catch (error) {
|
|
12059
12424
|
if (["ENOENT", "ENOTDIR"].includes(error.code ?? ""))
|
|
12060
12425
|
return null;
|
|
12061
12426
|
throw new Error("Cannot safely read Skills instance configuration");
|
|
12062
12427
|
}
|
|
12063
12428
|
try {
|
|
12064
|
-
const before =
|
|
12429
|
+
const before = fstatSync5(fd);
|
|
12065
12430
|
const uid = process.getuid?.() ?? process.geteuid?.();
|
|
12066
12431
|
if (!before.isFile() || ![256, 384].includes(before.mode & 4095) || uid !== undefined && before.uid !== uid || before.size > 64 * 1024) {
|
|
12067
12432
|
throw new Error("Unsafe Skills instance configuration; expected a bounded owner-only regular file");
|
|
@@ -12069,17 +12434,17 @@ function readMetadataText(file) {
|
|
|
12069
12434
|
const bytes = Buffer.alloc(64 * 1024 + 1);
|
|
12070
12435
|
let length = 0;
|
|
12071
12436
|
while (length < bytes.length) {
|
|
12072
|
-
const count =
|
|
12437
|
+
const count = readSync4(fd, bytes, length, bytes.length - length, null);
|
|
12073
12438
|
if (!count)
|
|
12074
12439
|
break;
|
|
12075
12440
|
length += count;
|
|
12076
12441
|
}
|
|
12077
|
-
if (length > 64 * 1024 || !unchanged(before,
|
|
12442
|
+
if (length > 64 * 1024 || !unchanged(before, fstatSync5(fd)) || !unchanged(before, fileIdentity(file))) {
|
|
12078
12443
|
throw new Error("Skills instance configuration changed while reading");
|
|
12079
12444
|
}
|
|
12080
12445
|
return bytes.subarray(0, length).toString("utf8");
|
|
12081
12446
|
} finally {
|
|
12082
|
-
|
|
12447
|
+
closeSync5(fd);
|
|
12083
12448
|
}
|
|
12084
12449
|
}
|
|
12085
12450
|
function readSkillsInstanceMetadata(file) {
|
|
@@ -12446,7 +12811,7 @@ class MissingSkillsFleetError extends Error {
|
|
|
12446
12811
|
}
|
|
12447
12812
|
|
|
12448
12813
|
// src/lib/remote-files.ts
|
|
12449
|
-
import { createHash as
|
|
12814
|
+
import { createHash as createHash9 } from "crypto";
|
|
12450
12815
|
var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
12451
12816
|
function describeRemoteFiles(files) {
|
|
12452
12817
|
if (files.length > 10)
|
|
@@ -12464,7 +12829,7 @@ function describeRemoteFiles(files) {
|
|
|
12464
12829
|
});
|
|
12465
12830
|
}
|
|
12466
12831
|
function sha256(bytes) {
|
|
12467
|
-
return
|
|
12832
|
+
return createHash9("sha256").update(bytes).digest("hex");
|
|
12468
12833
|
}
|
|
12469
12834
|
async function readBoundedResponse(response, maximum) {
|
|
12470
12835
|
if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
|
|
@@ -12654,7 +13019,7 @@ async function createProfileClient() {
|
|
|
12654
13019
|
}
|
|
12655
13020
|
|
|
12656
13021
|
// src/lib/dependency-preparation.ts
|
|
12657
|
-
import { constants as
|
|
13022
|
+
import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSync6, lstatSync as lstatSync11, mkdirSync as mkdirSync4, openSync as openSync6, readFileSync as readFileSync10, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
|
|
12658
13023
|
import { join as join13 } from "path";
|
|
12659
13024
|
|
|
12660
13025
|
// src/lib/preparation-process.ts
|
|
@@ -12702,7 +13067,7 @@ var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation";
|
|
|
12702
13067
|
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.";
|
|
12703
13068
|
function hasDependencyPreparationMarker(skillPath) {
|
|
12704
13069
|
try {
|
|
12705
|
-
|
|
13070
|
+
lstatSync11(join13(skillPath, DEPENDENCY_PREPARATION_MARKER));
|
|
12706
13071
|
return true;
|
|
12707
13072
|
} catch (error) {
|
|
12708
13073
|
return error.code !== "ENOENT";
|
|
@@ -12721,21 +13086,21 @@ function claimPreparation(skillPath) {
|
|
|
12721
13086
|
}
|
|
12722
13087
|
let fd;
|
|
12723
13088
|
try {
|
|
12724
|
-
if (!
|
|
13089
|
+
if (!lstatSync11(marker).isDirectory() || lstatSync11(marker).isSymbolicLink())
|
|
12725
13090
|
return;
|
|
12726
13091
|
if (!created) {
|
|
12727
13092
|
if (readdirSync5(marker).some((name) => name !== "state.json"))
|
|
12728
13093
|
return;
|
|
12729
|
-
if (!
|
|
13094
|
+
if (!lstatSync11(state).isFile() || lstatSync11(state).isSymbolicLink() || lstatSync11(state).size > 1024)
|
|
12730
13095
|
return;
|
|
12731
13096
|
const previous = JSON.parse(readFileSync10(state, "utf8"));
|
|
12732
13097
|
if (previous.version !== 1 || previous.status !== "failed" || Object.keys(previous).length !== 2)
|
|
12733
13098
|
return;
|
|
12734
13099
|
}
|
|
12735
13100
|
mkdirSync4(active, { mode: 448 });
|
|
12736
|
-
fd =
|
|
12737
|
-
if (!
|
|
12738
|
-
|
|
13101
|
+
fd = openSync6(state, constants5.O_RDWR | constants5.O_NOFOLLOW | (created ? constants5.O_CREAT | constants5.O_EXCL : 0), 384);
|
|
13102
|
+
if (!fstatSync6(fd).isFile()) {
|
|
13103
|
+
closeSync6(fd);
|
|
12739
13104
|
return;
|
|
12740
13105
|
}
|
|
12741
13106
|
const descriptor = fd;
|
|
@@ -12747,7 +13112,7 @@ function claimPreparation(skillPath) {
|
|
|
12747
13112
|
record2("pending");
|
|
12748
13113
|
return { abandon() {
|
|
12749
13114
|
try {
|
|
12750
|
-
|
|
13115
|
+
closeSync6(descriptor);
|
|
12751
13116
|
} catch {}
|
|
12752
13117
|
}, finish(success) {
|
|
12753
13118
|
try {
|
|
@@ -12755,7 +13120,7 @@ function claimPreparation(skillPath) {
|
|
|
12755
13120
|
unlinkSync2(state);
|
|
12756
13121
|
else
|
|
12757
13122
|
record2("failed");
|
|
12758
|
-
|
|
13123
|
+
closeSync6(descriptor);
|
|
12759
13124
|
fd = undefined;
|
|
12760
13125
|
rmdirSync(active);
|
|
12761
13126
|
if (success)
|
|
@@ -12763,7 +13128,7 @@ function claimPreparation(skillPath) {
|
|
|
12763
13128
|
return true;
|
|
12764
13129
|
} catch {
|
|
12765
13130
|
try {
|
|
12766
|
-
|
|
13131
|
+
closeSync6(descriptor);
|
|
12767
13132
|
} catch {}
|
|
12768
13133
|
return false;
|
|
12769
13134
|
}
|
|
@@ -12771,7 +13136,7 @@ function claimPreparation(skillPath) {
|
|
|
12771
13136
|
} catch {
|
|
12772
13137
|
if (fd !== undefined)
|
|
12773
13138
|
try {
|
|
12774
|
-
|
|
13139
|
+
closeSync6(fd);
|
|
12775
13140
|
} catch {}
|
|
12776
13141
|
return;
|
|
12777
13142
|
}
|
|
@@ -12867,36 +13232,36 @@ import { join as join27 } from "path";
|
|
|
12867
13232
|
// src/lib/codex-corpus-write.ts
|
|
12868
13233
|
import { KernelLock } from "@hasna/contracts/kernel-lock";
|
|
12869
13234
|
import { execFileSync } from "child_process";
|
|
12870
|
-
import { createHash as
|
|
12871
|
-
import { accessSync, constants as
|
|
12872
|
-
import { basename as
|
|
13235
|
+
import { createHash as createHash10 } from "crypto";
|
|
13236
|
+
import { accessSync, constants as constants6, lstatSync as lstatSync12, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
|
|
13237
|
+
import { basename as basename5, delimiter, dirname as dirname10, isAbsolute as isAbsolute9, join as join14, relative as relative4, resolve as resolve9, sep as sep4 } from "path";
|
|
12873
13238
|
var KEY = ".native-corpus-admission";
|
|
12874
13239
|
var fail = () => {
|
|
12875
13240
|
throw new Error("CODEX_CORPUS_ADMISSION_REQUIRED: verified native admission and its existing shared lock are required");
|
|
12876
13241
|
};
|
|
12877
13242
|
function physicalTarget(path) {
|
|
12878
13243
|
const suffix = [];
|
|
12879
|
-
for (let cursor = path;; cursor =
|
|
13244
|
+
for (let cursor = path;; cursor = dirname10(cursor)) {
|
|
12880
13245
|
try {
|
|
12881
13246
|
return join14(realpathSync3(cursor), ...suffix);
|
|
12882
13247
|
} catch (error) {
|
|
12883
|
-
if (error.code !== "ENOENT" || cursor ===
|
|
13248
|
+
if (error.code !== "ENOENT" || cursor === dirname10(cursor))
|
|
12884
13249
|
throw error;
|
|
12885
13250
|
try {
|
|
12886
|
-
if (
|
|
13251
|
+
if (lstatSync12(cursor).isSymbolicLink())
|
|
12887
13252
|
fail();
|
|
12888
13253
|
} catch (statError) {
|
|
12889
13254
|
if (statError.code !== "ENOENT")
|
|
12890
13255
|
throw statError;
|
|
12891
13256
|
}
|
|
12892
|
-
suffix.unshift(
|
|
13257
|
+
suffix.unshift(basename5(cursor));
|
|
12893
13258
|
}
|
|
12894
13259
|
}
|
|
12895
13260
|
}
|
|
12896
13261
|
function codexCorpusRootForPath(path) {
|
|
12897
|
-
const target =
|
|
13262
|
+
const target = resolve9(path), physical = physicalTarget(target);
|
|
12898
13263
|
const selected = process.env.CODEX_HOME;
|
|
12899
|
-
const selectedRoots = selected &&
|
|
13264
|
+
const selectedRoots = selected && isAbsolute9(selected) ? [resolve9(selected), physicalTarget(resolve9(selected))] : [];
|
|
12900
13265
|
const classify = (candidate) => {
|
|
12901
13266
|
const checked = (root2) => {
|
|
12902
13267
|
if (candidate === root2 || candidate === join14(root2, `${KEY}.flock-v1`))
|
|
@@ -12905,10 +13270,10 @@ function codexCorpusRootForPath(path) {
|
|
|
12905
13270
|
};
|
|
12906
13271
|
for (const root2 of selectedRoots) {
|
|
12907
13272
|
const distance = relative4(root2, candidate);
|
|
12908
|
-
if (distance === "" || !distance.startsWith(`..${sep4}`) && distance !== ".." && !
|
|
13273
|
+
if (distance === "" || !distance.startsWith(`..${sep4}`) && distance !== ".." && !isAbsolute9(distance))
|
|
12909
13274
|
return checked(root2);
|
|
12910
13275
|
}
|
|
12911
|
-
for (let cursor = candidate;cursor !==
|
|
13276
|
+
for (let cursor = candidate;cursor !== dirname10(cursor); cursor = dirname10(cursor)) {
|
|
12912
13277
|
if (cursor.endsWith(`${sep4}.codex`))
|
|
12913
13278
|
return checked(cursor);
|
|
12914
13279
|
}
|
|
@@ -12920,7 +13285,7 @@ function codexCorpusRootForPath(path) {
|
|
|
12920
13285
|
return root;
|
|
12921
13286
|
}
|
|
12922
13287
|
function identity3(path) {
|
|
12923
|
-
const stat =
|
|
13288
|
+
const stat = lstatSync12(path, { bigint: true });
|
|
12924
13289
|
return { device: String(stat.dev), inode: String(stat.ino) };
|
|
12925
13290
|
}
|
|
12926
13291
|
function sameIdentity(value, expected) {
|
|
@@ -12934,12 +13299,12 @@ function executable(options) {
|
|
|
12934
13299
|
if (typeof command !== "string" || !command || command.includes("\x00") || options.codexSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.codexSha256))
|
|
12935
13300
|
return fail();
|
|
12936
13301
|
let found;
|
|
12937
|
-
if (
|
|
13302
|
+
if (isAbsolute9(command))
|
|
12938
13303
|
found = command;
|
|
12939
13304
|
else if (!command.includes(sep4)) {
|
|
12940
|
-
found = (process.env.PATH ?? "").split(delimiter).filter(
|
|
13305
|
+
found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute9).map((dir) => join14(dir, command)).find((candidate) => {
|
|
12941
13306
|
try {
|
|
12942
|
-
accessSync(candidate,
|
|
13307
|
+
accessSync(candidate, constants6.X_OK);
|
|
12943
13308
|
return true;
|
|
12944
13309
|
} catch {
|
|
12945
13310
|
return false;
|
|
@@ -12951,7 +13316,7 @@ function executable(options) {
|
|
|
12951
13316
|
const path = realpathSync3(found), initial = statSync7(path, { bigint: true });
|
|
12952
13317
|
if (!initial.isFile() || (initial.mode & 0o022n) !== 0n || (initial.mode & 0o111n) === 0n || initial.uid !== BigInt(process.getuid()) && initial.uid !== 0n)
|
|
12953
13318
|
return fail();
|
|
12954
|
-
const hash2 = () =>
|
|
13319
|
+
const hash2 = () => createHash10("sha256").update(readFileSync11(path)).digest("hex");
|
|
12955
13320
|
const digest = hash2();
|
|
12956
13321
|
if (options.codexSha256 && digest !== options.codexSha256)
|
|
12957
13322
|
return fail();
|
|
@@ -12975,7 +13340,7 @@ function acquireCodexCorpusWrite(roots, options) {
|
|
|
12975
13340
|
}
|
|
12976
13341
|
};
|
|
12977
13342
|
try {
|
|
12978
|
-
if (roots.some((root) => typeof root !== "string" || !
|
|
13343
|
+
if (roots.some((root) => typeof root !== "string" || !isAbsolute9(root) || root.includes("\x00")))
|
|
12979
13344
|
fail();
|
|
12980
13345
|
const inputs = [...new Set(roots)].map((input) => ({ input, root: realpathSync3(input) })).sort((a, b) => a.root.localeCompare(b.root));
|
|
12981
13346
|
for (const { input, root } of inputs) {
|
|
@@ -13048,7 +13413,7 @@ import { homedir as homedir3 } from "os";
|
|
|
13048
13413
|
import { basename as basename7, dirname as dirname15, join as join23 } from "path";
|
|
13049
13414
|
|
|
13050
13415
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13051
|
-
import { createHash as
|
|
13416
|
+
import { createHash as createHash11 } from "crypto";
|
|
13052
13417
|
|
|
13053
13418
|
// src/lib/codex-local-mcp-controls.ts
|
|
13054
13419
|
function isCodexLocalStdioMcpServer(name, server) {
|
|
@@ -13116,317 +13481,6 @@ function isCodexLocalStdioMcpServer(name, server) {
|
|
|
13116
13481
|
}
|
|
13117
13482
|
}
|
|
13118
13483
|
|
|
13119
|
-
// src/lib/claude-settings-witness.ts
|
|
13120
|
-
import { createHash as createHash9 } from "crypto";
|
|
13121
|
-
import { closeSync as closeSync6, constants as constants6, fstatSync as fstatSync6, lstatSync as lstatSync12, openSync as openSync6, readSync as readSync4 } from "fs";
|
|
13122
|
-
import { basename as basename5, dirname as dirname10, isAbsolute as isAbsolute9, resolve as resolve9 } from "path";
|
|
13123
|
-
var CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
13124
|
-
function need2(value, reason) {
|
|
13125
|
-
if (!value)
|
|
13126
|
-
throw new Error(`Claude settings witness ${reason}`);
|
|
13127
|
-
}
|
|
13128
|
-
function absolutePath(value) {
|
|
13129
|
-
need2(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute9(value) && resolve9(value) === value, "requires a normalized absolute path");
|
|
13130
|
-
}
|
|
13131
|
-
var sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
|
|
13132
|
-
function parents(path) {
|
|
13133
|
-
const result = [];
|
|
13134
|
-
for (let at = dirname10(path);; at = dirname10(at)) {
|
|
13135
|
-
const stat = lstatSync12(at, { bigint: true });
|
|
13136
|
-
need2(stat.isDirectory() && !stat.isSymbolicLink(), "path traverses a symlink or non-directory");
|
|
13137
|
-
result.push([at, stat]);
|
|
13138
|
-
if (at === dirname10(at))
|
|
13139
|
-
return result;
|
|
13140
|
-
}
|
|
13141
|
-
}
|
|
13142
|
-
function readNativeSettingsWitnessFile(path, budget, filename = "settings.json") {
|
|
13143
|
-
absolutePath(path);
|
|
13144
|
-
need2(basename5(path) === filename, `requires ${filename}`);
|
|
13145
|
-
need2(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
|
|
13146
|
-
const ancestors = parents(path), initial = lstatSync12(path, { bigint: true });
|
|
13147
|
-
need2(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_SETTINGS_WITNESS_LIMITS.bytes), "is not a bounded regular file");
|
|
13148
|
-
need2(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
|
|
13149
|
-
const fd = openSync6(path, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
|
|
13150
|
-
try {
|
|
13151
|
-
const opened = fstatSync6(fd, { bigint: true });
|
|
13152
|
-
need2(opened.isFile() && sameFile(initial, opened), "changed during open");
|
|
13153
|
-
const bytes = Buffer.alloc(Number(opened.size) + 1);
|
|
13154
|
-
let length = 0;
|
|
13155
|
-
while (length < bytes.length) {
|
|
13156
|
-
const count = readSync4(fd, bytes, length, bytes.length - length, null);
|
|
13157
|
-
if (!count)
|
|
13158
|
-
break;
|
|
13159
|
-
length += count;
|
|
13160
|
-
}
|
|
13161
|
-
const after = fstatSync6(fd, { bigint: true }), current = lstatSync12(path, { bigint: true });
|
|
13162
|
-
need2(current.isFile() && sameFile(opened, after) && sameFile(after, current) && BigInt(length) === opened.size, "changed during read");
|
|
13163
|
-
for (const [ancestor, before] of ancestors) {
|
|
13164
|
-
const now = lstatSync12(ancestor, { bigint: true });
|
|
13165
|
-
need2(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
13166
|
-
}
|
|
13167
|
-
budget.remaining -= length;
|
|
13168
|
-
try {
|
|
13169
|
-
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
|
|
13170
|
-
} catch {
|
|
13171
|
-
throw new Error("Claude settings witness is not strict UTF-8");
|
|
13172
|
-
}
|
|
13173
|
-
} finally {
|
|
13174
|
-
closeSync6(fd);
|
|
13175
|
-
}
|
|
13176
|
-
}
|
|
13177
|
-
function parse3(text2) {
|
|
13178
|
-
let at = 0, nodes = 0;
|
|
13179
|
-
const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
|
|
13180
|
-
const space = () => {
|
|
13181
|
-
while (at < text2.length && /[\x20\t\r\n]/.test(text2[at]))
|
|
13182
|
-
at++;
|
|
13183
|
-
};
|
|
13184
|
-
function string() {
|
|
13185
|
-
need2(text2[at] === '"', "contains invalid JSON");
|
|
13186
|
-
const start = at++;
|
|
13187
|
-
while (at < text2.length) {
|
|
13188
|
-
const character = text2[at++];
|
|
13189
|
-
if (character === '"') {
|
|
13190
|
-
let value2;
|
|
13191
|
-
try {
|
|
13192
|
-
value2 = JSON.parse(text2.slice(start, at));
|
|
13193
|
-
} catch {
|
|
13194
|
-
throw new Error("Claude settings witness contains invalid JSON string");
|
|
13195
|
-
}
|
|
13196
|
-
need2(value2.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.stringCharacters, "exceeds its string limit");
|
|
13197
|
-
return value2;
|
|
13198
|
-
}
|
|
13199
|
-
if (character === "\\")
|
|
13200
|
-
at++;
|
|
13201
|
-
}
|
|
13202
|
-
throw new Error("Claude settings witness contains an unterminated JSON string");
|
|
13203
|
-
}
|
|
13204
|
-
function value(depth) {
|
|
13205
|
-
need2(depth <= CLAUDE_SETTINGS_WITNESS_LIMITS.depth && ++nodes <= CLAUDE_SETTINGS_WITNESS_LIMITS.nodes, "exceeds its nesting or node limit");
|
|
13206
|
-
space();
|
|
13207
|
-
const character = text2[at];
|
|
13208
|
-
if (character === '"')
|
|
13209
|
-
return { kind: "string", value: string() };
|
|
13210
|
-
if (character === "{" || character === "[") {
|
|
13211
|
-
at++;
|
|
13212
|
-
space();
|
|
13213
|
-
const object4 = character === "{", end = object4 ? "}" : "]", entries = [], items = [], keys = new Set;
|
|
13214
|
-
if (text2[at] !== end)
|
|
13215
|
-
while (true) {
|
|
13216
|
-
if (object4) {
|
|
13217
|
-
space();
|
|
13218
|
-
const key = string();
|
|
13219
|
-
need2(!keys.has(key), "contains a duplicate JSON key");
|
|
13220
|
-
keys.add(key);
|
|
13221
|
-
space();
|
|
13222
|
-
need2(text2[at++] === ":", "contains invalid JSON");
|
|
13223
|
-
entries.push([key, value(depth + 1)]);
|
|
13224
|
-
} else
|
|
13225
|
-
items.push(value(depth + 1));
|
|
13226
|
-
space();
|
|
13227
|
-
if (text2[at] === end)
|
|
13228
|
-
break;
|
|
13229
|
-
need2(text2[at++] === ",", "contains invalid JSON");
|
|
13230
|
-
}
|
|
13231
|
-
at++;
|
|
13232
|
-
return object4 ? { kind: "object", entries } : { kind: "array", items };
|
|
13233
|
-
}
|
|
13234
|
-
for (const literal of ["true", "false", "null"])
|
|
13235
|
-
if (text2.startsWith(literal, at)) {
|
|
13236
|
-
at += literal.length;
|
|
13237
|
-
return { kind: "literal", value: literal };
|
|
13238
|
-
}
|
|
13239
|
-
numberPattern.lastIndex = at;
|
|
13240
|
-
const number = numberPattern.exec(text2);
|
|
13241
|
-
need2(number, "contains invalid JSON");
|
|
13242
|
-
at += number[0].length;
|
|
13243
|
-
return { kind: "number", value: number[0] };
|
|
13244
|
-
}
|
|
13245
|
-
const root = value(0);
|
|
13246
|
-
space();
|
|
13247
|
-
need2(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
|
|
13248
|
-
need2(root.entries.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.rows, "exceeds its settings field limit");
|
|
13249
|
-
return root;
|
|
13250
|
-
}
|
|
13251
|
-
var BOOLEAN_PREFERENCES = new Set([
|
|
13252
|
-
"autoScrollEnabled",
|
|
13253
|
-
"axScreenReader",
|
|
13254
|
-
"emojiCompletionEnabled",
|
|
13255
|
-
"prefersReducedMotion",
|
|
13256
|
-
"showTurnDuration",
|
|
13257
|
-
"spinnerTipsEnabled",
|
|
13258
|
-
"syntaxHighlightingDisabled",
|
|
13259
|
-
"terminalProgressBarEnabled",
|
|
13260
|
-
"terminalTitleFromRename",
|
|
13261
|
-
"verbose",
|
|
13262
|
-
"wheelScrollAccelerationEnabled"
|
|
13263
|
-
]);
|
|
13264
|
-
var ENUM_PREFERENCES = Object.freeze({
|
|
13265
|
-
editorMode: ["normal", "vim"],
|
|
13266
|
-
tui: ["default", "fullscreen"],
|
|
13267
|
-
viewMode: ["default", "verbose", "focus"]
|
|
13268
|
-
});
|
|
13269
|
-
var BUILTIN_MODEL_SELECTIONS = new Set([
|
|
13270
|
-
"default",
|
|
13271
|
-
"best",
|
|
13272
|
-
"fable",
|
|
13273
|
-
"fable[1m]",
|
|
13274
|
-
"sonnet",
|
|
13275
|
-
"sonnet[1m]",
|
|
13276
|
-
"opus",
|
|
13277
|
-
"opus[1m]",
|
|
13278
|
-
"haiku",
|
|
13279
|
-
"opusplan",
|
|
13280
|
-
"claude-fable-5-1",
|
|
13281
|
-
"claude-fable-5",
|
|
13282
|
-
"claude-fable-5[1m]",
|
|
13283
|
-
"claude-opus-5",
|
|
13284
|
-
"claude-sonnet-5",
|
|
13285
|
-
"claude-haiku-4-5-20251001",
|
|
13286
|
-
"claude-opus-4-6",
|
|
13287
|
-
"claude-sonnet-4-5",
|
|
13288
|
-
"claude-sonnet-4-5-20250929",
|
|
13289
|
-
"claude-opus-4-8",
|
|
13290
|
-
"claude-opus-4-8[1m]",
|
|
13291
|
-
"claude-opus-4-7",
|
|
13292
|
-
"claude-sonnet-4-6",
|
|
13293
|
-
"claude-opus-4-5-20251101",
|
|
13294
|
-
"claude-opus-4-5",
|
|
13295
|
-
"claude-haiku-4-5",
|
|
13296
|
-
"claude-fable-5-1[1m]",
|
|
13297
|
-
"claude-opus-5[1m]",
|
|
13298
|
-
"claude-opus-4-7[1m]",
|
|
13299
|
-
"claude-opus-4-6[1m]",
|
|
13300
|
-
"claude-sonnet-4-6[1m]"
|
|
13301
|
-
]);
|
|
13302
|
-
function canonical2(value) {
|
|
13303
|
-
if (value.kind === "object")
|
|
13304
|
-
return { kind: "object", entries: value.entries.map(([key, child]) => [key, canonical2(child)]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0) };
|
|
13305
|
-
if (value.kind === "array")
|
|
13306
|
-
return { kind: "array", items: value.items.map(canonical2) };
|
|
13307
|
-
return value;
|
|
13308
|
-
}
|
|
13309
|
-
var CLAUDE_COORDINATED_HOOK_EVENTS = ["PreToolUse", "PostToolUse", "Stop", "Notification", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart"];
|
|
13310
|
-
function assertClaudeHookEventsReplacement(before, replacement, events) {
|
|
13311
|
-
need2(Array.isArray(events) && events.length > 0 && events.length <= CLAUDE_COORDINATED_HOOK_EVENTS.length && new Set(events).size === events.length && events.every((event) => CLAUDE_COORDINATED_HOOK_EVENTS.includes(event)), "requires explicit supported hook events");
|
|
13312
|
-
const label = events.join(",");
|
|
13313
|
-
function bounded(text2) {
|
|
13314
|
-
need2(typeof text2 === "string" && Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, `${label} update exceeds its byte limit`);
|
|
13315
|
-
return parse3(text2);
|
|
13316
|
-
}
|
|
13317
|
-
const original = bounded(before ?? "{}"), next = bounded(replacement);
|
|
13318
|
-
const hadHooks = original.entries.some(([key]) => key === "hooks");
|
|
13319
|
-
function withoutOwnedEvents(value) {
|
|
13320
|
-
const hooks = value.entries.find(([key]) => key === "hooks")?.[1];
|
|
13321
|
-
if (hooks !== undefined) {
|
|
13322
|
-
need2(hooks.kind === "object", `${label} update requires an object of hooks`);
|
|
13323
|
-
for (const event of events) {
|
|
13324
|
-
const selected = hooks.entries.find(([key]) => key === event)?.[1];
|
|
13325
|
-
need2(selected === undefined || selected.kind === "array", `${event} update requires a ${event} array`);
|
|
13326
|
-
}
|
|
13327
|
-
hooks.entries = hooks.entries.filter(([key]) => !events.includes(key));
|
|
13328
|
-
if (!hadHooks && hooks.entries.length === 0)
|
|
13329
|
-
value.entries = value.entries.filter(([key]) => key !== "hooks");
|
|
13330
|
-
}
|
|
13331
|
-
return canonical2(value);
|
|
13332
|
-
}
|
|
13333
|
-
need2(JSON.stringify(withoutOwnedEvents(original)) === JSON.stringify(withoutOwnedEvents(next)), `${label} update changed settings outside hooks.${label}`);
|
|
13334
|
-
}
|
|
13335
|
-
var PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
|
|
13336
|
-
function inferencePreference(key, child, version) {
|
|
13337
|
-
if (key === "skipDangerousModePermissionPrompt") {
|
|
13338
|
-
need2(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid dialog acknowledgment");
|
|
13339
|
-
return false;
|
|
13340
|
-
}
|
|
13341
|
-
if (key === "effortLevel") {
|
|
13342
|
-
need2(child.kind === "string" && PERSISTED_EFFORT_LEVELS.has(child.value), "contains an invalid persisted effort level");
|
|
13343
|
-
return true;
|
|
13344
|
-
}
|
|
13345
|
-
if (key !== "modelSettings")
|
|
13346
|
-
return false;
|
|
13347
|
-
need2(child.kind === "object", "requires an object of model preferences");
|
|
13348
|
-
child.entries = child.entries.filter(([model, preferences]) => {
|
|
13349
|
-
if (version === 2 && !BUILTIN_MODEL_SELECTIONS.has(model) || preferences.kind !== "object" || preferences.entries.length !== 1 || preferences.entries[0][0] !== "effortLevel")
|
|
13350
|
-
return true;
|
|
13351
|
-
const effort = preferences.entries[0][1];
|
|
13352
|
-
need2(effort.kind === "string" && PERSISTED_EFFORT_LEVELS.has(effort.value), "contains an invalid persisted model effort level");
|
|
13353
|
-
return false;
|
|
13354
|
-
});
|
|
13355
|
-
return child.entries.length === 0;
|
|
13356
|
-
}
|
|
13357
|
-
function settingsDigest(text2, version = 1) {
|
|
13358
|
-
const value = parse3(text2);
|
|
13359
|
-
value.entries = value.entries.filter(([key, child]) => {
|
|
13360
|
-
if (version !== 1 && inferencePreference(key, child, version))
|
|
13361
|
-
return false;
|
|
13362
|
-
if (key === "model") {
|
|
13363
|
-
need2(child.kind === "string", "contains an invalid model selection");
|
|
13364
|
-
return !BUILTIN_MODEL_SELECTIONS.has(child.value);
|
|
13365
|
-
}
|
|
13366
|
-
if (BOOLEAN_PREFERENCES.has(key)) {
|
|
13367
|
-
need2(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid display preference");
|
|
13368
|
-
return false;
|
|
13369
|
-
}
|
|
13370
|
-
if (Object.hasOwn(ENUM_PREFERENCES, key)) {
|
|
13371
|
-
need2(child.kind === "string" && ENUM_PREFERENCES[key].includes(child.value), "contains an invalid display preference");
|
|
13372
|
-
return false;
|
|
13373
|
-
}
|
|
13374
|
-
return true;
|
|
13375
|
-
});
|
|
13376
|
-
return createHash9("sha256").update(`hasna.skills.claude-settings.v${version}\x00`).update(JSON.stringify(canonical2(value))).digest("hex");
|
|
13377
|
-
}
|
|
13378
|
-
function hashClaudeSettingsReplacement(text2, budget) {
|
|
13379
|
-
need2(typeof text2 === "string", "requires settings text");
|
|
13380
|
-
const bytes = Buffer.byteLength(text2);
|
|
13381
|
-
need2(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
13382
|
-
budget.remaining -= bytes;
|
|
13383
|
-
return settingsDigest(text2);
|
|
13384
|
-
}
|
|
13385
|
-
function captureClaudeSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
13386
|
-
return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget)) };
|
|
13387
|
-
}
|
|
13388
|
-
function captureClaudeSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
13389
|
-
return { path, hashMode: "claude-settings-v2", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 2) };
|
|
13390
|
-
}
|
|
13391
|
-
function hashClaudeSettingsReplacementV2(text2, budget) {
|
|
13392
|
-
need2(typeof text2 === "string", "requires settings text");
|
|
13393
|
-
const bytes = Buffer.byteLength(text2);
|
|
13394
|
-
need2(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
13395
|
-
budget.remaining -= bytes;
|
|
13396
|
-
return settingsDigest(text2, 2);
|
|
13397
|
-
}
|
|
13398
|
-
function captureClaudeSettingsV3(path, budget = { remaining: 256 * 1024 * 1024 }) {
|
|
13399
|
-
return { path, hashMode: "claude-settings-v3", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 3) };
|
|
13400
|
-
}
|
|
13401
|
-
function hashClaudeSettingsReplacementV3(text2, budget) {
|
|
13402
|
-
need2(typeof text2 === "string", "requires settings text");
|
|
13403
|
-
const bytes = Buffer.byteLength(text2);
|
|
13404
|
-
need2(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
|
|
13405
|
-
budget.remaining -= bytes;
|
|
13406
|
-
return settingsDigest(text2, 3);
|
|
13407
|
-
}
|
|
13408
|
-
function upgradeClaudeSettingsWitness(previous, reviewedSettingsPath) {
|
|
13409
|
-
need2(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "claude-settings-v1" || previous.hashMode === "claude-settings-v2") && typeof previous.sha256 === "string" && /^[a-f0-9]{64}$/.test(previous.sha256), "requires an exact legacy settings witness");
|
|
13410
|
-
const budget = { remaining: 2 * CLAUDE_SETTINGS_WITNESS_LIMITS.bytes };
|
|
13411
|
-
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget), current = readNativeSettingsWitnessFile(previous.path, budget);
|
|
13412
|
-
const beforeDigest = previous.hashMode === undefined || previous.hashMode === "bytes" ? createHash9("sha256").update(before).digest("hex") : settingsDigest(before, previous.hashMode === "claude-settings-v1" ? 1 : 2);
|
|
13413
|
-
need2(beforeDigest === previous.sha256, "reviewed preimage does not match the legacy witness");
|
|
13414
|
-
const sha2562 = settingsDigest(current, 3);
|
|
13415
|
-
need2(settingsDigest(before, 3) === sha2562, "non-preference settings changed; explicit discovery review required");
|
|
13416
|
-
return { path: previous.path, hashMode: "claude-settings-v3", sha256: sha2562 };
|
|
13417
|
-
}
|
|
13418
|
-
function hashNativeJsonControls(text2, excludedMetadata) {
|
|
13419
|
-
need2(Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
|
|
13420
|
-
const value = parse3(text2);
|
|
13421
|
-
value.entries = value.entries.filter(([key, child]) => {
|
|
13422
|
-
if (key !== excludedMetadata)
|
|
13423
|
-
return true;
|
|
13424
|
-
need2(child.kind === "string" && child.value.length <= 128, "has invalid version metadata");
|
|
13425
|
-
return false;
|
|
13426
|
-
});
|
|
13427
|
-
return createHash9("sha256").update("hasna.skills.native-json-controls.v1\x00").update(JSON.stringify(canonical2(value))).digest("hex");
|
|
13428
|
-
}
|
|
13429
|
-
|
|
13430
13484
|
// src/lib/codex-plugin-skill-controls.ts
|
|
13431
13485
|
import { basename as basename6, dirname as dirname11, join as join16, relative as relative5, resolve as resolve10, sep as sep5 } from "path";
|
|
13432
13486
|
import { lstatSync as lstatSync13, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync6 } from "fs";
|
|
@@ -14117,7 +14171,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
|
|
|
14117
14171
|
const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
|
|
14118
14172
|
const nativeSkill = { name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true };
|
|
14119
14173
|
const denial = denials.find((item) => item.path === document2);
|
|
14120
|
-
const explicitDeny = denial !== undefined && remotePluginId !== undefined && catalog.version === "codex-cli 0.160.0" && denial.name === parsed.name && denial.sha256 ===
|
|
14174
|
+
const explicitDeny = denial !== undefined && remotePluginId !== undefined && catalog.version === "codex-cli 0.160.0" && denial.name === parsed.name && denial.sha256 === createHash11("sha256").update(read2(document2)).digest("hex") && Array.isArray(rules) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false);
|
|
14121
14175
|
if (denial && !explicitDeny)
|
|
14122
14176
|
refuse2();
|
|
14123
14177
|
if (explicitDeny)
|
|
@@ -14204,7 +14258,7 @@ enabled = false
|
|
|
14204
14258
|
}
|
|
14205
14259
|
|
|
14206
14260
|
// src/lib/codex-native-skill-policy.ts
|
|
14207
|
-
import { createHash as
|
|
14261
|
+
import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
|
|
14208
14262
|
import { closeSync as closeSync7, constants as constants7, fstatSync as fstatSync7, fsyncSync as fsyncSync2, lstatSync as lstatSync14, mkdirSync as mkdirSync5, openSync as openSync7, readFileSync as readFileSync12, readSync as readSync5, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
14209
14263
|
import { dirname as dirname12, isAbsolute as isAbsolute12, join as join17, resolve as resolve11 } from "path";
|
|
14210
14264
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
@@ -14332,7 +14386,7 @@ function verifyCodexNativeBridgeDocument(path, expectedContent, expectedSha256)
|
|
|
14332
14386
|
if (realpathSync7.native(path) !== path)
|
|
14333
14387
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document real path differs from its lexical path");
|
|
14334
14388
|
const bytes = readBounded(path, MAX_BRIDGE_DOCUMENT_BYTES, "the bridge document");
|
|
14335
|
-
const sha2562 =
|
|
14389
|
+
const sha2562 = createHash12("sha256").update(bytes).digest("hex");
|
|
14336
14390
|
if (!bytes.equals(Buffer.from(expectedContent, "utf8")) || sha2562 !== expectedSha256)
|
|
14337
14391
|
refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document bytes differ from the managed bridge");
|
|
14338
14392
|
return { path, sha256: sha2562 };
|
|
@@ -14800,8 +14854,8 @@ function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
|
|
|
14800
14854
|
}
|
|
14801
14855
|
|
|
14802
14856
|
// src/lib/codex-settings-witness-v1.ts
|
|
14803
|
-
import { createHash as
|
|
14804
|
-
var sha = (text2) =>
|
|
14857
|
+
import { createHash as createHash13 } from "crypto";
|
|
14858
|
+
var sha = (text2) => createHash13("sha256").update(text2).digest("hex");
|
|
14805
14859
|
var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
14806
14860
|
var VERBOSITY = new Set(["low", "medium", "high"]);
|
|
14807
14861
|
function need3(value) {
|
|
@@ -14847,8 +14901,8 @@ function numericTokens(text2) {
|
|
|
14847
14901
|
}
|
|
14848
14902
|
return result;
|
|
14849
14903
|
}
|
|
14850
|
-
function canonical3(value, depth = 0,
|
|
14851
|
-
need3(depth <= 32 && --
|
|
14904
|
+
function canonical3(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
14905
|
+
need3(depth <= 32 && --budget2.remaining >= 0);
|
|
14852
14906
|
if (value === null)
|
|
14853
14907
|
return ["null"];
|
|
14854
14908
|
if (typeof value === "string")
|
|
@@ -14864,13 +14918,13 @@ function canonical3(value, depth = 0, budget = { remaining: 65536 }) {
|
|
|
14864
14918
|
return ["date", value.toISOString()];
|
|
14865
14919
|
}
|
|
14866
14920
|
if (Array.isArray(value))
|
|
14867
|
-
return ["array", value.map((child) => canonical3(child, depth + 1,
|
|
14921
|
+
return ["array", value.map((child) => canonical3(child, depth + 1, budget2))];
|
|
14868
14922
|
need3(value && typeof value === "object");
|
|
14869
|
-
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical3(child, depth + 1,
|
|
14923
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical3(child, depth + 1, budget2)])];
|
|
14870
14924
|
}
|
|
14871
|
-
function hashCodexSettingsReplacement(text2,
|
|
14872
|
-
need3(Buffer.byteLength(text2) <= 1024 * 1024 &&
|
|
14873
|
-
|
|
14925
|
+
function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
14926
|
+
need3(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
14927
|
+
budget2.remaining -= Buffer.byteLength(text2);
|
|
14874
14928
|
let config;
|
|
14875
14929
|
try {
|
|
14876
14930
|
config = Bun.TOML.parse(text2);
|
|
@@ -14889,8 +14943,8 @@ function hashCodexSettingsReplacement(text2, budget = { remaining: 256 * 1024 *
|
|
|
14889
14943
|
}
|
|
14890
14944
|
return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical3(config), numericTokens(text2)])}`);
|
|
14891
14945
|
}
|
|
14892
|
-
function captureCodexSettings(path,
|
|
14893
|
-
const text2 = readNativeSettingsWitnessFile(path,
|
|
14946
|
+
function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
14947
|
+
const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
|
|
14894
14948
|
return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
|
|
14895
14949
|
}
|
|
14896
14950
|
// src/lib/codex-mcp-discovery-projection.ts
|
|
@@ -14958,9 +15012,9 @@ function projectRegularCodexMcp(config, source) {
|
|
|
14958
15012
|
}
|
|
14959
15013
|
|
|
14960
15014
|
// src/lib/codex-settings-witness.ts
|
|
14961
|
-
import { createHash as
|
|
15015
|
+
import { createHash as createHash14 } from "crypto";
|
|
14962
15016
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
14963
|
-
var sha2 = (text2) =>
|
|
15017
|
+
var sha2 = (text2) => createHash14("sha256").update(text2).digest("hex");
|
|
14964
15018
|
var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
14965
15019
|
var VERBOSITY2 = new Set(["low", "medium", "high"]);
|
|
14966
15020
|
var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
@@ -15015,8 +15069,8 @@ function numericTokens2(text2) {
|
|
|
15015
15069
|
}
|
|
15016
15070
|
return result.sort();
|
|
15017
15071
|
}
|
|
15018
|
-
function canonical4(value, depth = 0,
|
|
15019
|
-
need4(depth <= 32 && --
|
|
15072
|
+
function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
|
|
15073
|
+
need4(depth <= 32 && --budget2.remaining >= 0);
|
|
15020
15074
|
if (value === null)
|
|
15021
15075
|
return ["null"];
|
|
15022
15076
|
if (typeof value === "string")
|
|
@@ -15032,9 +15086,9 @@ function canonical4(value, depth = 0, budget = { remaining: 65536 }) {
|
|
|
15032
15086
|
return ["date", value.toISOString()];
|
|
15033
15087
|
}
|
|
15034
15088
|
if (Array.isArray(value))
|
|
15035
|
-
return ["array", value.map((child) => canonical4(child, depth + 1,
|
|
15089
|
+
return ["array", value.map((child) => canonical4(child, depth + 1, budget2))];
|
|
15036
15090
|
need4(isRecord3(value));
|
|
15037
|
-
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1,
|
|
15091
|
+
return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget2)])];
|
|
15038
15092
|
}
|
|
15039
15093
|
function normalize3(config, version = 2) {
|
|
15040
15094
|
if (isRecord3(config.hooks)) {
|
|
@@ -15210,40 +15264,40 @@ function settingsDigest2(text2, version = 2) {
|
|
|
15210
15264
|
normalize3(projected.config, version);
|
|
15211
15265
|
return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical4(projected.config), numericTokens2(projected.numericSource)])}`);
|
|
15212
15266
|
}
|
|
15213
|
-
function hashCodexSettingsReplacementV2(text2,
|
|
15214
|
-
need4(Buffer.byteLength(text2) <= 1024 * 1024 &&
|
|
15215
|
-
|
|
15267
|
+
function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15268
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15269
|
+
budget2.remaining -= Buffer.byteLength(text2);
|
|
15216
15270
|
return settingsDigest2(text2);
|
|
15217
15271
|
}
|
|
15218
|
-
function captureCodexSettingsV2(path,
|
|
15219
|
-
const text2 = readNativeSettingsWitnessFile(path,
|
|
15272
|
+
function captureCodexSettingsV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15273
|
+
const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
|
|
15220
15274
|
return { path, hashMode: "codex-settings-v2", sha256: settingsDigest2(text2) };
|
|
15221
15275
|
}
|
|
15222
|
-
function readCodexSettingsPreimage(path,
|
|
15223
|
-
return readNativeSettingsWitnessFile(path,
|
|
15276
|
+
function readCodexSettingsPreimage(path, budget2 = { remaining: 2 * 1024 * 1024 }) {
|
|
15277
|
+
return readNativeSettingsWitnessFile(path, budget2, "config.toml");
|
|
15224
15278
|
}
|
|
15225
15279
|
function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
|
|
15226
15280
|
need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "codex-settings-v1") && /^[a-f0-9]{64}$/.test(previous.sha256));
|
|
15227
|
-
const
|
|
15228
|
-
const before = readNativeSettingsWitnessFile(reviewedSettingsPath,
|
|
15281
|
+
const budget2 = { remaining: 2 * 1024 * 1024 };
|
|
15282
|
+
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
|
|
15229
15283
|
need4((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
|
|
15230
15284
|
const sha2562 = settingsDigest2(current);
|
|
15231
15285
|
if (settingsDigest2(before) !== sha2562)
|
|
15232
15286
|
throw new Error("Codex non-preference settings changed; explicit discovery review required");
|
|
15233
15287
|
return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
|
|
15234
15288
|
}
|
|
15235
|
-
function hashCodexSettingsReplacementV3(text2,
|
|
15236
|
-
need4(Buffer.byteLength(text2) <= 1024 * 1024 &&
|
|
15237
|
-
|
|
15289
|
+
function hashCodexSettingsReplacementV3(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15290
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15291
|
+
budget2.remaining -= Buffer.byteLength(text2);
|
|
15238
15292
|
return settingsDigest2(text2, 3);
|
|
15239
15293
|
}
|
|
15240
|
-
function captureCodexSettingsV3(path,
|
|
15241
|
-
return { path, hashMode: "codex-settings-v3", sha256: settingsDigest2(readNativeSettingsWitnessFile(path,
|
|
15294
|
+
function captureCodexSettingsV3(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15295
|
+
return { path, hashMode: "codex-settings-v3", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 3) };
|
|
15242
15296
|
}
|
|
15243
15297
|
function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
|
|
15244
15298
|
need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
|
|
15245
|
-
const
|
|
15246
|
-
const before = readNativeSettingsWitnessFile(reviewedSettingsPath,
|
|
15299
|
+
const budget2 = { remaining: 2 * 1024 * 1024 };
|
|
15300
|
+
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
|
|
15247
15301
|
const original = previous.hashMode === "codex-settings-v2" ? settingsDigest2(before) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
|
|
15248
15302
|
need4(original === previous.sha256);
|
|
15249
15303
|
const sha2562 = settingsDigest2(current, 3);
|
|
@@ -15251,18 +15305,18 @@ function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
|
|
|
15251
15305
|
throw new Error("Codex settings outside the reviewed V3 contract changed; explicit discovery review required");
|
|
15252
15306
|
return { path: previous.path, hashMode: "codex-settings-v3", sha256: sha2562 };
|
|
15253
15307
|
}
|
|
15254
|
-
function hashCodexSettingsReplacementV4(text2,
|
|
15255
|
-
need4(Buffer.byteLength(text2) <= 1024 * 1024 &&
|
|
15256
|
-
|
|
15308
|
+
function hashCodexSettingsReplacementV4(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15309
|
+
need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
|
|
15310
|
+
budget2.remaining -= Buffer.byteLength(text2);
|
|
15257
15311
|
return settingsDigest2(text2, 4);
|
|
15258
15312
|
}
|
|
15259
|
-
function captureCodexSettingsV4(path,
|
|
15260
|
-
return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path,
|
|
15313
|
+
function captureCodexSettingsV4(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
15314
|
+
return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 4) };
|
|
15261
15315
|
}
|
|
15262
15316
|
function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
15263
15317
|
need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
|
|
15264
|
-
const
|
|
15265
|
-
const before = readNativeSettingsWitnessFile(reviewedSettingsPath,
|
|
15318
|
+
const budget2 = { remaining: 2 * 1024 * 1024 };
|
|
15319
|
+
const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
|
|
15266
15320
|
const original = previous.hashMode === "codex-settings-v3" ? settingsDigest2(before, 3) : previous.hashMode === "codex-settings-v2" ? settingsDigest2(before, 2) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
|
|
15267
15321
|
need4(original === previous.sha256);
|
|
15268
15322
|
const sha2562 = settingsDigest2(current, 4);
|
|
@@ -15272,7 +15326,7 @@ function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
|
|
|
15272
15326
|
}
|
|
15273
15327
|
|
|
15274
15328
|
// src/lib/agent-integration.ts
|
|
15275
|
-
import { createHash as
|
|
15329
|
+
import { createHash as createHash17, randomUUID as randomUUID3 } from "crypto";
|
|
15276
15330
|
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
15277
15331
|
import { existsSync as existsSync9, lstatSync as lstatSync19, statSync as statSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync14, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync5, realpathSync as realpathSync9, renameSync as renameSync3, rmdirSync as rmdirSync2, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync10, closeSync as closeSync10, fsyncSync as fsyncSync4, fstatSync as fstatSync10, readSync as readSync8, constants as constants10, linkSync as linkSync2 } from "fs";
|
|
15278
15332
|
import { dirname as dirname14, join as join21, resolve as resolve13, relative as relative6, isAbsolute as isAbsolute13, sep as sep6 } from "path";
|
|
@@ -15359,7 +15413,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
|
|
|
15359
15413
|
}
|
|
15360
15414
|
|
|
15361
15415
|
// src/lib/agent-bridge.ts
|
|
15362
|
-
import { createHash as
|
|
15416
|
+
import { createHash as createHash15 } from "crypto";
|
|
15363
15417
|
import { lstatSync as lstatSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
|
|
15364
15418
|
import { join as join19 } from "path";
|
|
15365
15419
|
var CLI_BRIDGE_NAME = "skills-cli";
|
|
@@ -15378,7 +15432,7 @@ Use the Skills CLI as the skill authority for this station.
|
|
|
15378
15432
|
|
|
15379
15433
|
This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
|
|
15380
15434
|
`;
|
|
15381
|
-
var CLI_BRIDGE_DIGEST =
|
|
15435
|
+
var CLI_BRIDGE_DIGEST = createHash15("sha256").update(markdown).digest("hex");
|
|
15382
15436
|
var CLI_BRIDGE_FILES = Object.freeze({
|
|
15383
15437
|
"SKILL.md": markdown,
|
|
15384
15438
|
".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
|
|
@@ -15703,31 +15757,44 @@ function sumiConfigPath(home) {
|
|
|
15703
15757
|
return join20(sumiConfigDirectory(home), "sumi.json");
|
|
15704
15758
|
}
|
|
15705
15759
|
function renderSumiPlugin(command, profile) {
|
|
15760
|
+
return renderSumiPluginVersion(command, profile, false);
|
|
15761
|
+
}
|
|
15762
|
+
function isManagedSumiPlugin(text2, command, profile) {
|
|
15763
|
+
return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, true);
|
|
15764
|
+
}
|
|
15765
|
+
function renderSumiPluginVersion(command, profile, legacy) {
|
|
15766
|
+
const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
|
|
15706
15767
|
return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
|
|
15707
15768
|
import { spawn } from "node:child_process";
|
|
15708
15769
|
const command = ${JSON.stringify(command)};
|
|
15709
15770
|
const profile = ${JSON.stringify(profile)};
|
|
15710
|
-
function
|
|
15771
|
+
${legacy ? "" : `function refusal() {
|
|
15772
|
+
const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
|
|
15773
|
+
error.name = "SkillsHookRefusal";
|
|
15774
|
+
error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
|
|
15775
|
+
return error;
|
|
15776
|
+
}
|
|
15777
|
+
`}function context(input) {
|
|
15711
15778
|
return new Promise((resolve, reject) => {
|
|
15712
15779
|
const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
15713
15780
|
const chunks = []; let bytes = 0, failure;
|
|
15714
|
-
const timer = setTimeout(() => { failure =
|
|
15715
|
-
child.stdout.on("data", chunk => { bytes += chunk.length; if (bytes > 1048576) { failure =
|
|
15781
|
+
const timer = setTimeout(() => { failure = ${failure("Skills prompt hook timed out")}; child.kill("SIGKILL"); }, 15000);
|
|
15782
|
+
child.stdout.on("data", chunk => { bytes += chunk.length; if (bytes > 1048576) { failure = ${failure("Skills prompt hook output exceeded its limit")}; child.kill("SIGKILL"); } else chunks.push(chunk); });
|
|
15716
15783
|
child.stderr.on("data", () => {});
|
|
15717
15784
|
child.stdin.on("error", () => {});
|
|
15718
|
-
child.on("error", error => { clearTimeout(timer); reject(error); });
|
|
15785
|
+
child.on("error", error => { clearTimeout(timer); reject(${legacy ? "error" : "refusal()"}); });
|
|
15719
15786
|
child.on("close", code => {
|
|
15720
15787
|
clearTimeout(timer);
|
|
15721
|
-
if (failure || code !== 0) return reject(failure ??
|
|
15788
|
+
if (failure || code !== 0) return reject(failure ?? ${failure("Skills prompt hook failed")});
|
|
15722
15789
|
try {
|
|
15723
15790
|
const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
15724
|
-
if (!result || typeof result !== "object" || Array.isArray(result)) throw
|
|
15725
|
-
if (result.decision === "block" || result.continue === false) throw
|
|
15726
|
-
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw
|
|
15791
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
|
|
15792
|
+
if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
|
|
15793
|
+
if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
|
|
15727
15794
|
const text = result.hookSpecificOutput?.additionalContext ?? "";
|
|
15728
|
-
if (typeof text !== "string") throw
|
|
15795
|
+
if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
|
|
15729
15796
|
resolve(text);
|
|
15730
|
-
} catch (error) { reject(error); }
|
|
15797
|
+
} catch (error) { reject(${legacy ? "error" : "refusal()"}); }
|
|
15731
15798
|
});
|
|
15732
15799
|
child.stdin.end(JSON.stringify(input));
|
|
15733
15800
|
});
|
|
@@ -15735,13 +15802,14 @@ function context(input) {
|
|
|
15735
15802
|
export default {
|
|
15736
15803
|
id: "local.hasna-skills-cli",
|
|
15737
15804
|
async setup(ctx) {
|
|
15738
|
-
if (!ctx?.session?.hook || !ctx?.session?.get || !ctx?.skill?.transform || !ctx?.tool?.hook) throw
|
|
15805
|
+
if (!ctx?.session?.hook || !ctx?.session?.get || !ctx?.skill?.transform || !ctx?.tool?.hook) throw ${failure("Unsupported Sumi native Skills contract")};
|
|
15739
15806
|
const registrations = [];
|
|
15740
15807
|
const sessions = new Set();
|
|
15741
15808
|
async function input(sessionID, prompt, first) {
|
|
15742
15809
|
// The Promise adapter unwraps single-data endpoint responses.
|
|
15743
|
-
const session = await ctx.session.get({ sessionID });
|
|
15744
|
-
|
|
15810
|
+
${legacy ? "const session = await ctx.session.get({ sessionID });" : `let session;
|
|
15811
|
+
try { session = await ctx.session.get({ sessionID }); } catch { throw refusal(); }`}
|
|
15812
|
+
if (!session || session.id !== sessionID || typeof session.location?.directory !== "string" || (session.parentID !== undefined && typeof session.parentID !== "string") || session.parentID === sessionID) throw ${failure("Invalid Sumi session custody")};
|
|
15745
15813
|
return { hook_event_name: first ? session.parentID ? "SubagentStart" : "SessionStart" : "UserPromptSubmit", cwd: session.location.directory, session_id: sessionID, parent_session_id: session.parentID ?? null, prompt, restore: true };
|
|
15746
15814
|
}
|
|
15747
15815
|
registrations.push(await ctx.skill.transform(editor => {
|
|
@@ -15750,11 +15818,11 @@ export default {
|
|
|
15750
15818
|
// Native config transforms may add skills after this transform. Guard
|
|
15751
15819
|
// both direct tool calls and prompt attachments at the consumption edge.
|
|
15752
15820
|
registrations.push(await ctx.tool.hook("execute.before", event => {
|
|
15753
|
-
if (event.tool === "skill" && event.input?.id !== "skills-cli") throw
|
|
15821
|
+
if (event.tool === "skill" && event.input?.id !== "skills-cli") throw ${failure("Native Sumi skill payload refused; use Skills CLI")};
|
|
15754
15822
|
}));
|
|
15755
15823
|
registrations.push(await ctx.session.hook("prompt", async event => {
|
|
15756
|
-
if (typeof event.prompt?.text !== "string") throw
|
|
15757
|
-
if (event.prompt.skills !== undefined && (!Array.isArray(event.prompt.skills) || event.prompt.skills.some(skill => skill?.id !== "skills-cli"))) throw
|
|
15824
|
+
if (typeof event.prompt?.text !== "string") throw ${failure("Invalid Sumi prompt")};
|
|
15825
|
+
if (event.prompt.skills !== undefined && (!Array.isArray(event.prompt.skills) || event.prompt.skills.some(skill => skill?.id !== "skills-cli"))) throw ${failure("Native Sumi skill attachment refused; use Skills CLI")};
|
|
15758
15826
|
await context(await input(event.sessionID, event.prompt.text, !sessions.has(event.sessionID)));
|
|
15759
15827
|
sessions.add(event.sessionID);
|
|
15760
15828
|
}));
|
|
@@ -15779,7 +15847,7 @@ var SUMI_SKILL_PERMISSIONS = [
|
|
|
15779
15847
|
|
|
15780
15848
|
// src/lib/codex-hook-trust-files.ts
|
|
15781
15849
|
import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, fsyncSync as fsyncSync3, lstatSync as lstatSync18, openSync as openSync9, readSync as readSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
15782
|
-
import { createHash as
|
|
15850
|
+
import { createHash as createHash16 } from "crypto";
|
|
15783
15851
|
import { dirname as dirname13 } from "path";
|
|
15784
15852
|
function need5(value, code) {
|
|
15785
15853
|
if (!value)
|
|
@@ -15815,7 +15883,7 @@ function snapshot(file, privateFile = false, options = {}) {
|
|
|
15815
15883
|
}
|
|
15816
15884
|
need5(length === Number(before.size) && sameStat(before, fstatSync9(fd, { bigint: true })) && sameStat(before, lstatSync18(file, { bigint: true })), "INPUT_CHANGED");
|
|
15817
15885
|
const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
15818
|
-
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256:
|
|
15886
|
+
return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
|
|
15819
15887
|
} finally {
|
|
15820
15888
|
closeSync9(fd);
|
|
15821
15889
|
}
|
|
@@ -15826,7 +15894,7 @@ function unchanged2(before) {
|
|
|
15826
15894
|
}
|
|
15827
15895
|
|
|
15828
15896
|
// src/lib/agent-integration.ts
|
|
15829
|
-
var sha3 = (data) =>
|
|
15897
|
+
var sha3 = (data) => createHash17("sha256").update(data).digest("hex");
|
|
15830
15898
|
var ROOTS = NATIVE_SKILL_ROOTS;
|
|
15831
15899
|
function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
|
|
15832
15900
|
try {
|
|
@@ -16020,7 +16088,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
|
|
|
16020
16088
|
}
|
|
16021
16089
|
}
|
|
16022
16090
|
function treeHash(root) {
|
|
16023
|
-
const hash2 =
|
|
16091
|
+
const hash2 = createHash17("sha256");
|
|
16024
16092
|
let bytes = 0, files = 0;
|
|
16025
16093
|
function visit2(path) {
|
|
16026
16094
|
const stat = lstatSync19(path);
|
|
@@ -16514,7 +16582,7 @@ function planAgentIntegration(options) {
|
|
|
16514
16582
|
const pluginPath = join21(sumiConfigDirectory(home), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
|
|
16515
16583
|
const pluginAfter = renderSumiPlugin(command, profileId2);
|
|
16516
16584
|
const priorCommand = policy.bridge?.commands?.sumi, priorProfile = policy.bridge?.profiles?.sumi ?? policy.profileId;
|
|
16517
|
-
if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && pluginBefore
|
|
16585
|
+
if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && isManagedSumiPlugin(pluginBefore, priorCommand, priorProfile)))
|
|
16518
16586
|
throw new Error("Refusing to overwrite a modified Sumi Skills plugin; preserve and review it first");
|
|
16519
16587
|
if (pluginBefore !== pluginAfter)
|
|
16520
16588
|
changes.push({ path: pluginPath, before: pluginBefore, after: pluginAfter });
|
|
@@ -16868,7 +16936,7 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
16868
16936
|
const binding = policy.bridge?.discovery?.[options.agent];
|
|
16869
16937
|
if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
|
|
16870
16938
|
throw new Error("Settings witness upgrade requires an existing reviewed native binding");
|
|
16871
|
-
const configPath = canonicalAgentPath(join21(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
|
|
16939
|
+
const configPath = canonicalAgentPath(options.agent === "sumi" ? sumiConfigPath(home) : join21(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
|
|
16872
16940
|
const settings = readOptional(configPath);
|
|
16873
16941
|
if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
|
|
16874
16942
|
throw new Error("Native settings preimage changed");
|
|
@@ -16894,7 +16962,19 @@ function planAgentSettingsWitnessUpgrade(options) {
|
|
|
16894
16962
|
replaced.push(source);
|
|
16895
16963
|
}
|
|
16896
16964
|
}
|
|
16897
|
-
|
|
16965
|
+
if (options.agent === "sumi") {
|
|
16966
|
+
const preimage = readSumiSettingsPreimage(options.reviewedPreimage);
|
|
16967
|
+
for (const source of binding.sources) {
|
|
16968
|
+
if (source === previous || source.path !== configPath)
|
|
16969
|
+
continue;
|
|
16970
|
+
if (source.hashMode !== undefined || source.format !== "json" || !isDeepStrictEqual6(source.fields, [...SUMI_DISCOVERY_PROJECTION_FIELDS]) || typeof source.sha256 !== "string")
|
|
16971
|
+
throw new Error("Sumi settings witness upgrade found an unrecognized configuration witness");
|
|
16972
|
+
if (projectNativeDiscoveryFields(preimage, "json", source.fields, configPath) !== source.sha256)
|
|
16973
|
+
throw new Error("Reviewed preimage does not explain the Sumi discovery projection witness");
|
|
16974
|
+
replaced.push(source);
|
|
16975
|
+
}
|
|
16976
|
+
}
|
|
16977
|
+
const next = options.agent === "sumi" ? upgradeSumiSettingsWitness(previous, options.reviewedPreimage) : options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : options.targetCodexVersion === 4 ? upgradeCodexSettingsWitnessV4(previous, options.reviewedPreimage) : options.targetCodexVersion === 3 ? upgradeCodexSettingsWitnessV3(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
|
|
16898
16978
|
const rebound = [];
|
|
16899
16979
|
for (const source of binding.sources) {
|
|
16900
16980
|
if (!replaced.includes(source)) {
|
|
@@ -17255,7 +17335,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
|
|
|
17255
17335
|
throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
|
|
17256
17336
|
assertHermesProtection(home, config, command, profile, supervisor);
|
|
17257
17337
|
} else if (agent === "sumi") {
|
|
17258
|
-
if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || readOptional(join21(sumiConfigDirectory(home), "plugins", "skills-cli.js"))
|
|
17338
|
+
if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(join21(sumiConfigDirectory(home), "plugins", "skills-cli.js")), command, profile))
|
|
17259
17339
|
throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
|
|
17260
17340
|
} else if (agent === "opencode") {
|
|
17261
17341
|
if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join21(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
|
|
@@ -18614,7 +18694,7 @@ function exactProfileSelection(spec, profile) {
|
|
|
18614
18694
|
}
|
|
18615
18695
|
|
|
18616
18696
|
// src/lib/plugin-projection.ts
|
|
18617
|
-
import { createHash as
|
|
18697
|
+
import { createHash as createHash18 } from "crypto";
|
|
18618
18698
|
import { posix } from "path";
|
|
18619
18699
|
var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
|
|
18620
18700
|
var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
|
|
@@ -18625,7 +18705,7 @@ function pluginNeed(value, message) {
|
|
|
18625
18705
|
if (!value)
|
|
18626
18706
|
pluginRefusal(message);
|
|
18627
18707
|
}
|
|
18628
|
-
var pluginHash = (bytes) =>
|
|
18708
|
+
var pluginHash = (bytes) => createHash18("sha256").update(bytes).digest("hex");
|
|
18629
18709
|
function pluginObject(value) {
|
|
18630
18710
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
18631
18711
|
}
|
|
@@ -18813,7 +18893,7 @@ function buildPluginProjection(entries) {
|
|
|
18813
18893
|
}
|
|
18814
18894
|
|
|
18815
18895
|
// src/lib/plugin-projection-store.ts
|
|
18816
|
-
import { createHash as
|
|
18896
|
+
import { createHash as createHash19, randomUUID as randomUUID4 } from "crypto";
|
|
18817
18897
|
import { constants as constants11, closeSync as closeSync11, fchmodSync, fstatSync as fstatSync11, fsyncSync as fsyncSync5, linkSync as linkSync3, lstatSync as lstatSync21, mkdirSync as mkdirSync9, openSync as openSync11, opendirSync as opendirSync4, readSync as readSync9, renameSync as renameSync5, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync5, writeSync as writeSync2 } from "fs";
|
|
18818
18898
|
import { dirname as dirname16, isAbsolute as isAbsolute14, join as join28, parse as parse5, resolve as resolve14 } from "path";
|
|
18819
18899
|
var READ_FLAGS = constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK;
|
|
@@ -19155,7 +19235,7 @@ function pluginExecutableDigest(path) {
|
|
|
19155
19235
|
const opened = fstatSync11(fd, { bigint: true });
|
|
19156
19236
|
same3(before, opened);
|
|
19157
19237
|
fileShape(opened, maximum, "executable");
|
|
19158
|
-
const hash2 =
|
|
19238
|
+
const hash2 = createHash19("sha256"), buffer = new Uint8Array(64 * 1024);
|
|
19159
19239
|
let remaining = Number(opened.size);
|
|
19160
19240
|
while (remaining > 0) {
|
|
19161
19241
|
const count = readSync9(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
|
|
@@ -19714,7 +19794,7 @@ function captureManagedPluginRegistry(path, managedPlugins) {
|
|
|
19714
19794
|
}
|
|
19715
19795
|
|
|
19716
19796
|
// src/lib/claude-marketplace-registry.ts
|
|
19717
|
-
import { createHash as
|
|
19797
|
+
import { createHash as createHash20 } from "crypto";
|
|
19718
19798
|
import { closeSync as closeSync13, constants as constants13, fstatSync as fstatSync13, lstatSync as lstatSync23, openSync as openSync13, readSync as readSync11 } from "fs";
|
|
19719
19799
|
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute17, resolve as resolve17 } from "path";
|
|
19720
19800
|
var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
@@ -19736,13 +19816,13 @@ function parents2(path) {
|
|
|
19736
19816
|
return result;
|
|
19737
19817
|
}
|
|
19738
19818
|
}
|
|
19739
|
-
function readRegistry2(path,
|
|
19819
|
+
function readRegistry2(path, budget2) {
|
|
19740
19820
|
absolutePath3(path);
|
|
19741
19821
|
need6(basename9(path) === "known_marketplaces.json", "requires known_marketplaces.json");
|
|
19742
|
-
need6(Number.isSafeInteger(
|
|
19822
|
+
need6(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
|
|
19743
19823
|
const ancestors2 = parents2(path), initial = lstatSync23(path, { bigint: true });
|
|
19744
19824
|
need6(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
|
|
19745
|
-
need6(initial.size <= BigInt(
|
|
19825
|
+
need6(initial.size <= BigInt(budget2.remaining), "exceeds its aggregate byte limit");
|
|
19746
19826
|
const fd = openSync13(path, constants13.O_RDONLY | constants13.O_NOFOLLOW | constants13.O_NONBLOCK);
|
|
19747
19827
|
try {
|
|
19748
19828
|
const opened = fstatSync13(fd, { bigint: true });
|
|
@@ -19761,7 +19841,7 @@ function readRegistry2(path, budget) {
|
|
|
19761
19841
|
const now = lstatSync23(ancestor, { bigint: true });
|
|
19762
19842
|
need6(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
|
|
19763
19843
|
}
|
|
19764
|
-
|
|
19844
|
+
budget2.remaining -= length;
|
|
19765
19845
|
try {
|
|
19766
19846
|
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
|
|
19767
19847
|
} catch {
|
|
@@ -19879,23 +19959,23 @@ function normalizeRow(row, version = 1) {
|
|
|
19879
19959
|
need6(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
|
|
19880
19960
|
row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
|
|
19881
19961
|
}
|
|
19882
|
-
function captureClaudeMarketplaceRegistry(path,
|
|
19883
|
-
const value = parse6(readRegistry2(path,
|
|
19962
|
+
function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
19963
|
+
const value = parse6(readRegistry2(path, budget2));
|
|
19884
19964
|
for (const [, row] of value.entries)
|
|
19885
19965
|
normalizeRow(row);
|
|
19886
|
-
const sha2562 =
|
|
19966
|
+
const sha2562 = createHash20("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
|
|
19887
19967
|
return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
|
|
19888
19968
|
}
|
|
19889
|
-
function captureClaudeMarketplaceRegistryV2(path,
|
|
19890
|
-
const value = parse6(readRegistry2(path,
|
|
19969
|
+
function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
|
|
19970
|
+
const value = parse6(readRegistry2(path, budget2));
|
|
19891
19971
|
for (const [, row] of value.entries)
|
|
19892
19972
|
normalizeRow(row, 2);
|
|
19893
|
-
const sha2562 =
|
|
19973
|
+
const sha2562 = createHash20("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
|
|
19894
19974
|
return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
|
|
19895
19975
|
}
|
|
19896
19976
|
|
|
19897
19977
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
19898
|
-
import { createHash as
|
|
19978
|
+
import { createHash as createHash21 } from "crypto";
|
|
19899
19979
|
|
|
19900
19980
|
// src/lib/codex-hook-additions-review.ts
|
|
19901
19981
|
var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
|
|
@@ -19903,7 +19983,7 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
|
|
|
19903
19983
|
var proofs = new WeakMap;
|
|
19904
19984
|
|
|
19905
19985
|
// src/lib/codex-hook-discovery-recovery.ts
|
|
19906
|
-
var sha4 = (text2) =>
|
|
19986
|
+
var sha4 = (text2) => createHash21("sha256").update(text2).digest("hex");
|
|
19907
19987
|
var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
|
|
19908
19988
|
var proofs2 = new WeakMap;
|
|
19909
19989
|
function assertCodexHookDiscoveryRecovery(binding, witness) {
|
|
@@ -19922,7 +20002,7 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
|
|
|
19922
20002
|
}
|
|
19923
20003
|
|
|
19924
20004
|
// src/lib/agent-discovery.ts
|
|
19925
|
-
var digest = (text2) =>
|
|
20005
|
+
var digest = (text2) => createHash22("sha256").update(text2).digest("hex");
|
|
19926
20006
|
function parseConfig(text2, path, toml = false) {
|
|
19927
20007
|
try {
|
|
19928
20008
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -19960,33 +20040,45 @@ function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
|
19960
20040
|
throw new Error("Expected native discovery configuration object");
|
|
19961
20041
|
return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object6[field2] ?? null]))));
|
|
19962
20042
|
}
|
|
19963
|
-
function
|
|
20043
|
+
function isSupersededSettingsProjection(agent, configPath, source) {
|
|
20044
|
+
if (agent === "sumi")
|
|
20045
|
+
return source.path === configPath && source.format === "json" && isDeepStrictEqual7(source.fields, [...SUMI_DISCOVERY_PROJECTION_FIELDS]);
|
|
19964
20046
|
return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual7(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
|
|
19965
20047
|
}
|
|
19966
|
-
function
|
|
19967
|
-
|
|
20048
|
+
function supersedesSettingsProjection(agent, sources, configPath) {
|
|
20049
|
+
if (agent === "sumi")
|
|
20050
|
+
return sources.some((source) => source.path === configPath && source.hashMode === "sumi-settings-v1");
|
|
20051
|
+
return agent === "codex" && sources.some((source) => source.path === configPath && (source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")));
|
|
19968
20052
|
}
|
|
19969
|
-
function projected(source, changes,
|
|
20053
|
+
function projected(source, changes, budget2 = discoveryByteBudget()) {
|
|
20054
|
+
if (source.hashMode === "sumi-settings-v1") {
|
|
20055
|
+
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
20056
|
+
throw new Error("Sumi settings witnesses require exact typed metadata");
|
|
20057
|
+
const current = captureSumiSettings(source.path, budget2).sha256;
|
|
20058
|
+
if (current !== source.sha256)
|
|
20059
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
20060
|
+
return changes?.has(source.path) ? hashSumiSettingsReplacement(changes.get(source.path), budget2) : current;
|
|
20061
|
+
}
|
|
19970
20062
|
if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) {
|
|
19971
20063
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
19972
20064
|
throw new Error("Codex settings witnesses require exact typed metadata");
|
|
19973
|
-
const current = (source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path,
|
|
20065
|
+
const current = (source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget2).sha256;
|
|
19974
20066
|
if (current !== source.sha256)
|
|
19975
20067
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
19976
|
-
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path),
|
|
20068
|
+
return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
19977
20069
|
}
|
|
19978
20070
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
19979
20071
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
|
|
19980
20072
|
throw new Error("Claude settings witnesses require exact typed metadata");
|
|
19981
|
-
const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path,
|
|
20073
|
+
const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
|
|
19982
20074
|
if (current !== source.sha256)
|
|
19983
20075
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
19984
|
-
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path),
|
|
20076
|
+
return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
|
|
19985
20077
|
}
|
|
19986
20078
|
if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") {
|
|
19987
20079
|
if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
|
|
19988
20080
|
throw new Error("Claude marketplace witnesses require an exact reviewed registry without synthetic changes");
|
|
19989
|
-
const sha2562 = (source.hashMode === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : captureClaudeMarketplaceRegistry)(source.path,
|
|
20081
|
+
const sha2562 = (source.hashMode === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : captureClaudeMarketplaceRegistry)(source.path, budget2).sha256;
|
|
19990
20082
|
if (sha2562 !== source.sha256)
|
|
19991
20083
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
19992
20084
|
return sha2562;
|
|
@@ -20004,8 +20096,8 @@ function projected(source, changes, budget = discoveryByteBudget()) {
|
|
|
20004
20096
|
if (source.format !== undefined || source.fields !== undefined)
|
|
20005
20097
|
throw new Error("Raw discovery witnesses cannot project configuration fields");
|
|
20006
20098
|
if (source.hashMode === "path-bytes")
|
|
20007
|
-
return hashDiscoveryPathFile(source.path,
|
|
20008
|
-
return hashRawDiscoveryFile(source.path,
|
|
20099
|
+
return hashDiscoveryPathFile(source.path, budget2, changes);
|
|
20100
|
+
return hashRawDiscoveryFile(source.path, budget2, changes);
|
|
20009
20101
|
}
|
|
20010
20102
|
const text2 = read2(source.path, changes);
|
|
20011
20103
|
if (text2 === null)
|
|
@@ -20153,7 +20245,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
20153
20245
|
verifyDiscoveryDirectories(projected2, installationRoots);
|
|
20154
20246
|
}
|
|
20155
20247
|
const absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root2) => !lstatSync24(root2, { throwIfNoEntry: false }))];
|
|
20156
|
-
const
|
|
20248
|
+
const budget2 = discoveryByteBudget();
|
|
20157
20249
|
for (const source of binding.sources) {
|
|
20158
20250
|
assertMarketplaceBinding(binding, source);
|
|
20159
20251
|
if (source.hashMode === "claude-plugin-registry" && binding.agent !== "claude")
|
|
@@ -20170,7 +20262,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
|
|
|
20170
20262
|
safe4(source.path);
|
|
20171
20263
|
continue;
|
|
20172
20264
|
}
|
|
20173
|
-
const current = projected(source, undefined,
|
|
20265
|
+
const current = projected(source, undefined, budget2);
|
|
20174
20266
|
if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
|
|
20175
20267
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
20176
20268
|
}
|
|
@@ -20204,6 +20296,8 @@ function codexInstallationRoots(binding) {
|
|
|
20204
20296
|
});
|
|
20205
20297
|
}
|
|
20206
20298
|
function assertMarketplaceBinding(binding, source) {
|
|
20299
|
+
if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename10(source.path) !== "sumi.json"))
|
|
20300
|
+
throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
|
|
20207
20301
|
if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
|
|
20208
20302
|
throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
|
|
20209
20303
|
if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
|
|
@@ -20212,13 +20306,13 @@ function assertMarketplaceBinding(binding, source) {
|
|
|
20212
20306
|
throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
|
|
20213
20307
|
}
|
|
20214
20308
|
function rebindAgentDiscovery(binding, changes) {
|
|
20215
|
-
const
|
|
20309
|
+
const budget2 = discoveryByteBudget();
|
|
20216
20310
|
const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync24(root2, { throwIfNoEntry: false }))];
|
|
20217
20311
|
const rebound = { ...binding, sources: binding.sources.map((source) => {
|
|
20218
20312
|
assertMarketplaceBinding(binding, source);
|
|
20219
20313
|
if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep7)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep7)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)))
|
|
20220
20314
|
return source;
|
|
20221
|
-
const sha2562 = projected(source, changes,
|
|
20315
|
+
const sha2562 = projected(source, changes, budget2);
|
|
20222
20316
|
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
20223
20317
|
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
20224
20318
|
return { ...source, sha256: sha2562 };
|
|
@@ -20233,14 +20327,14 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
20233
20327
|
function captureDiscoveryByteSources(paths) {
|
|
20234
20328
|
if (!Array.isArray(paths) || paths.length > AGENT_POLICY_LIMITS.discoverySources || new Set(paths).size !== paths.length)
|
|
20235
20329
|
throw new Error("Invalid raw discovery source collection");
|
|
20236
|
-
const
|
|
20237
|
-
return paths.map((path) => ({ path, hashMode: "bytes", sha256: hashRawDiscoveryFile(path,
|
|
20330
|
+
const budget2 = discoveryByteBudget();
|
|
20331
|
+
return paths.map((path) => ({ path, hashMode: "bytes", sha256: hashRawDiscoveryFile(path, budget2) }));
|
|
20238
20332
|
}
|
|
20239
20333
|
function captureDiscoveryPathSources(paths) {
|
|
20240
20334
|
if (!Array.isArray(paths) || paths.length > AGENT_POLICY_LIMITS.discoverySources || new Set(paths).size !== paths.length)
|
|
20241
20335
|
throw new Error("Invalid path discovery source collection");
|
|
20242
|
-
const
|
|
20243
|
-
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path,
|
|
20336
|
+
const budget2 = discoveryByteBudget();
|
|
20337
|
+
return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget2) }));
|
|
20244
20338
|
}
|
|
20245
20339
|
function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18, reviewed) {
|
|
20246
20340
|
const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
|
|
@@ -20308,7 +20402,7 @@ function resolveAgentDiscovery(options) {
|
|
|
20308
20402
|
if (agent === "hermes")
|
|
20309
20403
|
assertHermesEnvironment(home);
|
|
20310
20404
|
const configPath = agentDiscoveryConfigPath(home, agent);
|
|
20311
|
-
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? [
|
|
20405
|
+
const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? [...SUMI_DISCOVERY_PROJECTION_FIELDS] : ["version"]);
|
|
20312
20406
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
20313
20407
|
const unresolved = (detail) => {
|
|
20314
20408
|
throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
@@ -20405,9 +20499,9 @@ function resolveAgentDiscovery(options) {
|
|
|
20405
20499
|
verifyAgentDiscovery(retained);
|
|
20406
20500
|
if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
|
|
20407
20501
|
unresolved("retained review is missing the full agent configuration source");
|
|
20408
|
-
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
|
|
20502
|
+
if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
|
|
20409
20503
|
unresolved("retained settings witness names another configuration source");
|
|
20410
|
-
const covered =
|
|
20504
|
+
const covered = supersedesSettingsProjection(agent, retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical6(configPath), source)) : sources;
|
|
20411
20505
|
if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual7(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
20412
20506
|
unresolved("retained review is missing current runtime discovery coverage");
|
|
20413
20507
|
return retained;
|
|
@@ -20422,11 +20516,11 @@ function resolveAgentDiscovery(options) {
|
|
|
20422
20516
|
if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
|
|
20423
20517
|
throw new Error("Explicit discovery reviews require full source-file hashes");
|
|
20424
20518
|
verifyAgentDiscovery(supplied);
|
|
20425
|
-
if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
|
|
20426
|
-
throw new Error("Semantic settings witnesses must name the configured
|
|
20519
|
+
if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
|
|
20520
|
+
throw new Error("Semantic settings witnesses must name the configured agent configuration source");
|
|
20427
20521
|
if (!review.sources.some((source) => source.path === canonical6(configPath)))
|
|
20428
20522
|
throw new Error("Discovery review must include the agent configuration source");
|
|
20429
|
-
const reviewedSources =
|
|
20523
|
+
const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical6(configPath), source)) : sources;
|
|
20430
20524
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...reviewedSources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
|
|
20431
20525
|
}
|
|
20432
20526
|
function plugin(root2) {
|
|
@@ -21388,7 +21482,7 @@ function getAllTags(catalog) {
|
|
|
21388
21482
|
return Array.from(tagSet).sort();
|
|
21389
21483
|
}
|
|
21390
21484
|
// src/lib/run-state.ts
|
|
21391
|
-
import { createHash as
|
|
21485
|
+
import { createHash as createHash23, randomBytes } from "crypto";
|
|
21392
21486
|
import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync21, readdirSync as readdirSync13, statSync as statSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
21393
21487
|
import { extname, join as join35, relative as relative8 } from "path";
|
|
21394
21488
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
@@ -21515,7 +21609,7 @@ function collectRunArtifacts(context) {
|
|
|
21515
21609
|
artifacts.push({
|
|
21516
21610
|
path: toProjectRelative(context.targetDir, path),
|
|
21517
21611
|
mime: mimeForPath(path),
|
|
21518
|
-
sha256:
|
|
21612
|
+
sha256: createHash23("sha256").update(bytes).digest("hex"),
|
|
21519
21613
|
sizeBytes: stat2.size
|
|
21520
21614
|
});
|
|
21521
21615
|
}
|
|
@@ -40934,7 +41028,7 @@ function clone2(value) {
|
|
|
40934
41028
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
40935
41029
|
}
|
|
40936
41030
|
// src/lib/canonical-json.ts
|
|
40937
|
-
import { createHash as
|
|
41031
|
+
import { createHash as createHash24 } from "crypto";
|
|
40938
41032
|
function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
|
|
40939
41033
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
40940
41034
|
}
|
|
@@ -40978,7 +41072,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
40978
41072
|
return visit3(value, 0);
|
|
40979
41073
|
}
|
|
40980
41074
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
40981
|
-
return
|
|
41075
|
+
return createHash24("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
|
|
40982
41076
|
}
|
|
40983
41077
|
|
|
40984
41078
|
// src/lib/remote-recurring-contract.ts
|
|
@@ -41165,7 +41259,7 @@ function recurringRequest(action, input2) {
|
|
|
41165
41259
|
}
|
|
41166
41260
|
return { path, read: read3, method: read3 ? "GET" : "POST", body: read3 ? undefined : JSON.stringify(action === "preview" ? input2 : action === "activate" ? row.approval : { contractVersion: 1 }) };
|
|
41167
41261
|
}
|
|
41168
|
-
var
|
|
41262
|
+
var budget2 = exports_external.object({ reservedCredits: integer2, settledCredits: integer2, admittedOccurrences: integer2 });
|
|
41169
41263
|
var period = exports_external.object({ startsAt: instant2, endsAt: instant2 });
|
|
41170
41264
|
var preview = exports_external.object({
|
|
41171
41265
|
contractVersion: exports_external.literal(1),
|
|
@@ -41187,8 +41281,8 @@ var consent = exports_external.object({
|
|
|
41187
41281
|
state: exports_external.enum(["active", "revoked", "expired"]),
|
|
41188
41282
|
activatedAt: instant2,
|
|
41189
41283
|
revokedAt: instant2.nullable(),
|
|
41190
|
-
total:
|
|
41191
|
-
currentPeriod:
|
|
41284
|
+
total: budget2,
|
|
41285
|
+
currentPeriod: budget2.extend(period.shape).nullable(),
|
|
41192
41286
|
nextDueAt: instant2.nullable()
|
|
41193
41287
|
});
|
|
41194
41288
|
var refusal2 = exports_external.enum([
|
|
@@ -43245,7 +43339,7 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
43245
43339
|
saveSchedules(data, targetDir);
|
|
43246
43340
|
}
|
|
43247
43341
|
// src/lib/revision.ts
|
|
43248
|
-
import { createHash as
|
|
43342
|
+
import { createHash as createHash25 } from "crypto";
|
|
43249
43343
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
43250
43344
|
function revisionIdOf(content) {
|
|
43251
43345
|
const canonicalContent = {
|
|
@@ -43267,7 +43361,7 @@ function revisionIdOf(content) {
|
|
|
43267
43361
|
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
43268
43362
|
}
|
|
43269
43363
|
const canonical6 = JSON.stringify(canonicalContent);
|
|
43270
|
-
return
|
|
43364
|
+
return createHash25("sha256").update(canonical6).digest("hex");
|
|
43271
43365
|
}
|
|
43272
43366
|
function revisionIdOfRecord(record8) {
|
|
43273
43367
|
return revisionIdOf(record8);
|
|
@@ -43808,7 +43902,7 @@ async function requestInvitationEmail(origin, action, input2) {
|
|
|
43808
43902
|
}
|
|
43809
43903
|
|
|
43810
43904
|
// src/lib/remote-private-publications.ts
|
|
43811
|
-
import { createHash as
|
|
43905
|
+
import { createHash as createHash26 } from "crypto";
|
|
43812
43906
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
43813
43907
|
var failures2 = {
|
|
43814
43908
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -43855,7 +43949,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
|
43855
43949
|
var record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
43856
43950
|
var exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
|
|
43857
43951
|
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));
|
|
43858
|
-
var publicationSha256 = (bytes) =>
|
|
43952
|
+
var publicationSha256 = (bytes) => createHash26("sha256").update(bytes).digest("hex");
|
|
43859
43953
|
function checkedPublicationDeclaration(value) {
|
|
43860
43954
|
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)
|
|
43861
43955
|
return bad();
|
|
@@ -43875,7 +43969,7 @@ function checkedPublicationView(value, skillId, intentId, declaration) {
|
|
|
43875
43969
|
return invalid4();
|
|
43876
43970
|
return Object.freeze({ ...value });
|
|
43877
43971
|
}
|
|
43878
|
-
async function boundedJson(url2, init, token, mutation,
|
|
43972
|
+
async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal) {
|
|
43879
43973
|
const controller = new AbortController;
|
|
43880
43974
|
let reader;
|
|
43881
43975
|
let response;
|
|
@@ -43930,7 +44024,7 @@ async function boundedJson(url2, init, token, mutation, budget2 = 15000, signal)
|
|
|
43930
44024
|
controller.abort();
|
|
43931
44025
|
reject(timeout);
|
|
43932
44026
|
};
|
|
43933
|
-
timer = setTimeout(abort, Math.max(1, Math.min(15000,
|
|
44027
|
+
timer = setTimeout(abort, Math.max(1, Math.min(15000, budget3)));
|
|
43934
44028
|
signal?.addEventListener("abort", abort, { once: true });
|
|
43935
44029
|
})]);
|
|
43936
44030
|
} catch (error61) {
|
|
@@ -45721,7 +45815,7 @@ function isApiMode(env = process.env) {
|
|
|
45721
45815
|
}
|
|
45722
45816
|
}
|
|
45723
45817
|
// src/lib/native-storage.ts
|
|
45724
|
-
import { createHash as
|
|
45818
|
+
import { createHash as createHash27, createHmac as createHmac2 } from "crypto";
|
|
45725
45819
|
import {
|
|
45726
45820
|
existsSync as existsSync20,
|
|
45727
45821
|
mkdirSync as mkdirSync16,
|
|
@@ -45897,7 +45991,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
45897
45991
|
files.push({
|
|
45898
45992
|
path: relativePath,
|
|
45899
45993
|
sizeBytes: bytes.byteLength,
|
|
45900
|
-
sha256:
|
|
45994
|
+
sha256: createHash27("sha256").update(bytes).digest("hex"),
|
|
45901
45995
|
...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
|
|
45902
45996
|
});
|
|
45903
45997
|
}
|
|
@@ -45922,7 +46016,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
|
|
|
45922
46016
|
continue;
|
|
45923
46017
|
}
|
|
45924
46018
|
const bytes = Buffer.from(file2.contentBase64, "base64");
|
|
45925
|
-
const hash4 =
|
|
46019
|
+
const hash4 = createHash27("sha256").update(bytes).digest("hex");
|
|
45926
46020
|
if (hash4 !== file2.sha256) {
|
|
45927
46021
|
throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
|
|
45928
46022
|
}
|
|
@@ -46254,7 +46348,7 @@ function toIsoString(value) {
|
|
|
46254
46348
|
return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
|
|
46255
46349
|
}
|
|
46256
46350
|
function sha256Hex2(value) {
|
|
46257
|
-
return
|
|
46351
|
+
return createHash27("sha256").update(value).digest("hex");
|
|
46258
46352
|
}
|
|
46259
46353
|
function normalizeHeaders(headers) {
|
|
46260
46354
|
const result = {};
|
|
@@ -46306,7 +46400,7 @@ function toArrayBuffer(bytes) {
|
|
|
46306
46400
|
return buffer;
|
|
46307
46401
|
}
|
|
46308
46402
|
// src/lib/station-snapshot.ts
|
|
46309
|
-
import { createHash as
|
|
46403
|
+
import { createHash as createHash28 } from "crypto";
|
|
46310
46404
|
import {
|
|
46311
46405
|
copyFileSync,
|
|
46312
46406
|
mkdirSync as mkdirSync17,
|
|
@@ -46481,7 +46575,7 @@ function validateStationId(stationId) {
|
|
|
46481
46575
|
}
|
|
46482
46576
|
}
|
|
46483
46577
|
function sha256File(filePath) {
|
|
46484
|
-
return
|
|
46578
|
+
return createHash28("sha256").update(readFileSync27(filePath)).digest("hex");
|
|
46485
46579
|
}
|
|
46486
46580
|
function scanHome(definition, homesRoot) {
|
|
46487
46581
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -46632,7 +46726,7 @@ function writeStationSnapshot(options) {
|
|
|
46632
46726
|
};
|
|
46633
46727
|
}
|
|
46634
46728
|
// src/lib/station-hydrate.ts
|
|
46635
|
-
import { createHash as
|
|
46729
|
+
import { createHash as createHash29 } from "crypto";
|
|
46636
46730
|
import {
|
|
46637
46731
|
copyFileSync as copyFileSync2,
|
|
46638
46732
|
mkdirSync as mkdirSync18,
|
|
@@ -46838,7 +46932,7 @@ function skillSha256(skill) {
|
|
|
46838
46932
|
return sha256File(skill.files[0].winner.fullPath);
|
|
46839
46933
|
}
|
|
46840
46934
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
46841
|
-
return
|
|
46935
|
+
return createHash29("sha256").update(joined.sort().join(`
|
|
46842
46936
|
`)).digest("hex");
|
|
46843
46937
|
}
|
|
46844
46938
|
function writeStationHydration(options) {
|
|
@@ -47166,7 +47260,7 @@ async function inspectLocked(client2, directory2, cancel) {
|
|
|
47166
47260
|
return privatePublicationResult(directory2, receipt);
|
|
47167
47261
|
}
|
|
47168
47262
|
// src/lib/skill-context.ts
|
|
47169
|
-
import { createHash as
|
|
47263
|
+
import { createHash as createHash30 } from "crypto";
|
|
47170
47264
|
import { resolve as resolve24 } from "path";
|
|
47171
47265
|
function words(value) {
|
|
47172
47266
|
return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
|
|
@@ -47259,7 +47353,7 @@ ${content}`;
|
|
|
47259
47353
|
`);
|
|
47260
47354
|
const receipt = {
|
|
47261
47355
|
schemaVersion: 1,
|
|
47262
|
-
id:
|
|
47356
|
+
id: createHash30("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
|
|
47263
47357
|
profileId: profile.profileId,
|
|
47264
47358
|
profileRevision: profile.profileRevision,
|
|
47265
47359
|
authority: profile.authority,
|
|
@@ -47275,9 +47369,9 @@ ${content}`;
|
|
|
47275
47369
|
return { context: context2, selections, omitted, receipt };
|
|
47276
47370
|
}
|
|
47277
47371
|
// src/lib/session-reconciliation.ts
|
|
47278
|
-
import { createHash as
|
|
47372
|
+
import { createHash as createHash31 } from "crypto";
|
|
47279
47373
|
var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
|
|
47280
|
-
var digest3 = (value) =>
|
|
47374
|
+
var digest3 = (value) => createHash31("sha256").update(JSON.stringify(value)).digest("hex");
|
|
47281
47375
|
function exactTimestamp(value) {
|
|
47282
47376
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
47283
47377
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -47391,6 +47485,7 @@ export {
|
|
|
47391
47485
|
validateCron,
|
|
47392
47486
|
validateBlogArticleRunOptions,
|
|
47393
47487
|
uploadSkillsSnapshotFilesToS3,
|
|
47488
|
+
upgradeSumiSettingsWitness,
|
|
47394
47489
|
upgradeCodexSettingsWitnessV4,
|
|
47395
47490
|
upgradeCodexSettingsWitnessV3,
|
|
47396
47491
|
upgradeCodexSettingsWitness,
|
|
@@ -47575,6 +47670,7 @@ export {
|
|
|
47575
47670
|
computeContentHash,
|
|
47576
47671
|
completeSkillRun,
|
|
47577
47672
|
clearRegistryCache,
|
|
47673
|
+
captureSumiSettings,
|
|
47578
47674
|
captureManagedPluginRegistry,
|
|
47579
47675
|
captureDiscoveryPathSources,
|
|
47580
47676
|
captureDiscoveryDirectories,
|