@cybedefend/vibedefend 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -4
- package/dist/clients/claude-code.js +6 -8
- package/dist/clients/claude-code.js.map +1 -1
- package/dist/clients/codex.js +6 -1
- package/dist/clients/codex.js.map +1 -1
- package/dist/clients/detect.js +5 -1
- package/dist/clients/detect.js.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/diagnostics.js +5 -0
- package/dist/diagnostics.js.map +1 -1
- package/dist/doctor.js +13 -0
- package/dist/doctor.js.map +1 -1
- package/dist/hook-runner.js +537 -101
- package/dist/hooks/install.js +54 -9
- package/dist/hooks/install.js.map +1 -1
- package/dist/hooks/runtime/guard-violations-buffer.js +65 -15
- package/dist/hooks/runtime/guard-violations-buffer.js.map +1 -1
- package/dist/hooks/runtime/self-update-check.js +196 -0
- package/dist/hooks/runtime/self-update-check.js.map +1 -0
- package/dist/hooks/runtime/semver.js +34 -0
- package/dist/hooks/runtime/semver.js.map +1 -0
- package/dist/hooks/runtime/session-review.js +126 -40
- package/dist/hooks/runtime/session-review.js.map +1 -1
- package/dist/hooks/runtime/session-start.js +24 -0
- package/dist/hooks/runtime/session-start.js.map +1 -1
- package/dist/hooks/shim-entry.js +10 -0
- package/dist/hooks/shim-entry.js.map +1 -0
- package/dist/hooks/shim.js +66 -0
- package/dist/hooks/shim.js.map +1 -0
- package/dist/login.js +25 -5
- package/dist/login.js.map +1 -1
- package/dist/prompts.js +14 -2
- package/dist/prompts.js.map +1 -1
- package/dist/self-update.js +46 -21
- package/dist/self-update.js.map +1 -1
- package/dist/shim.js +40 -0
- package/dist/utils.js +5 -1
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/hook-runner.js
CHANGED
|
@@ -3654,10 +3654,10 @@ var require_re2 = __commonJS({
|
|
|
3654
3654
|
} });
|
|
3655
3655
|
var calledRun;
|
|
3656
3656
|
dependenciesFulfilled = function runCaller() {
|
|
3657
|
-
if (!calledRun)
|
|
3657
|
+
if (!calledRun) run3();
|
|
3658
3658
|
if (!calledRun) dependenciesFulfilled = runCaller;
|
|
3659
3659
|
};
|
|
3660
|
-
function
|
|
3660
|
+
function run3(args) {
|
|
3661
3661
|
args = args || arguments_;
|
|
3662
3662
|
if (runDependencies > 0) {
|
|
3663
3663
|
return;
|
|
@@ -3690,7 +3690,7 @@ var require_re2 = __commonJS({
|
|
|
3690
3690
|
}
|
|
3691
3691
|
checkStackCookie();
|
|
3692
3692
|
}
|
|
3693
|
-
Module["run"] =
|
|
3693
|
+
Module["run"] = run3;
|
|
3694
3694
|
if (Module["preInit"]) {
|
|
3695
3695
|
if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]];
|
|
3696
3696
|
while (Module["preInit"].length > 0) {
|
|
@@ -3698,7 +3698,7 @@ var require_re2 = __commonJS({
|
|
|
3698
3698
|
}
|
|
3699
3699
|
}
|
|
3700
3700
|
noExitRuntime = true;
|
|
3701
|
-
|
|
3701
|
+
run3();
|
|
3702
3702
|
}
|
|
3703
3703
|
});
|
|
3704
3704
|
|
|
@@ -5135,6 +5135,323 @@ function composeSecurityBody(markdown, total, filePath) {
|
|
|
5135
5135
|
].join("\n");
|
|
5136
5136
|
}
|
|
5137
5137
|
|
|
5138
|
+
// src/hooks/runtime/self-update-check.ts
|
|
5139
|
+
import { spawn } from "node:child_process";
|
|
5140
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5141
|
+
import { homedir as homedir3 } from "node:os";
|
|
5142
|
+
import { join as join4 } from "node:path";
|
|
5143
|
+
|
|
5144
|
+
// src/utils.ts
|
|
5145
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5146
|
+
import { dirname, join as join3 } from "node:path";
|
|
5147
|
+
|
|
5148
|
+
// node_modules/.pnpm/kleur@4.1.5/node_modules/kleur/index.mjs
|
|
5149
|
+
var FORCE_COLOR;
|
|
5150
|
+
var NODE_DISABLE_COLORS;
|
|
5151
|
+
var NO_COLOR;
|
|
5152
|
+
var TERM;
|
|
5153
|
+
var isTTY = true;
|
|
5154
|
+
if (typeof process !== "undefined") {
|
|
5155
|
+
({ FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env || {});
|
|
5156
|
+
isTTY = process.stdout && process.stdout.isTTY;
|
|
5157
|
+
}
|
|
5158
|
+
var $ = {
|
|
5159
|
+
enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== "dumb" && (FORCE_COLOR != null && FORCE_COLOR !== "0" || isTTY),
|
|
5160
|
+
// modifiers
|
|
5161
|
+
reset: init(0, 0),
|
|
5162
|
+
bold: init(1, 22),
|
|
5163
|
+
dim: init(2, 22),
|
|
5164
|
+
italic: init(3, 23),
|
|
5165
|
+
underline: init(4, 24),
|
|
5166
|
+
inverse: init(7, 27),
|
|
5167
|
+
hidden: init(8, 28),
|
|
5168
|
+
strikethrough: init(9, 29),
|
|
5169
|
+
// colors
|
|
5170
|
+
black: init(30, 39),
|
|
5171
|
+
red: init(31, 39),
|
|
5172
|
+
green: init(32, 39),
|
|
5173
|
+
yellow: init(33, 39),
|
|
5174
|
+
blue: init(34, 39),
|
|
5175
|
+
magenta: init(35, 39),
|
|
5176
|
+
cyan: init(36, 39),
|
|
5177
|
+
white: init(37, 39),
|
|
5178
|
+
gray: init(90, 39),
|
|
5179
|
+
grey: init(90, 39),
|
|
5180
|
+
// background colors
|
|
5181
|
+
bgBlack: init(40, 49),
|
|
5182
|
+
bgRed: init(41, 49),
|
|
5183
|
+
bgGreen: init(42, 49),
|
|
5184
|
+
bgYellow: init(43, 49),
|
|
5185
|
+
bgBlue: init(44, 49),
|
|
5186
|
+
bgMagenta: init(45, 49),
|
|
5187
|
+
bgCyan: init(46, 49),
|
|
5188
|
+
bgWhite: init(47, 49)
|
|
5189
|
+
};
|
|
5190
|
+
function run(arr, str) {
|
|
5191
|
+
let i = 0, tmp, beg = "", end = "";
|
|
5192
|
+
for (; i < arr.length; i++) {
|
|
5193
|
+
tmp = arr[i];
|
|
5194
|
+
beg += tmp.open;
|
|
5195
|
+
end += tmp.close;
|
|
5196
|
+
if (!!~str.indexOf(tmp.close)) {
|
|
5197
|
+
str = str.replace(tmp.rgx, tmp.close + tmp.open);
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
5200
|
+
return beg + str + end;
|
|
5201
|
+
}
|
|
5202
|
+
function chain(has, keys) {
|
|
5203
|
+
let ctx = { has, keys };
|
|
5204
|
+
ctx.reset = $.reset.bind(ctx);
|
|
5205
|
+
ctx.bold = $.bold.bind(ctx);
|
|
5206
|
+
ctx.dim = $.dim.bind(ctx);
|
|
5207
|
+
ctx.italic = $.italic.bind(ctx);
|
|
5208
|
+
ctx.underline = $.underline.bind(ctx);
|
|
5209
|
+
ctx.inverse = $.inverse.bind(ctx);
|
|
5210
|
+
ctx.hidden = $.hidden.bind(ctx);
|
|
5211
|
+
ctx.strikethrough = $.strikethrough.bind(ctx);
|
|
5212
|
+
ctx.black = $.black.bind(ctx);
|
|
5213
|
+
ctx.red = $.red.bind(ctx);
|
|
5214
|
+
ctx.green = $.green.bind(ctx);
|
|
5215
|
+
ctx.yellow = $.yellow.bind(ctx);
|
|
5216
|
+
ctx.blue = $.blue.bind(ctx);
|
|
5217
|
+
ctx.magenta = $.magenta.bind(ctx);
|
|
5218
|
+
ctx.cyan = $.cyan.bind(ctx);
|
|
5219
|
+
ctx.white = $.white.bind(ctx);
|
|
5220
|
+
ctx.gray = $.gray.bind(ctx);
|
|
5221
|
+
ctx.grey = $.grey.bind(ctx);
|
|
5222
|
+
ctx.bgBlack = $.bgBlack.bind(ctx);
|
|
5223
|
+
ctx.bgRed = $.bgRed.bind(ctx);
|
|
5224
|
+
ctx.bgGreen = $.bgGreen.bind(ctx);
|
|
5225
|
+
ctx.bgYellow = $.bgYellow.bind(ctx);
|
|
5226
|
+
ctx.bgBlue = $.bgBlue.bind(ctx);
|
|
5227
|
+
ctx.bgMagenta = $.bgMagenta.bind(ctx);
|
|
5228
|
+
ctx.bgCyan = $.bgCyan.bind(ctx);
|
|
5229
|
+
ctx.bgWhite = $.bgWhite.bind(ctx);
|
|
5230
|
+
return ctx;
|
|
5231
|
+
}
|
|
5232
|
+
function init(open, close) {
|
|
5233
|
+
let blk = {
|
|
5234
|
+
open: `\x1B[${open}m`,
|
|
5235
|
+
close: `\x1B[${close}m`,
|
|
5236
|
+
rgx: new RegExp(`\\x1b\\[${close}m`, "g")
|
|
5237
|
+
};
|
|
5238
|
+
return function(txt) {
|
|
5239
|
+
if (this !== void 0 && this.has !== void 0) {
|
|
5240
|
+
!!~this.has.indexOf(open) || (this.has.push(open), this.keys.push(blk));
|
|
5241
|
+
return txt === void 0 ? this : $.enabled ? run(this.keys, txt + "") : txt + "";
|
|
5242
|
+
}
|
|
5243
|
+
return txt === void 0 ? chain([open], [blk]) : $.enabled ? run([blk], txt + "") : txt + "";
|
|
5244
|
+
};
|
|
5245
|
+
}
|
|
5246
|
+
|
|
5247
|
+
// src/utils.ts
|
|
5248
|
+
function ensureDirFor(filePath) {
|
|
5249
|
+
const dir = dirname(filePath);
|
|
5250
|
+
if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
|
|
5251
|
+
}
|
|
5252
|
+
|
|
5253
|
+
// src/hooks/runtime/semver.ts
|
|
5254
|
+
function parseVersion(input) {
|
|
5255
|
+
const cleaned = input.trim().replace(/^v/i, "");
|
|
5256
|
+
const core = cleaned.split("-")[0].split("+")[0];
|
|
5257
|
+
const parts = core.split(".");
|
|
5258
|
+
const seg = (i) => {
|
|
5259
|
+
const v = Number.parseInt(parts[i] ?? "0", 10);
|
|
5260
|
+
return Number.isFinite(v) && v >= 0 ? v : 0;
|
|
5261
|
+
};
|
|
5262
|
+
return [seg(0), seg(1), seg(2)];
|
|
5263
|
+
}
|
|
5264
|
+
function compareVersions(a, b) {
|
|
5265
|
+
const pa = parseVersion(a);
|
|
5266
|
+
const pb = parseVersion(b);
|
|
5267
|
+
for (let i = 0; i < 3; i++) {
|
|
5268
|
+
if (pa[i] < pb[i]) return -1;
|
|
5269
|
+
if (pa[i] > pb[i]) return 1;
|
|
5270
|
+
}
|
|
5271
|
+
return 0;
|
|
5272
|
+
}
|
|
5273
|
+
function isNewer(latest, current) {
|
|
5274
|
+
return compareVersions(latest, current) > 0;
|
|
5275
|
+
}
|
|
5276
|
+
|
|
5277
|
+
// src/self-update.ts
|
|
5278
|
+
import { fileURLToPath } from "node:url";
|
|
5279
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
5280
|
+
var GLOBAL_MODES = /* @__PURE__ */ new Set([
|
|
5281
|
+
"npm-global",
|
|
5282
|
+
"pnpm-global",
|
|
5283
|
+
"yarn-global"
|
|
5284
|
+
]);
|
|
5285
|
+
function detectInstallMode(scriptPath) {
|
|
5286
|
+
if (/[\\/]_npx[\\/]/i.test(scriptPath)) return "npx-cache";
|
|
5287
|
+
if (/[\\/]pnpm(?:[\\/]global|[\\/]nodejs|[\\/]node_modules)/i.test(scriptPath)) {
|
|
5288
|
+
return "pnpm-global";
|
|
5289
|
+
}
|
|
5290
|
+
if (/[\\/]\.pnpm-global[\\/]/i.test(scriptPath)) return "pnpm-global";
|
|
5291
|
+
if (/[\\/]yarn[\\/](?:[^\\/]+[\\/])?global[\\/]/i.test(scriptPath)) {
|
|
5292
|
+
return "yarn-global";
|
|
5293
|
+
}
|
|
5294
|
+
if (/[\\/]lib[\\/]node_modules[\\/]/i.test(scriptPath)) return "npm-global";
|
|
5295
|
+
if (/[\\/](?:npm|nodejs)[\\/]node_modules[\\/]/i.test(scriptPath)) {
|
|
5296
|
+
return "npm-global";
|
|
5297
|
+
}
|
|
5298
|
+
if (/[\\/]nvm[\\/][^\\/]+[\\/]node_modules[\\/]/i.test(scriptPath)) {
|
|
5299
|
+
return "npm-global";
|
|
5300
|
+
}
|
|
5301
|
+
if (/[\\/]node_modules[\\/]/i.test(scriptPath)) return "project-local";
|
|
5302
|
+
return "unknown";
|
|
5303
|
+
}
|
|
5304
|
+
function resolveScriptPath() {
|
|
5305
|
+
const fromArgv = process.argv[1];
|
|
5306
|
+
if (fromArgv && existsSync5(fromArgv)) return fromArgv;
|
|
5307
|
+
return fileURLToPath(import.meta.url);
|
|
5308
|
+
}
|
|
5309
|
+
|
|
5310
|
+
// src/hooks/runtime/self-update-check.ts
|
|
5311
|
+
var PACKAGE_NAME = "@cybedefend/vibedefend";
|
|
5312
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
5313
|
+
var THROTTLE_MS = 24 * 60 * 60 * 1e3;
|
|
5314
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
5315
|
+
async function selfUpdateCheck(deps) {
|
|
5316
|
+
const now = deps.now ?? (() => Date.now());
|
|
5317
|
+
const dir = deps.cybeDir ?? join4(process.env.CYBEDEFEND_HOME ?? homedir3(), ".cybedefend");
|
|
5318
|
+
const throttlePath = join4(dir, "update-check.json");
|
|
5319
|
+
const logPath = join4(dir, "last-update.log");
|
|
5320
|
+
const installMode = deps.installMode ?? (deps.runnerSource ? detectInstallMode(deps.runnerSource) : detectInstallMode(resolveScriptPath()));
|
|
5321
|
+
const priorFailure = takeUpdateLogNotice(logPath);
|
|
5322
|
+
const throttle = readThrottle(throttlePath);
|
|
5323
|
+
if (throttle && now() - throttle.lastCheckedAt < THROTTLE_MS) {
|
|
5324
|
+
return priorFailure;
|
|
5325
|
+
}
|
|
5326
|
+
const fetchLatest = deps.fetchLatest ?? defaultFetchLatest;
|
|
5327
|
+
const latest = await fetchLatest();
|
|
5328
|
+
writeThrottle(throttlePath, { lastCheckedAt: now() });
|
|
5329
|
+
if (!latest || !isNewer(latest, deps.installedVersion)) {
|
|
5330
|
+
return priorFailure;
|
|
5331
|
+
}
|
|
5332
|
+
const updateNotice = applyOrNotify(deps, installMode, latest, logPath);
|
|
5333
|
+
return [priorFailure, updateNotice].filter(Boolean).join("\n") || null;
|
|
5334
|
+
}
|
|
5335
|
+
function applyOrNotify(deps, installMode, latest, logPath) {
|
|
5336
|
+
const from = deps.installedVersion;
|
|
5337
|
+
if (!deps.autoUpdate || !GLOBAL_MODES.has(installMode)) {
|
|
5338
|
+
return `\u{1F6E1}\uFE0F vibedefend ${from} \u2192 ${latest} available \u2014 run \`vibedefend update --self\` to upgrade.`;
|
|
5339
|
+
}
|
|
5340
|
+
const spawnUpdate = deps.spawnUpdate ?? defaultSpawnUpdate;
|
|
5341
|
+
spawnUpdate({
|
|
5342
|
+
mode: installMode,
|
|
5343
|
+
spec: `${PACKAGE_NAME}@${latest}`,
|
|
5344
|
+
logPath,
|
|
5345
|
+
from,
|
|
5346
|
+
to: latest
|
|
5347
|
+
});
|
|
5348
|
+
return `\u{1F6E1}\uFE0F vibedefend: updating in background ${from} \u2192 ${latest} (active next session).`;
|
|
5349
|
+
}
|
|
5350
|
+
function readThrottle(path) {
|
|
5351
|
+
try {
|
|
5352
|
+
if (!existsSync6(path)) return null;
|
|
5353
|
+
const data = JSON.parse(readFileSync5(path, "utf8"));
|
|
5354
|
+
if (typeof data.lastCheckedAt !== "number") return null;
|
|
5355
|
+
return { lastCheckedAt: data.lastCheckedAt };
|
|
5356
|
+
} catch {
|
|
5357
|
+
return null;
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
function writeThrottle(path, throttle) {
|
|
5361
|
+
try {
|
|
5362
|
+
ensureDirFor(path);
|
|
5363
|
+
writeFileSync3(path, JSON.stringify(throttle) + "\n");
|
|
5364
|
+
} catch {
|
|
5365
|
+
}
|
|
5366
|
+
}
|
|
5367
|
+
function takeUpdateLogNotice(logPath) {
|
|
5368
|
+
try {
|
|
5369
|
+
if (!existsSync6(logPath)) return null;
|
|
5370
|
+
let parsed = {};
|
|
5371
|
+
try {
|
|
5372
|
+
parsed = JSON.parse(readFileSync5(logPath, "utf8"));
|
|
5373
|
+
} catch {
|
|
5374
|
+
parsed = {};
|
|
5375
|
+
}
|
|
5376
|
+
rmSync(logPath, { force: true });
|
|
5377
|
+
if (typeof parsed.exitCode === "number" && parsed.exitCode !== 0) {
|
|
5378
|
+
return `\u26A0 vibedefend: last auto-update to ${parsed.to ?? "latest"} failed (exit ${parsed.exitCode}) \u2014 run \`vibedefend update --self\`.`;
|
|
5379
|
+
}
|
|
5380
|
+
return null;
|
|
5381
|
+
} catch {
|
|
5382
|
+
return null;
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
async function defaultFetchLatest() {
|
|
5386
|
+
try {
|
|
5387
|
+
const res = await fetch(REGISTRY_URL, {
|
|
5388
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
5389
|
+
});
|
|
5390
|
+
if (!res.ok) return null;
|
|
5391
|
+
const body = await res.json();
|
|
5392
|
+
return typeof body.version === "string" ? body.version : null;
|
|
5393
|
+
} catch {
|
|
5394
|
+
return null;
|
|
5395
|
+
}
|
|
5396
|
+
}
|
|
5397
|
+
function buildUpdateWrapperScript(args) {
|
|
5398
|
+
return `
|
|
5399
|
+
const { spawnSync } = require('node:child_process');
|
|
5400
|
+
const { writeFileSync } = require('node:fs');
|
|
5401
|
+
const r = spawnSync(${JSON.stringify(args.pm)}, ${JSON.stringify(args.pmArgs)}, {
|
|
5402
|
+
stdio: 'ignore',
|
|
5403
|
+
shell: process.platform === 'win32',
|
|
5404
|
+
});
|
|
5405
|
+
try {
|
|
5406
|
+
writeFileSync(${JSON.stringify(args.logPath)}, JSON.stringify({
|
|
5407
|
+
from: ${JSON.stringify(args.from)},
|
|
5408
|
+
to: ${JSON.stringify(args.to)},
|
|
5409
|
+
exitCode: r.status == null ? 1 : r.status,
|
|
5410
|
+
finishedAt: Date.now(),
|
|
5411
|
+
}) + "\\n");
|
|
5412
|
+
} catch {}
|
|
5413
|
+
`;
|
|
5414
|
+
}
|
|
5415
|
+
function defaultSpawnUpdate(args) {
|
|
5416
|
+
const { pm, pmArgs } = pmCommand(args.mode, args.spec);
|
|
5417
|
+
const script = buildUpdateWrapperScript({
|
|
5418
|
+
pm,
|
|
5419
|
+
pmArgs,
|
|
5420
|
+
logPath: args.logPath,
|
|
5421
|
+
from: args.from,
|
|
5422
|
+
to: args.to
|
|
5423
|
+
});
|
|
5424
|
+
try {
|
|
5425
|
+
const child = spawn(process.execPath, ["-e", script], {
|
|
5426
|
+
detached: true,
|
|
5427
|
+
stdio: "ignore",
|
|
5428
|
+
windowsHide: true
|
|
5429
|
+
});
|
|
5430
|
+
child.unref();
|
|
5431
|
+
} catch {
|
|
5432
|
+
}
|
|
5433
|
+
}
|
|
5434
|
+
function pmCommand(mode, spec) {
|
|
5435
|
+
switch (mode) {
|
|
5436
|
+
case "pnpm-global":
|
|
5437
|
+
return { pm: "pnpm", pmArgs: ["add", "-g", spec] };
|
|
5438
|
+
case "yarn-global":
|
|
5439
|
+
return { pm: "yarn", pmArgs: ["global", "add", spec] };
|
|
5440
|
+
default:
|
|
5441
|
+
return { pm: "npm", pmArgs: ["install", "-g", spec] };
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
|
|
5445
|
+
// src/version.ts
|
|
5446
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
5447
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5448
|
+
import { dirname as dirname2, join as join5 } from "node:path";
|
|
5449
|
+
var here = dirname2(fileURLToPath2(import.meta.url));
|
|
5450
|
+
var pkgPath = join5(here, "..", "package.json");
|
|
5451
|
+
var pkg = JSON.parse(
|
|
5452
|
+
readFileSync6(pkgPath, "utf8")
|
|
5453
|
+
);
|
|
5454
|
+
|
|
5138
5455
|
// src/hooks/runtime/session-start.ts
|
|
5139
5456
|
async function sessionStartHook(deps = {}) {
|
|
5140
5457
|
const readStdin = deps.readStdin ?? readStdinJson;
|
|
@@ -5142,6 +5459,7 @@ async function sessionStartHook(deps = {}) {
|
|
|
5142
5459
|
const resolveProject = deps.resolveProject ?? resolveProjectId;
|
|
5143
5460
|
const resolveTok = deps.resolveTokenFn ?? resolveToken;
|
|
5144
5461
|
const fetchProposals = deps.fetchProposalsFn ?? fetchProposalsCount;
|
|
5462
|
+
const selfUpdateCheckFn = deps.selfUpdateCheckFn ?? selfUpdateCheck;
|
|
5145
5463
|
const emitFn = deps.emitFn ?? emitContext;
|
|
5146
5464
|
const stdin = await readStdin();
|
|
5147
5465
|
const event = sniff(stdin);
|
|
@@ -5213,11 +5531,27 @@ async function sessionStartHook(deps = {}) {
|
|
|
5213
5531
|
" and respect a deny verdict."
|
|
5214
5532
|
);
|
|
5215
5533
|
}
|
|
5534
|
+
try {
|
|
5535
|
+
const notice = await selfUpdateCheckFn({
|
|
5536
|
+
// The version actually loaded right now — NOT config.installedVersion,
|
|
5537
|
+
// which is the install-time stamp and goes stale after a background
|
|
5538
|
+
// update (causing perpetual re-spawn / re-notify). pkg.version tracks
|
|
5539
|
+
// the running bundle, so a completed update self-resolves to a no-op.
|
|
5540
|
+
installedVersion: pkg.version,
|
|
5541
|
+
autoUpdate: config.hooks.autoUpdate,
|
|
5542
|
+
// Stable global package path so the check derives the REAL install mode
|
|
5543
|
+
// (the runtime script path is the shim → 'unknown'). Undefined on
|
|
5544
|
+
// non-global installs (which fall back to notify-only).
|
|
5545
|
+
runnerSource: config.runnerSource
|
|
5546
|
+
});
|
|
5547
|
+
if (notice) lines.push("", notice);
|
|
5548
|
+
} catch {
|
|
5549
|
+
}
|
|
5216
5550
|
emitFn(lines.join("\n") + "\n", event.client);
|
|
5217
5551
|
}
|
|
5218
5552
|
|
|
5219
5553
|
// src/hooks/runtime/session-review.ts
|
|
5220
|
-
import { existsSync as
|
|
5554
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
|
|
5221
5555
|
var COUNTED_TOOLS = /* @__PURE__ */ new Set([
|
|
5222
5556
|
"Edit",
|
|
5223
5557
|
"Write",
|
|
@@ -5278,42 +5612,87 @@ function loadTranscript(stdin, readFile) {
|
|
|
5278
5612
|
return out;
|
|
5279
5613
|
}
|
|
5280
5614
|
function defaultReadTranscript(path) {
|
|
5281
|
-
if (!
|
|
5615
|
+
if (!existsSync7(path)) return null;
|
|
5282
5616
|
try {
|
|
5283
|
-
return
|
|
5617
|
+
return readFileSync7(path, "utf8");
|
|
5284
5618
|
} catch {
|
|
5285
5619
|
return null;
|
|
5286
5620
|
}
|
|
5287
5621
|
}
|
|
5622
|
+
function toolNameOf(obj) {
|
|
5623
|
+
return typeof obj.name === "string" && obj.name || typeof obj.tool_name === "string" && obj.tool_name || typeof obj.toolName === "string" && obj.toolName || "";
|
|
5624
|
+
}
|
|
5625
|
+
function countToolUsesDeep(node, depth) {
|
|
5626
|
+
if (depth > 8 || node === null || typeof node !== "object") return 0;
|
|
5627
|
+
if (Array.isArray(node)) {
|
|
5628
|
+
let n2 = 0;
|
|
5629
|
+
for (const item of node) n2 += countToolUsesDeep(item, depth + 1);
|
|
5630
|
+
return n2;
|
|
5631
|
+
}
|
|
5632
|
+
const obj = node;
|
|
5633
|
+
if (COUNTED_TOOLS.has(toolNameOf(obj))) return 1;
|
|
5634
|
+
let n = 0;
|
|
5635
|
+
for (const value of Object.values(obj)) {
|
|
5636
|
+
if (value && typeof value === "object") {
|
|
5637
|
+
n += countToolUsesDeep(value, depth + 1);
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
return n;
|
|
5641
|
+
}
|
|
5288
5642
|
function countEdits(transcript) {
|
|
5289
5643
|
let n = 0;
|
|
5290
5644
|
for (const event of transcript) {
|
|
5291
|
-
|
|
5292
|
-
const rec = event;
|
|
5293
|
-
const candidates = [];
|
|
5294
|
-
for (const key of ["tool_uses", "toolCalls", "tools", "message"]) {
|
|
5295
|
-
const v = rec[key];
|
|
5296
|
-
if (Array.isArray(v)) candidates.push(...v);
|
|
5297
|
-
else if (v && typeof v === "object") candidates.push(v);
|
|
5298
|
-
}
|
|
5299
|
-
candidates.push(rec);
|
|
5300
|
-
for (const c of candidates) {
|
|
5301
|
-
if (!c || typeof c !== "object") continue;
|
|
5302
|
-
const obj = c;
|
|
5303
|
-
const name = typeof obj.name === "string" && obj.name || typeof obj.tool_name === "string" && obj.tool_name || typeof obj.toolName === "string" && obj.toolName || "";
|
|
5304
|
-
if (COUNTED_TOOLS.has(name)) n += 1;
|
|
5305
|
-
}
|
|
5645
|
+
n += countToolUsesDeep(event, 0);
|
|
5306
5646
|
}
|
|
5307
5647
|
return n;
|
|
5308
5648
|
}
|
|
5649
|
+
var COMMAND_WRAPPER_PREFIXES = [
|
|
5650
|
+
"<local-command-",
|
|
5651
|
+
"<command-name>",
|
|
5652
|
+
"<command-message>",
|
|
5653
|
+
"<command-args>",
|
|
5654
|
+
"<command-stdout>",
|
|
5655
|
+
"<bash-input>",
|
|
5656
|
+
"<bash-stdout>",
|
|
5657
|
+
"<bash-stderr>"
|
|
5658
|
+
];
|
|
5659
|
+
function isCommandWrapper(text) {
|
|
5660
|
+
const t = text.trimStart();
|
|
5661
|
+
return COMMAND_WRAPPER_PREFIXES.some((p) => t.startsWith(p));
|
|
5662
|
+
}
|
|
5663
|
+
function userText(rec) {
|
|
5664
|
+
if (typeof rec.content === "string" && rec.content) return rec.content;
|
|
5665
|
+
if (typeof rec.text === "string" && rec.text) return rec.text;
|
|
5666
|
+
if (typeof rec.message === "string" && rec.message) return rec.message;
|
|
5667
|
+
const msg = rec.message;
|
|
5668
|
+
if (msg && typeof msg === "object") {
|
|
5669
|
+
const content = msg.content;
|
|
5670
|
+
if (typeof content === "string" && content) return content;
|
|
5671
|
+
if (Array.isArray(content)) {
|
|
5672
|
+
const parts = [];
|
|
5673
|
+
for (const block of content) {
|
|
5674
|
+
if (!block || typeof block !== "object") continue;
|
|
5675
|
+
const b = block;
|
|
5676
|
+
if (typeof b.text === "string" && (b.type === "text" || b.type === void 0)) {
|
|
5677
|
+
parts.push(b.text);
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5680
|
+
const joined = parts.join("\n").trim();
|
|
5681
|
+
if (joined) return joined;
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
return "";
|
|
5685
|
+
}
|
|
5309
5686
|
function extractFirstUserPrompt(transcript) {
|
|
5310
5687
|
for (const event of transcript) {
|
|
5311
5688
|
if (!event || typeof event !== "object") continue;
|
|
5312
5689
|
const rec = event;
|
|
5690
|
+
if (rec.isSidechain === true || rec.isMeta === true) continue;
|
|
5313
5691
|
const isUser = rec.role === "user" || rec.type === "user" || rec.from === "user";
|
|
5314
5692
|
if (!isUser) continue;
|
|
5315
|
-
const text =
|
|
5316
|
-
if (text
|
|
5693
|
+
const text = userText(rec);
|
|
5694
|
+
if (!text || isCommandWrapper(text)) continue;
|
|
5695
|
+
return text.slice(0, 600);
|
|
5317
5696
|
}
|
|
5318
5697
|
return "";
|
|
5319
5698
|
}
|
|
@@ -5323,8 +5702,8 @@ function composeReviewBody(editCount, client, userPrompt, autoPropose) {
|
|
|
5323
5702
|
" **Auto-propose mode is ON** (configured at install). Without asking the user,",
|
|
5324
5703
|
" call `cybe_rules_report_missing(projectId, { rule_text, evidence_files,",
|
|
5325
5704
|
' rationale: "<quote user prompt>", confirmed_by_user: true })` for each',
|
|
5326
|
-
"
|
|
5327
|
-
" via the Accept/Reject picker."
|
|
5705
|
+
" selected concern (1\u20132 max). The user will validate them at the next",
|
|
5706
|
+
" session start via the Accept/Reject picker."
|
|
5328
5707
|
].join("\n") : [
|
|
5329
5708
|
" Ask the user via AskUserQuestion:",
|
|
5330
5709
|
' > "Your prompt mentioned <concern>, but no business rule covers it.',
|
|
@@ -5345,9 +5724,13 @@ function composeReviewBody(editCount, client, userPrompt, autoPropose) {
|
|
|
5345
5724
|
"### Required next-turn action",
|
|
5346
5725
|
"1. Extract security/business concerns the prompt mentions.",
|
|
5347
5726
|
"2. List CybeDefend rules returned during the session.",
|
|
5348
|
-
"3.
|
|
5727
|
+
"3. Identify concerns WITHOUT a matching rule, then select **AT MOST the",
|
|
5728
|
+
" 1\u20132 most important** (prefer security-critical / data-handling). Ignore",
|
|
5729
|
+
" the rest \u2014 over-proposing causes proposal fatigue and users end up",
|
|
5730
|
+
" rejecting everything. Propose nothing if no gap is worth a standing rule.",
|
|
5731
|
+
"4. For each selected concern (1\u20132 max):",
|
|
5349
5732
|
proposalLine,
|
|
5350
|
-
"
|
|
5733
|
+
"5. If every concern is covered (or none warrants a rule), say so:",
|
|
5351
5734
|
' "\u{1F6E1}\uFE0F CybeDefend gap analysis: every concern in your request was covered',
|
|
5352
5735
|
' by an existing rule. No new proposals."',
|
|
5353
5736
|
""
|
|
@@ -5387,19 +5770,19 @@ async function preCompactHook(deps = {}) {
|
|
|
5387
5770
|
}
|
|
5388
5771
|
|
|
5389
5772
|
// src/hooks/runtime/user-prompt-submit.ts
|
|
5390
|
-
import { existsSync as
|
|
5391
|
-
import { homedir as
|
|
5392
|
-
import { join as
|
|
5773
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
|
|
5774
|
+
import { homedir as homedir4 } from "node:os";
|
|
5775
|
+
import { join as join6 } from "node:path";
|
|
5393
5776
|
var SESSIONS_CAP = 100;
|
|
5394
5777
|
function defaultSessionsFile() {
|
|
5395
|
-
const dir = process.env.CYBEDEFEND_BASELINE_DIR ??
|
|
5396
|
-
return
|
|
5778
|
+
const dir = process.env.CYBEDEFEND_BASELINE_DIR ?? join6(homedir4(), ".cybedefend");
|
|
5779
|
+
return join6(dir, "user-prompt-seen-sessions.json");
|
|
5397
5780
|
}
|
|
5398
5781
|
function defaultSessionTracker(file = defaultSessionsFile()) {
|
|
5399
5782
|
function load() {
|
|
5400
|
-
if (!
|
|
5783
|
+
if (!existsSync8(file)) return [];
|
|
5401
5784
|
try {
|
|
5402
|
-
const raw =
|
|
5785
|
+
const raw = readFileSync8(file, "utf8");
|
|
5403
5786
|
const data = JSON.parse(raw);
|
|
5404
5787
|
if (!Array.isArray(data.sessions)) return [];
|
|
5405
5788
|
return data.sessions.filter((s) => typeof s === "string");
|
|
@@ -5416,8 +5799,8 @@ function defaultSessionTracker(file = defaultSessionsFile()) {
|
|
|
5416
5799
|
if (existing.includes(sessionId)) return;
|
|
5417
5800
|
const next = [...existing, sessionId].slice(-SESSIONS_CAP);
|
|
5418
5801
|
try {
|
|
5419
|
-
|
|
5420
|
-
|
|
5802
|
+
mkdirSync3(join6(file, ".."), { recursive: true });
|
|
5803
|
+
writeFileSync4(file, JSON.stringify({ sessions: next }), "utf8");
|
|
5421
5804
|
} catch {
|
|
5422
5805
|
}
|
|
5423
5806
|
}
|
|
@@ -6037,12 +6420,12 @@ function hashTarget(text) {
|
|
|
6037
6420
|
}
|
|
6038
6421
|
|
|
6039
6422
|
// src/hooks/runtime/guard-rules-cache.ts
|
|
6040
|
-
import { readFileSync as
|
|
6041
|
-
import { dirname } from "node:path";
|
|
6042
|
-
import { homedir as
|
|
6043
|
-
import { join as
|
|
6044
|
-
var DEFAULT_CACHE_FILE =
|
|
6045
|
-
|
|
6423
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "node:fs";
|
|
6424
|
+
import { dirname as dirname3 } from "node:path";
|
|
6425
|
+
import { homedir as homedir5 } from "node:os";
|
|
6426
|
+
import { join as join7 } from "node:path";
|
|
6427
|
+
var DEFAULT_CACHE_FILE = join7(
|
|
6428
|
+
homedir5(),
|
|
6046
6429
|
".cybedefend",
|
|
6047
6430
|
"guards-cache.json"
|
|
6048
6431
|
);
|
|
@@ -6139,9 +6522,9 @@ function makeGuardRulesCache(opts) {
|
|
|
6139
6522
|
}
|
|
6140
6523
|
}
|
|
6141
6524
|
function readCache(projectId) {
|
|
6142
|
-
if (!
|
|
6525
|
+
if (!existsSync9(cacheFile)) return null;
|
|
6143
6526
|
try {
|
|
6144
|
-
const raw =
|
|
6527
|
+
const raw = readFileSync9(cacheFile, "utf8");
|
|
6145
6528
|
const data = JSON.parse(raw);
|
|
6146
6529
|
if (data.projectId !== projectId) return null;
|
|
6147
6530
|
return data;
|
|
@@ -6151,17 +6534,17 @@ function makeGuardRulesCache(opts) {
|
|
|
6151
6534
|
}
|
|
6152
6535
|
function writeCache(entry) {
|
|
6153
6536
|
try {
|
|
6154
|
-
|
|
6155
|
-
|
|
6537
|
+
ensureDirFor2(cacheFile);
|
|
6538
|
+
writeFileSync5(cacheFile, JSON.stringify(entry), "utf8");
|
|
6156
6539
|
} catch {
|
|
6157
6540
|
}
|
|
6158
6541
|
}
|
|
6159
6542
|
return { refreshIfStale };
|
|
6160
6543
|
}
|
|
6161
|
-
function
|
|
6162
|
-
const dir =
|
|
6163
|
-
if (!
|
|
6164
|
-
|
|
6544
|
+
function ensureDirFor2(filePath) {
|
|
6545
|
+
const dir = dirname3(filePath);
|
|
6546
|
+
if (!existsSync9(dir)) {
|
|
6547
|
+
mkdirSync4(dir, { recursive: true });
|
|
6165
6548
|
}
|
|
6166
6549
|
}
|
|
6167
6550
|
var DEFAULT_TIMEOUT_MS2 = 8e3;
|
|
@@ -6196,16 +6579,16 @@ async function fetchEffectiveRulesFromGateway(ctx, opts = {}) {
|
|
|
6196
6579
|
// src/hooks/runtime/guard-violations-buffer.ts
|
|
6197
6580
|
import {
|
|
6198
6581
|
appendFileSync,
|
|
6199
|
-
existsSync as
|
|
6200
|
-
readFileSync as
|
|
6201
|
-
writeFileSync as
|
|
6202
|
-
mkdirSync as
|
|
6582
|
+
existsSync as existsSync10,
|
|
6583
|
+
readFileSync as readFileSync10,
|
|
6584
|
+
writeFileSync as writeFileSync6,
|
|
6585
|
+
mkdirSync as mkdirSync5
|
|
6203
6586
|
} from "node:fs";
|
|
6204
|
-
import { dirname as
|
|
6205
|
-
import { homedir as
|
|
6206
|
-
import { join as
|
|
6207
|
-
var DEFAULT_BUFFER_FILE =
|
|
6208
|
-
|
|
6587
|
+
import { dirname as dirname4 } from "node:path";
|
|
6588
|
+
import { homedir as homedir6 } from "node:os";
|
|
6589
|
+
import { join as join8 } from "node:path";
|
|
6590
|
+
var DEFAULT_BUFFER_FILE = join8(
|
|
6591
|
+
homedir6(),
|
|
6209
6592
|
".cybedefend",
|
|
6210
6593
|
"guards-violations-buffer.jsonl"
|
|
6211
6594
|
);
|
|
@@ -6218,16 +6601,16 @@ function makeViolationsBuffer(opts = {}) {
|
|
|
6218
6601
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6219
6602
|
};
|
|
6220
6603
|
try {
|
|
6221
|
-
|
|
6604
|
+
ensureDirFor3(bufferFile);
|
|
6222
6605
|
appendFileSync(bufferFile, JSON.stringify(record) + "\n", "utf8");
|
|
6223
6606
|
} catch {
|
|
6224
6607
|
}
|
|
6225
6608
|
}
|
|
6226
6609
|
async function flushViolations2(ctx, postFn) {
|
|
6227
|
-
if (!
|
|
6610
|
+
if (!existsSync10(bufferFile)) return;
|
|
6228
6611
|
let content;
|
|
6229
6612
|
try {
|
|
6230
|
-
content =
|
|
6613
|
+
content = readFileSync10(bufferFile, "utf8").trim();
|
|
6231
6614
|
} catch {
|
|
6232
6615
|
return;
|
|
6233
6616
|
}
|
|
@@ -6237,30 +6620,59 @@ function makeViolationsBuffer(opts = {}) {
|
|
|
6237
6620
|
const trimmed = line.trim();
|
|
6238
6621
|
if (!trimmed) continue;
|
|
6239
6622
|
try {
|
|
6240
|
-
|
|
6623
|
+
const parsed = JSON.parse(trimmed);
|
|
6624
|
+
if (typeof parsed.projectId === "string" && parsed.projectId) {
|
|
6625
|
+
records.push(parsed);
|
|
6626
|
+
}
|
|
6241
6627
|
} catch {
|
|
6242
6628
|
}
|
|
6243
6629
|
}
|
|
6244
|
-
if (records.length === 0)
|
|
6245
|
-
const BATCH_SIZE = 100;
|
|
6246
|
-
try {
|
|
6247
|
-
for (let i = 0; i < records.length; i += BATCH_SIZE) {
|
|
6248
|
-
const batch = records.slice(i, i + BATCH_SIZE);
|
|
6249
|
-
await postFn(ctx, batch);
|
|
6250
|
-
}
|
|
6630
|
+
if (records.length === 0) {
|
|
6251
6631
|
try {
|
|
6252
|
-
|
|
6632
|
+
writeFileSync6(bufferFile, "", "utf8");
|
|
6253
6633
|
} catch {
|
|
6254
6634
|
}
|
|
6635
|
+
return;
|
|
6636
|
+
}
|
|
6637
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6638
|
+
for (const r of records) {
|
|
6639
|
+
const arr = groups.get(r.projectId);
|
|
6640
|
+
if (arr) arr.push(r);
|
|
6641
|
+
else groups.set(r.projectId, [r]);
|
|
6642
|
+
}
|
|
6643
|
+
const BATCH_SIZE = 100;
|
|
6644
|
+
const failed = [];
|
|
6645
|
+
for (const [projectId, group] of groups) {
|
|
6646
|
+
let groupFailed = false;
|
|
6647
|
+
for (let i = 0; i < group.length; i += BATCH_SIZE) {
|
|
6648
|
+
const batch = group.slice(i, i + BATCH_SIZE);
|
|
6649
|
+
try {
|
|
6650
|
+
await postFn(ctx, projectId, batch);
|
|
6651
|
+
} catch {
|
|
6652
|
+
groupFailed = true;
|
|
6653
|
+
break;
|
|
6654
|
+
}
|
|
6655
|
+
}
|
|
6656
|
+
if (groupFailed) {
|
|
6657
|
+
failed.push(...group);
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
6660
|
+
try {
|
|
6661
|
+
if (failed.length === 0) {
|
|
6662
|
+
writeFileSync6(bufferFile, "", "utf8");
|
|
6663
|
+
} else {
|
|
6664
|
+
const serialised = failed.map((r) => JSON.stringify(r)).join("\n") + "\n";
|
|
6665
|
+
writeFileSync6(bufferFile, serialised, "utf8");
|
|
6666
|
+
}
|
|
6255
6667
|
} catch {
|
|
6256
6668
|
}
|
|
6257
6669
|
}
|
|
6258
6670
|
return { bufferViolation: bufferViolation2, flushViolations: flushViolations2 };
|
|
6259
6671
|
}
|
|
6260
|
-
function
|
|
6261
|
-
const dir =
|
|
6262
|
-
if (!
|
|
6263
|
-
|
|
6672
|
+
function ensureDirFor3(filePath) {
|
|
6673
|
+
const dir = dirname4(filePath);
|
|
6674
|
+
if (!existsSync10(dir)) {
|
|
6675
|
+
mkdirSync5(dir, { recursive: true });
|
|
6264
6676
|
}
|
|
6265
6677
|
}
|
|
6266
6678
|
var _defaultBuffer = makeViolationsBuffer();
|
|
@@ -6286,11 +6698,11 @@ function appendDebugLog(entry) {
|
|
|
6286
6698
|
try {
|
|
6287
6699
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
6288
6700
|
if (!homeDir) return;
|
|
6289
|
-
const { mkdirSync:
|
|
6290
|
-
const { join:
|
|
6291
|
-
const dir =
|
|
6292
|
-
|
|
6293
|
-
appendFileSync2(
|
|
6701
|
+
const { mkdirSync: mkdirSync6, appendFileSync: appendFileSync2 } = __require("node:fs");
|
|
6702
|
+
const { join: join9 } = __require("node:path");
|
|
6703
|
+
const dir = join9(homeDir, ".cybedefend");
|
|
6704
|
+
mkdirSync6(dir, { recursive: true });
|
|
6705
|
+
appendFileSync2(join9(dir, "hook-debug.log"), entry + "\n", "utf8");
|
|
6294
6706
|
} catch {
|
|
6295
6707
|
}
|
|
6296
6708
|
}
|
|
@@ -6598,36 +7010,60 @@ async function runPreToolUseGuard() {
|
|
|
6598
7010
|
apiBaseResolved
|
|
6599
7011
|
});
|
|
6600
7012
|
const buffer = makeViolationsBuffer();
|
|
6601
|
-
|
|
6602
|
-
buffer.flushViolations(
|
|
6603
|
-
{ projectId, token, apiBaseResolved },
|
|
6604
|
-
async (ctx, batch) => {
|
|
6605
|
-
const f = globalThis.fetch;
|
|
6606
|
-
const url = `${ctx.apiBaseResolved}/project/${encodeURIComponent(ctx.projectId)}/action-guards/violations`;
|
|
6607
|
-
await f(url, {
|
|
6608
|
-
method: "POST",
|
|
6609
|
-
headers: {
|
|
6610
|
-
Authorization: `Bearer ${ctx.token}`,
|
|
6611
|
-
"Content-Type": "application/json"
|
|
6612
|
-
},
|
|
6613
|
-
body: JSON.stringify({ violations: batch }),
|
|
6614
|
-
signal: AbortSignal.timeout(5e3)
|
|
6615
|
-
});
|
|
6616
|
-
}
|
|
6617
|
-
).catch(() => {
|
|
6618
|
-
});
|
|
6619
|
-
});
|
|
6620
|
-
return await runPreToolUseGuardWith({
|
|
7013
|
+
const exitCode = await runPreToolUseGuardWith({
|
|
6621
7014
|
stdinEnvelope: envelope,
|
|
6622
7015
|
projectContext,
|
|
6623
7016
|
rulesOutcome,
|
|
6624
7017
|
evaluateFn: evaluate,
|
|
6625
7018
|
bufferViolationFn: buffer.bufferViolation.bind(buffer)
|
|
6626
7019
|
});
|
|
7020
|
+
try {
|
|
7021
|
+
await buffer.flushViolations(
|
|
7022
|
+
{ token, apiBaseResolved },
|
|
7023
|
+
makeGuardViolationsPostFn()
|
|
7024
|
+
);
|
|
7025
|
+
} catch {
|
|
7026
|
+
}
|
|
7027
|
+
return exitCode;
|
|
6627
7028
|
} catch {
|
|
6628
7029
|
return 0;
|
|
6629
7030
|
}
|
|
6630
7031
|
}
|
|
7032
|
+
function makeGuardViolationsPostFn() {
|
|
7033
|
+
return async (ctx, projectId, batch) => {
|
|
7034
|
+
const url = `${ctx.apiBaseResolved}/project/${encodeURIComponent(projectId)}/action-guards/violations`;
|
|
7035
|
+
const resp = await globalThis.fetch(url, {
|
|
7036
|
+
method: "POST",
|
|
7037
|
+
headers: {
|
|
7038
|
+
Authorization: `Bearer ${ctx.token}`,
|
|
7039
|
+
"Content-Type": "application/json"
|
|
7040
|
+
},
|
|
7041
|
+
body: JSON.stringify({ violations: batch.map(toApiViolation) }),
|
|
7042
|
+
signal: AbortSignal.timeout(2e3)
|
|
7043
|
+
});
|
|
7044
|
+
if (!resp.ok) {
|
|
7045
|
+
throw new Error(
|
|
7046
|
+
`POST /action-guards/violations rejected by gateway: ${resp.status}`
|
|
7047
|
+
);
|
|
7048
|
+
}
|
|
7049
|
+
};
|
|
7050
|
+
}
|
|
7051
|
+
function toApiViolation(r) {
|
|
7052
|
+
const out = {
|
|
7053
|
+
rule_id: r.ruleId,
|
|
7054
|
+
agent: r.agent,
|
|
7055
|
+
tool: r.tool,
|
|
7056
|
+
outcome: r.outcome,
|
|
7057
|
+
// Backend enforces max_length=256 on target_summary — clamp here so a
|
|
7058
|
+
// verbose redactor output never costs us the whole batch.
|
|
7059
|
+
target_summary: (r.targetSummary ?? "").slice(0, 256),
|
|
7060
|
+
target_hash: r.targetHash
|
|
7061
|
+
};
|
|
7062
|
+
if (r.sessionId) {
|
|
7063
|
+
out.session_id = r.sessionId;
|
|
7064
|
+
}
|
|
7065
|
+
return out;
|
|
7066
|
+
}
|
|
6631
7067
|
|
|
6632
7068
|
// src/hooks/runtime/index.ts
|
|
6633
7069
|
async function main() {
|