@node9/proxy 1.3.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +759 -270
- package/dist/cli.mjs +746 -257
- package/dist/index.js +506 -53
- package/dist/index.mjs +506 -53
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -114,8 +114,8 @@ function sanitizeConfig(raw) {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
const lines = result.error.issues.map((issue) => {
|
|
117
|
-
const
|
|
118
|
-
return ` \u2022 ${
|
|
117
|
+
const path24 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
118
|
+
return ` \u2022 ${path24}: ${issue.message}`;
|
|
119
119
|
});
|
|
120
120
|
return {
|
|
121
121
|
sanitized,
|
|
@@ -1178,8 +1178,440 @@ var init_dlp = __esm({
|
|
|
1178
1178
|
}
|
|
1179
1179
|
});
|
|
1180
1180
|
|
|
1181
|
+
// src/utils/provenance.ts
|
|
1182
|
+
function findInPath(cmd) {
|
|
1183
|
+
if (import_path5.default.posix.isAbsolute(cmd)) return cmd;
|
|
1184
|
+
const pathEnv = process.env.PATH ?? "";
|
|
1185
|
+
for (const dir of pathEnv.split(import_path5.default.delimiter)) {
|
|
1186
|
+
if (!dir) continue;
|
|
1187
|
+
const full = import_path5.default.join(dir, cmd);
|
|
1188
|
+
try {
|
|
1189
|
+
import_fs5.default.accessSync(full, import_fs5.default.constants.X_OK);
|
|
1190
|
+
return full;
|
|
1191
|
+
} catch {
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
return null;
|
|
1195
|
+
}
|
|
1196
|
+
function _classifyPath(resolved, cwd) {
|
|
1197
|
+
if (cwd && resolved.startsWith(cwd + "/")) {
|
|
1198
|
+
return { trustLevel: "user", reason: "binary in project directory" };
|
|
1199
|
+
}
|
|
1200
|
+
const osTmp = import_os4.default.tmpdir();
|
|
1201
|
+
const allSuspect = osTmp ? [...SUSPECT_PREFIXES, osTmp] : SUSPECT_PREFIXES;
|
|
1202
|
+
if (allSuspect.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
|
|
1203
|
+
return { trustLevel: "suspect", reason: `binary in temp directory: ${resolved}` };
|
|
1204
|
+
}
|
|
1205
|
+
if (SYSTEM_PREFIXES.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
|
|
1206
|
+
return { trustLevel: "system", reason: "" };
|
|
1207
|
+
}
|
|
1208
|
+
if (MANAGED_PREFIXES.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
|
|
1209
|
+
return { trustLevel: "managed", reason: "" };
|
|
1210
|
+
}
|
|
1211
|
+
if (USER_PREFIXES.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
|
|
1212
|
+
return { trustLevel: "user", reason: "" };
|
|
1213
|
+
}
|
|
1214
|
+
return { trustLevel: "unknown", reason: "binary in unrecognized location" };
|
|
1215
|
+
}
|
|
1216
|
+
function checkProvenance(cmd, cwd) {
|
|
1217
|
+
const bare = cmd.startsWith("./") ? cmd.slice(2) : cmd;
|
|
1218
|
+
if (import_path5.default.posix.isAbsolute(bare)) {
|
|
1219
|
+
const early = _classifyPath(bare, cwd);
|
|
1220
|
+
if (early.trustLevel === "suspect") {
|
|
1221
|
+
return { resolvedPath: bare, ...early };
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
let resolved;
|
|
1225
|
+
try {
|
|
1226
|
+
const found = findInPath(bare);
|
|
1227
|
+
if (!found) {
|
|
1228
|
+
return {
|
|
1229
|
+
resolvedPath: cmd,
|
|
1230
|
+
trustLevel: "unknown",
|
|
1231
|
+
reason: "binary not found in PATH"
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
resolved = import_fs5.default.realpathSync(found);
|
|
1235
|
+
} catch {
|
|
1236
|
+
return {
|
|
1237
|
+
resolvedPath: cmd,
|
|
1238
|
+
trustLevel: "unknown",
|
|
1239
|
+
reason: "binary not found in PATH"
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
try {
|
|
1243
|
+
const stat = import_fs5.default.statSync(resolved);
|
|
1244
|
+
if (stat.mode & 2) {
|
|
1245
|
+
return {
|
|
1246
|
+
resolvedPath: resolved,
|
|
1247
|
+
trustLevel: "suspect",
|
|
1248
|
+
reason: "binary is world-writable"
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
} catch {
|
|
1252
|
+
return {
|
|
1253
|
+
resolvedPath: resolved,
|
|
1254
|
+
trustLevel: "unknown",
|
|
1255
|
+
reason: "could not stat binary"
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
const classify = _classifyPath(resolved, cwd);
|
|
1259
|
+
return { resolvedPath: resolved, ...classify };
|
|
1260
|
+
}
|
|
1261
|
+
var import_fs5, import_path5, import_os4, SYSTEM_PREFIXES, MANAGED_PREFIXES, USER_PREFIXES, SUSPECT_PREFIXES;
|
|
1262
|
+
var init_provenance = __esm({
|
|
1263
|
+
"src/utils/provenance.ts"() {
|
|
1264
|
+
"use strict";
|
|
1265
|
+
import_fs5 = __toESM(require("fs"));
|
|
1266
|
+
import_path5 = __toESM(require("path"));
|
|
1267
|
+
import_os4 = __toESM(require("os"));
|
|
1268
|
+
SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
|
|
1269
|
+
MANAGED_PREFIXES = ["/usr/local/bin", "/opt/homebrew", "/home/linuxbrew", "/nix/store"];
|
|
1270
|
+
USER_PREFIXES = [
|
|
1271
|
+
import_path5.default.join(import_os4.default.homedir(), "bin"),
|
|
1272
|
+
import_path5.default.join(import_os4.default.homedir(), ".local", "bin"),
|
|
1273
|
+
import_path5.default.join(import_os4.default.homedir(), ".cargo", "bin"),
|
|
1274
|
+
import_path5.default.join(import_os4.default.homedir(), ".npm-global", "bin"),
|
|
1275
|
+
import_path5.default.join(import_os4.default.homedir(), ".volta", "bin")
|
|
1276
|
+
];
|
|
1277
|
+
SUSPECT_PREFIXES = ["/tmp", "/var/tmp", "/dev/shm"];
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
// src/policy/pipe-chain.ts
|
|
1282
|
+
function isSensitivePath(p) {
|
|
1283
|
+
return SENSITIVE_PATTERNS.some((re) => re.test(p));
|
|
1284
|
+
}
|
|
1285
|
+
function splitOnPipe(cmd) {
|
|
1286
|
+
const segments = [];
|
|
1287
|
+
let current = "";
|
|
1288
|
+
let inSingle = false;
|
|
1289
|
+
let inDouble = false;
|
|
1290
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
1291
|
+
const ch = cmd[i];
|
|
1292
|
+
if (ch === "'" && !inDouble) {
|
|
1293
|
+
inSingle = !inSingle;
|
|
1294
|
+
current += ch;
|
|
1295
|
+
} else if (ch === '"' && !inSingle) {
|
|
1296
|
+
inDouble = !inDouble;
|
|
1297
|
+
current += ch;
|
|
1298
|
+
} else if (ch === "|" && !inSingle && !inDouble && cmd[i + 1] !== "|" && (i === 0 || cmd[i - 1] !== "|")) {
|
|
1299
|
+
segments.push(current.trim());
|
|
1300
|
+
current = "";
|
|
1301
|
+
} else {
|
|
1302
|
+
current += ch;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
if (current.trim()) segments.push(current.trim());
|
|
1306
|
+
return segments.filter(Boolean);
|
|
1307
|
+
}
|
|
1308
|
+
function positionalTokens(segment) {
|
|
1309
|
+
return segment.split(/\s+/).slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("@") && t.length > 0);
|
|
1310
|
+
}
|
|
1311
|
+
function analyzePipeChain(command) {
|
|
1312
|
+
const segments = splitOnPipe(command);
|
|
1313
|
+
if (segments.length < 2) {
|
|
1314
|
+
return {
|
|
1315
|
+
isPipeline: false,
|
|
1316
|
+
hasSensitiveSource: false,
|
|
1317
|
+
hasExternalSink: false,
|
|
1318
|
+
hasObfuscation: false,
|
|
1319
|
+
sourceFiles: [],
|
|
1320
|
+
sinkTargets: [],
|
|
1321
|
+
risk: "none"
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
const sourceFiles = [];
|
|
1325
|
+
const sinkTargets = [];
|
|
1326
|
+
let hasSensitiveSource = false;
|
|
1327
|
+
let hasExternalSink = false;
|
|
1328
|
+
let hasObfuscation = false;
|
|
1329
|
+
for (const segment of segments) {
|
|
1330
|
+
const tokens = segment.split(/\s+/).filter(Boolean);
|
|
1331
|
+
if (tokens.length === 0) continue;
|
|
1332
|
+
const binary = tokens[0].toLowerCase();
|
|
1333
|
+
const args = positionalTokens(segment);
|
|
1334
|
+
if (SOURCE_COMMANDS.has(binary)) {
|
|
1335
|
+
sourceFiles.push(...args);
|
|
1336
|
+
if (args.some(isSensitivePath)) hasSensitiveSource = true;
|
|
1337
|
+
}
|
|
1338
|
+
if (OBFUSCATORS.has(binary)) hasObfuscation = true;
|
|
1339
|
+
if (SINK_COMMANDS.has(binary)) {
|
|
1340
|
+
const targets = args.filter(
|
|
1341
|
+
(a) => a.includes(".") || a.includes("://") || /^\d+\.\d+/.test(a)
|
|
1342
|
+
);
|
|
1343
|
+
sinkTargets.push(...targets);
|
|
1344
|
+
if (targets.length > 0) hasExternalSink = true;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const fullCmd = command.toLowerCase();
|
|
1348
|
+
if (!hasSensitiveSource) {
|
|
1349
|
+
const redirMatch = fullCmd.match(/<\s*(\S+)/);
|
|
1350
|
+
if (redirMatch && isSensitivePath(redirMatch[1])) {
|
|
1351
|
+
hasSensitiveSource = true;
|
|
1352
|
+
sourceFiles.push(redirMatch[1]);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
const risk = hasSensitiveSource && hasExternalSink && hasObfuscation ? "critical" : hasSensitiveSource && hasExternalSink ? "high" : hasExternalSink ? "medium" : "none";
|
|
1356
|
+
return {
|
|
1357
|
+
isPipeline: true,
|
|
1358
|
+
hasSensitiveSource,
|
|
1359
|
+
hasExternalSink,
|
|
1360
|
+
hasObfuscation,
|
|
1361
|
+
sourceFiles,
|
|
1362
|
+
sinkTargets,
|
|
1363
|
+
risk
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
var SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS;
|
|
1367
|
+
var init_pipe_chain = __esm({
|
|
1368
|
+
"src/policy/pipe-chain.ts"() {
|
|
1369
|
+
"use strict";
|
|
1370
|
+
SOURCE_COMMANDS = /* @__PURE__ */ new Set([
|
|
1371
|
+
"cat",
|
|
1372
|
+
"head",
|
|
1373
|
+
"tail",
|
|
1374
|
+
"grep",
|
|
1375
|
+
"awk",
|
|
1376
|
+
"sed",
|
|
1377
|
+
"cut",
|
|
1378
|
+
"sort",
|
|
1379
|
+
"tee",
|
|
1380
|
+
"less",
|
|
1381
|
+
"more",
|
|
1382
|
+
"strings",
|
|
1383
|
+
"xxd"
|
|
1384
|
+
]);
|
|
1385
|
+
SINK_COMMANDS = /* @__PURE__ */ new Set([
|
|
1386
|
+
"curl",
|
|
1387
|
+
"wget",
|
|
1388
|
+
"nc",
|
|
1389
|
+
"ncat",
|
|
1390
|
+
"netcat",
|
|
1391
|
+
"ssh",
|
|
1392
|
+
"scp",
|
|
1393
|
+
"rsync",
|
|
1394
|
+
"socat",
|
|
1395
|
+
"ftp",
|
|
1396
|
+
"sftp",
|
|
1397
|
+
"telnet"
|
|
1398
|
+
]);
|
|
1399
|
+
OBFUSCATORS = /* @__PURE__ */ new Set([
|
|
1400
|
+
"base64",
|
|
1401
|
+
"gzip",
|
|
1402
|
+
"gunzip",
|
|
1403
|
+
"bzip2",
|
|
1404
|
+
"xz",
|
|
1405
|
+
"zstd",
|
|
1406
|
+
"openssl",
|
|
1407
|
+
"gpg",
|
|
1408
|
+
"python",
|
|
1409
|
+
"python3",
|
|
1410
|
+
"perl",
|
|
1411
|
+
"ruby",
|
|
1412
|
+
"node"
|
|
1413
|
+
]);
|
|
1414
|
+
SENSITIVE_PATTERNS = [
|
|
1415
|
+
/(?:^|\/)\.env(?:\.|$)/i,
|
|
1416
|
+
// .env, .env.local, .env.production
|
|
1417
|
+
/id_rsa|id_ed25519|id_ecdsa|id_dsa/i,
|
|
1418
|
+
// SSH private keys
|
|
1419
|
+
/\.pem$|\.key$|\.p12$|\.pfx$/i,
|
|
1420
|
+
// certificate files
|
|
1421
|
+
/(?:^|\/)\.ssh\//i,
|
|
1422
|
+
// ~/.ssh/ directory
|
|
1423
|
+
/(?:^|\/)\.aws\/credentials/i,
|
|
1424
|
+
// AWS credentials
|
|
1425
|
+
/(?:^|\/)\.netrc$/i,
|
|
1426
|
+
// netrc (stores HTTP credentials)
|
|
1427
|
+
/(?:^|\/)(passwd|shadow|sudoers)$/i,
|
|
1428
|
+
// /etc/passwd, /etc/shadow
|
|
1429
|
+
/(?:^|\/)credentials(?:\.json)?$/i
|
|
1430
|
+
// generic credentials files
|
|
1431
|
+
];
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
// src/policy/flag-tables.ts
|
|
1436
|
+
function extractPositionalArgs(tokens, binary) {
|
|
1437
|
+
const binaryName = import_path6.default.basename(binary).replace(/\.exe$/i, "");
|
|
1438
|
+
const flagsWithValues = FLAGS_WITH_VALUES[binaryName] ?? /* @__PURE__ */ new Set();
|
|
1439
|
+
const positional = [];
|
|
1440
|
+
let skipNext = false;
|
|
1441
|
+
for (const token of tokens) {
|
|
1442
|
+
if (skipNext) {
|
|
1443
|
+
skipNext = false;
|
|
1444
|
+
continue;
|
|
1445
|
+
}
|
|
1446
|
+
if (token.startsWith("--") && token.includes("=")) continue;
|
|
1447
|
+
if (token.startsWith("-") && token.length === 2 && flagsWithValues.has(token)) {
|
|
1448
|
+
skipNext = true;
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1451
|
+
if (token.startsWith("--") && flagsWithValues.has(token)) {
|
|
1452
|
+
skipNext = true;
|
|
1453
|
+
continue;
|
|
1454
|
+
}
|
|
1455
|
+
const shortFlag = token.slice(0, 2);
|
|
1456
|
+
if (token.startsWith("-") && token.length > 2 && flagsWithValues.has(shortFlag)) continue;
|
|
1457
|
+
if (token.startsWith("-")) continue;
|
|
1458
|
+
if (token.startsWith("@")) continue;
|
|
1459
|
+
positional.push(token);
|
|
1460
|
+
}
|
|
1461
|
+
return positional;
|
|
1462
|
+
}
|
|
1463
|
+
function extractNetworkTargets(tokens, binary) {
|
|
1464
|
+
return extractPositionalArgs(tokens, binary).map((t) => t.includes("@") ? t.split("@")[1] : t).map((t) => {
|
|
1465
|
+
const colonIdx = t.indexOf(":");
|
|
1466
|
+
if (colonIdx === -1) return t;
|
|
1467
|
+
const afterColon = t.slice(colonIdx + 1);
|
|
1468
|
+
if (/^\d+$/.test(afterColon)) return t.slice(0, colonIdx);
|
|
1469
|
+
return t;
|
|
1470
|
+
}).filter(Boolean);
|
|
1471
|
+
}
|
|
1472
|
+
var import_path6, FLAGS_WITH_VALUES;
|
|
1473
|
+
var init_flag_tables = __esm({
|
|
1474
|
+
"src/policy/flag-tables.ts"() {
|
|
1475
|
+
"use strict";
|
|
1476
|
+
import_path6 = __toESM(require("path"));
|
|
1477
|
+
FLAGS_WITH_VALUES = {
|
|
1478
|
+
curl: /* @__PURE__ */ new Set([
|
|
1479
|
+
"-H",
|
|
1480
|
+
"--header",
|
|
1481
|
+
"-A",
|
|
1482
|
+
"--user-agent",
|
|
1483
|
+
"-e",
|
|
1484
|
+
"--referer",
|
|
1485
|
+
"-x",
|
|
1486
|
+
"--proxy",
|
|
1487
|
+
"-u",
|
|
1488
|
+
"--user",
|
|
1489
|
+
"-d",
|
|
1490
|
+
"--data",
|
|
1491
|
+
"--data-raw",
|
|
1492
|
+
"--data-binary",
|
|
1493
|
+
"-o",
|
|
1494
|
+
"--output",
|
|
1495
|
+
"-F",
|
|
1496
|
+
"--form",
|
|
1497
|
+
"--connect-to",
|
|
1498
|
+
"--resolve",
|
|
1499
|
+
"--cacert",
|
|
1500
|
+
"--cert",
|
|
1501
|
+
"--key",
|
|
1502
|
+
"-m",
|
|
1503
|
+
"--max-time"
|
|
1504
|
+
]),
|
|
1505
|
+
wget: /* @__PURE__ */ new Set([
|
|
1506
|
+
"-O",
|
|
1507
|
+
"--output-document",
|
|
1508
|
+
"-P",
|
|
1509
|
+
"--directory-prefix",
|
|
1510
|
+
"-U",
|
|
1511
|
+
"--user-agent",
|
|
1512
|
+
"-e",
|
|
1513
|
+
"--execute",
|
|
1514
|
+
"--proxy",
|
|
1515
|
+
"--ca-certificate"
|
|
1516
|
+
]),
|
|
1517
|
+
nc: /* @__PURE__ */ new Set(["-x", "-p", "-s", "-w", "-W", "-I", "-O"]),
|
|
1518
|
+
ncat: /* @__PURE__ */ new Set(["-x", "-p", "-s", "--proxy", "--proxy-auth", "-w", "--wait"]),
|
|
1519
|
+
netcat: /* @__PURE__ */ new Set(["-x", "-p", "-s", "-w"]),
|
|
1520
|
+
ssh: /* @__PURE__ */ new Set([
|
|
1521
|
+
"-i",
|
|
1522
|
+
"-l",
|
|
1523
|
+
"-p",
|
|
1524
|
+
"-o",
|
|
1525
|
+
"-E",
|
|
1526
|
+
"-F",
|
|
1527
|
+
"-J",
|
|
1528
|
+
"-L",
|
|
1529
|
+
"-R",
|
|
1530
|
+
"-W",
|
|
1531
|
+
"-b",
|
|
1532
|
+
"-c",
|
|
1533
|
+
"-D",
|
|
1534
|
+
"-e",
|
|
1535
|
+
"-I",
|
|
1536
|
+
"-S"
|
|
1537
|
+
]),
|
|
1538
|
+
scp: /* @__PURE__ */ new Set(["-i", "-o", "-P", "-S"]),
|
|
1539
|
+
rsync: /* @__PURE__ */ new Set(["-e", "--rsh", "--rsync-path", "--password-file", "--log-file"]),
|
|
1540
|
+
socat: /* @__PURE__ */ new Set([])
|
|
1541
|
+
// socat uses address syntax, not flags — no value-flags
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1545
|
+
|
|
1546
|
+
// src/policy/ssh-parser.ts
|
|
1547
|
+
function tokenize(cmd) {
|
|
1548
|
+
const tokens = [];
|
|
1549
|
+
let current = "";
|
|
1550
|
+
let inSingle = false;
|
|
1551
|
+
let inDouble = false;
|
|
1552
|
+
for (const ch of cmd) {
|
|
1553
|
+
if (ch === "'" && !inDouble) {
|
|
1554
|
+
inSingle = !inSingle;
|
|
1555
|
+
} else if (ch === '"' && !inSingle) {
|
|
1556
|
+
inDouble = !inDouble;
|
|
1557
|
+
} else if ((ch === " " || ch === " ") && !inSingle && !inDouble) {
|
|
1558
|
+
if (current) {
|
|
1559
|
+
tokens.push(current);
|
|
1560
|
+
current = "";
|
|
1561
|
+
}
|
|
1562
|
+
} else {
|
|
1563
|
+
current += ch;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
if (current) tokens.push(current);
|
|
1567
|
+
return tokens;
|
|
1568
|
+
}
|
|
1569
|
+
function parseHost(raw) {
|
|
1570
|
+
return raw.split("@").pop().split(":")[0];
|
|
1571
|
+
}
|
|
1572
|
+
function extractAllSshHosts(tokens) {
|
|
1573
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
1574
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
1575
|
+
const t = tokens[i];
|
|
1576
|
+
if (t === "-J" && tokens[i + 1]) {
|
|
1577
|
+
for (const hop of tokens[++i].split(",")) {
|
|
1578
|
+
const h = parseHost(hop);
|
|
1579
|
+
if (h) hosts.add(h);
|
|
1580
|
+
}
|
|
1581
|
+
continue;
|
|
1582
|
+
}
|
|
1583
|
+
if (t === "-o" && tokens[i + 1]?.toLowerCase().startsWith("proxyjump=")) {
|
|
1584
|
+
const val = tokens[++i].split("=").slice(1).join("=");
|
|
1585
|
+
for (const hop of val.split(",")) {
|
|
1586
|
+
const h = parseHost(hop);
|
|
1587
|
+
if (h) hosts.add(h);
|
|
1588
|
+
}
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
if (t === "-o" && tokens[i + 1]?.toLowerCase().startsWith("proxycommand=")) {
|
|
1592
|
+
const raw = tokens[++i].split("=").slice(1).join("=").replace(/^['"]|['"]$/g, "");
|
|
1593
|
+
const subTokens = tokenize(raw);
|
|
1594
|
+
const binary = subTokens[0] ?? "";
|
|
1595
|
+
extractNetworkTargets(subTokens.slice(1), binary).forEach((h) => hosts.add(h));
|
|
1596
|
+
extractAllSshHosts(subTokens.slice(1)).forEach((h) => hosts.add(h));
|
|
1597
|
+
continue;
|
|
1598
|
+
}
|
|
1599
|
+
if (!t.startsWith("-")) {
|
|
1600
|
+
const h = parseHost(t);
|
|
1601
|
+
if (h) hosts.add(h);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return [...hosts].filter(Boolean);
|
|
1605
|
+
}
|
|
1606
|
+
var init_ssh_parser = __esm({
|
|
1607
|
+
"src/policy/ssh-parser.ts"() {
|
|
1608
|
+
"use strict";
|
|
1609
|
+
init_flag_tables();
|
|
1610
|
+
}
|
|
1611
|
+
});
|
|
1612
|
+
|
|
1181
1613
|
// src/policy/index.ts
|
|
1182
|
-
function
|
|
1614
|
+
function tokenize2(toolName) {
|
|
1183
1615
|
return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
|
|
1184
1616
|
}
|
|
1185
1617
|
function matchesPattern(text, patterns) {
|
|
@@ -1192,9 +1624,9 @@ function matchesPattern(text, patterns) {
|
|
|
1192
1624
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1193
1625
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1194
1626
|
}
|
|
1195
|
-
function getNestedValue(obj,
|
|
1627
|
+
function getNestedValue(obj, path24) {
|
|
1196
1628
|
if (!obj || typeof obj !== "object") return null;
|
|
1197
|
-
return
|
|
1629
|
+
return path24.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
1198
1630
|
}
|
|
1199
1631
|
function shouldSnapshot(toolName, args, config) {
|
|
1200
1632
|
if (!config.settings.enableUndo) return false;
|
|
@@ -1329,7 +1761,7 @@ async function analyzeShellCommand(command) {
|
|
|
1329
1761
|
}
|
|
1330
1762
|
return { actions, paths, allTokens };
|
|
1331
1763
|
}
|
|
1332
|
-
async function evaluatePolicy(toolName, args, agent) {
|
|
1764
|
+
async function evaluatePolicy(toolName, args, agent, cwd) {
|
|
1333
1765
|
const config = getConfig();
|
|
1334
1766
|
if (matchesPattern(toolName, config.policy.ignoredTools)) return { decision: "allow" };
|
|
1335
1767
|
if (config.policy.smartRules.length > 0) {
|
|
@@ -1359,11 +1791,55 @@ async function evaluatePolicy(toolName, args, agent) {
|
|
|
1359
1791
|
if (INLINE_EXEC_PATTERN.test(shellCommand.trim())) {
|
|
1360
1792
|
return { decision: "review", blockedByLabel: "Node9 Standard (Inline Execution)", tier: 3 };
|
|
1361
1793
|
}
|
|
1794
|
+
const pipeAnalysis = analyzePipeChain(shellCommand);
|
|
1795
|
+
if (pipeAnalysis.isPipeline) {
|
|
1796
|
+
if (pipeAnalysis.risk === "critical") {
|
|
1797
|
+
return {
|
|
1798
|
+
decision: "block",
|
|
1799
|
+
blockedByLabel: "Node9: Pipe-Chain Exfiltration (critical)",
|
|
1800
|
+
reason: `Sensitive file piped through obfuscator to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${pipeAnalysis.sinkTargets.join(", ")}`,
|
|
1801
|
+
tier: 3
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
if (pipeAnalysis.risk === "high") {
|
|
1805
|
+
return {
|
|
1806
|
+
decision: "review",
|
|
1807
|
+
blockedByLabel: "Node9: Pipe-Chain Exfiltration (high)",
|
|
1808
|
+
reason: `Sensitive file piped to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${pipeAnalysis.sinkTargets.join(", ")}`,
|
|
1809
|
+
tier: 3
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
const firstToken = analyzed.actions[0] ?? "";
|
|
1814
|
+
if (["ssh", "scp", "rsync"].includes(firstToken)) {
|
|
1815
|
+
const rawTokens = shellCommand.trim().split(/\s+/);
|
|
1816
|
+
const sshHosts = extractAllSshHosts(rawTokens.slice(1));
|
|
1817
|
+
allTokens.push(...sshHosts);
|
|
1818
|
+
}
|
|
1819
|
+
if (firstToken && import_path7.default.posix.isAbsolute(firstToken)) {
|
|
1820
|
+
const prov = checkProvenance(firstToken, cwd);
|
|
1821
|
+
if (prov.trustLevel === "suspect") {
|
|
1822
|
+
return {
|
|
1823
|
+
decision: config.settings.mode === "strict" ? "block" : "review",
|
|
1824
|
+
blockedByLabel: "Node9: Suspect Binary",
|
|
1825
|
+
reason: `Binary "${firstToken}" resolved to ${prov.resolvedPath} \u2014 ${prov.reason}`,
|
|
1826
|
+
tier: 3
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
if (prov.trustLevel === "unknown" && config.settings.mode === "strict") {
|
|
1830
|
+
return {
|
|
1831
|
+
decision: "review",
|
|
1832
|
+
blockedByLabel: "Node9: Unknown Binary (strict mode)",
|
|
1833
|
+
reason: `Binary "${firstToken}" \u2014 ${prov.reason}`,
|
|
1834
|
+
tier: 3
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1362
1838
|
if (isSqlTool(toolName, config.policy.toolInspection)) {
|
|
1363
1839
|
allTokens = allTokens.filter((t) => !SQL_DML_KEYWORDS.has(t.toLowerCase()));
|
|
1364
1840
|
}
|
|
1365
1841
|
} else {
|
|
1366
|
-
allTokens =
|
|
1842
|
+
allTokens = tokenize2(toolName);
|
|
1367
1843
|
if (args && typeof args === "object") {
|
|
1368
1844
|
const flattenedArgs = JSON.stringify(args).toLowerCase();
|
|
1369
1845
|
const extraTokens = flattenedArgs.split(/[^a-zA-Z0-9]+/).filter((t) => t.length > 1);
|
|
@@ -1437,9 +1913,9 @@ async function evaluatePolicy(toolName, args, agent) {
|
|
|
1437
1913
|
}
|
|
1438
1914
|
async function explainPolicy(toolName, args) {
|
|
1439
1915
|
const steps = [];
|
|
1440
|
-
const globalPath =
|
|
1441
|
-
const projectPath =
|
|
1442
|
-
const credsPath =
|
|
1916
|
+
const globalPath = import_path7.default.join(import_os5.default.homedir(), ".node9", "config.json");
|
|
1917
|
+
const projectPath = import_path7.default.join(process.cwd(), "node9.config.json");
|
|
1918
|
+
const credsPath = import_path7.default.join(import_os5.default.homedir(), ".node9", "credentials.json");
|
|
1443
1919
|
const waterfall = [
|
|
1444
1920
|
{
|
|
1445
1921
|
tier: 1,
|
|
@@ -1450,19 +1926,19 @@ async function explainPolicy(toolName, args) {
|
|
|
1450
1926
|
{
|
|
1451
1927
|
tier: 2,
|
|
1452
1928
|
label: "Cloud policy",
|
|
1453
|
-
status:
|
|
1454
|
-
note:
|
|
1929
|
+
status: import_fs6.default.existsSync(credsPath) ? "active" : "missing",
|
|
1930
|
+
note: import_fs6.default.existsSync(credsPath) ? "credentials found (not evaluated in explain mode)" : "not connected \u2014 run: node9 login"
|
|
1455
1931
|
},
|
|
1456
1932
|
{
|
|
1457
1933
|
tier: 3,
|
|
1458
1934
|
label: "Project config",
|
|
1459
|
-
status:
|
|
1935
|
+
status: import_fs6.default.existsSync(projectPath) ? "active" : "missing",
|
|
1460
1936
|
path: projectPath
|
|
1461
1937
|
},
|
|
1462
1938
|
{
|
|
1463
1939
|
tier: 4,
|
|
1464
1940
|
label: "Global config",
|
|
1465
|
-
status:
|
|
1941
|
+
status: import_fs6.default.existsSync(globalPath) ? "active" : "missing",
|
|
1466
1942
|
path: globalPath
|
|
1467
1943
|
},
|
|
1468
1944
|
{
|
|
@@ -1594,7 +2070,7 @@ async function explainPolicy(toolName, args) {
|
|
|
1594
2070
|
});
|
|
1595
2071
|
}
|
|
1596
2072
|
} else {
|
|
1597
|
-
allTokens =
|
|
2073
|
+
allTokens = tokenize2(toolName);
|
|
1598
2074
|
let detail = `No toolInspection match for "${toolName}" \u2014 tokens: [${allTokens.join(", ")}]`;
|
|
1599
2075
|
if (args && typeof args === "object") {
|
|
1600
2076
|
const flattenedArgs = JSON.stringify(args).toLowerCase();
|
|
@@ -1699,18 +2175,21 @@ function isIgnoredTool(toolName) {
|
|
|
1699
2175
|
const config = getConfig();
|
|
1700
2176
|
return matchesPattern(toolName, config.policy.ignoredTools);
|
|
1701
2177
|
}
|
|
1702
|
-
var
|
|
2178
|
+
var import_fs6, import_path7, import_os5, import_picomatch, import_sh_syntax, SQL_DML_KEYWORDS;
|
|
1703
2179
|
var init_policy = __esm({
|
|
1704
2180
|
"src/policy/index.ts"() {
|
|
1705
2181
|
"use strict";
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
2182
|
+
import_fs6 = __toESM(require("fs"));
|
|
2183
|
+
import_path7 = __toESM(require("path"));
|
|
2184
|
+
import_os5 = __toESM(require("os"));
|
|
1709
2185
|
import_picomatch = __toESM(require("picomatch"));
|
|
1710
2186
|
import_sh_syntax = require("sh-syntax");
|
|
1711
2187
|
init_dlp();
|
|
1712
2188
|
init_config();
|
|
1713
2189
|
init_regex();
|
|
2190
|
+
init_provenance();
|
|
2191
|
+
init_pipe_chain();
|
|
2192
|
+
init_ssh_parser();
|
|
1714
2193
|
SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
|
|
1715
2194
|
}
|
|
1716
2195
|
});
|
|
@@ -1718,11 +2197,11 @@ var init_policy = __esm({
|
|
|
1718
2197
|
// src/auth/state.ts
|
|
1719
2198
|
function checkPause() {
|
|
1720
2199
|
try {
|
|
1721
|
-
if (!
|
|
1722
|
-
const state = JSON.parse(
|
|
2200
|
+
if (!import_fs7.default.existsSync(PAUSED_FILE)) return { paused: false };
|
|
2201
|
+
const state = JSON.parse(import_fs7.default.readFileSync(PAUSED_FILE, "utf-8"));
|
|
1723
2202
|
if (state.expiry > 0 && Date.now() >= state.expiry) {
|
|
1724
2203
|
try {
|
|
1725
|
-
|
|
2204
|
+
import_fs7.default.unlinkSync(PAUSED_FILE);
|
|
1726
2205
|
} catch {
|
|
1727
2206
|
}
|
|
1728
2207
|
return { paused: false };
|
|
@@ -1733,11 +2212,11 @@ function checkPause() {
|
|
|
1733
2212
|
}
|
|
1734
2213
|
}
|
|
1735
2214
|
function atomicWriteSync(filePath, data, options) {
|
|
1736
|
-
const dir =
|
|
1737
|
-
if (!
|
|
1738
|
-
const tmpPath = `${filePath}.${
|
|
1739
|
-
|
|
1740
|
-
|
|
2215
|
+
const dir = import_path8.default.dirname(filePath);
|
|
2216
|
+
if (!import_fs7.default.existsSync(dir)) import_fs7.default.mkdirSync(dir, { recursive: true });
|
|
2217
|
+
const tmpPath = `${filePath}.${import_os6.default.hostname()}.${process.pid}.tmp`;
|
|
2218
|
+
import_fs7.default.writeFileSync(tmpPath, data, options);
|
|
2219
|
+
import_fs7.default.renameSync(tmpPath, filePath);
|
|
1741
2220
|
}
|
|
1742
2221
|
function pauseNode9(durationMs, durationStr) {
|
|
1743
2222
|
const state = { expiry: Date.now() + durationMs, duration: durationStr };
|
|
@@ -1745,18 +2224,18 @@ function pauseNode9(durationMs, durationStr) {
|
|
|
1745
2224
|
}
|
|
1746
2225
|
function resumeNode9() {
|
|
1747
2226
|
try {
|
|
1748
|
-
if (
|
|
2227
|
+
if (import_fs7.default.existsSync(PAUSED_FILE)) import_fs7.default.unlinkSync(PAUSED_FILE);
|
|
1749
2228
|
} catch {
|
|
1750
2229
|
}
|
|
1751
2230
|
}
|
|
1752
2231
|
function getActiveTrustSession(toolName) {
|
|
1753
2232
|
try {
|
|
1754
|
-
if (!
|
|
1755
|
-
const trust = JSON.parse(
|
|
2233
|
+
if (!import_fs7.default.existsSync(TRUST_FILE)) return false;
|
|
2234
|
+
const trust = JSON.parse(import_fs7.default.readFileSync(TRUST_FILE, "utf-8"));
|
|
1756
2235
|
const now = Date.now();
|
|
1757
2236
|
const active = trust.entries.filter((e) => e.expiry > now);
|
|
1758
2237
|
if (active.length !== trust.entries.length) {
|
|
1759
|
-
|
|
2238
|
+
import_fs7.default.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
|
|
1760
2239
|
}
|
|
1761
2240
|
return active.some((e) => e.tool === toolName || matchesPattern(toolName, e.tool));
|
|
1762
2241
|
} catch {
|
|
@@ -1767,8 +2246,8 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
1767
2246
|
try {
|
|
1768
2247
|
let trust = { entries: [] };
|
|
1769
2248
|
try {
|
|
1770
|
-
if (
|
|
1771
|
-
trust = JSON.parse(
|
|
2249
|
+
if (import_fs7.default.existsSync(TRUST_FILE)) {
|
|
2250
|
+
trust = JSON.parse(import_fs7.default.readFileSync(TRUST_FILE, "utf-8"));
|
|
1772
2251
|
}
|
|
1773
2252
|
} catch {
|
|
1774
2253
|
}
|
|
@@ -1784,34 +2263,34 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
1784
2263
|
}
|
|
1785
2264
|
function getPersistentDecision(toolName) {
|
|
1786
2265
|
try {
|
|
1787
|
-
const file =
|
|
1788
|
-
if (!
|
|
1789
|
-
const decisions = JSON.parse(
|
|
2266
|
+
const file = import_path8.default.join(import_os6.default.homedir(), ".node9", "decisions.json");
|
|
2267
|
+
if (!import_fs7.default.existsSync(file)) return null;
|
|
2268
|
+
const decisions = JSON.parse(import_fs7.default.readFileSync(file, "utf-8"));
|
|
1790
2269
|
const d = decisions[toolName];
|
|
1791
2270
|
if (d === "allow" || d === "deny") return d;
|
|
1792
2271
|
} catch {
|
|
1793
2272
|
}
|
|
1794
2273
|
return null;
|
|
1795
2274
|
}
|
|
1796
|
-
var
|
|
2275
|
+
var import_fs7, import_path8, import_os6, PAUSED_FILE, TRUST_FILE;
|
|
1797
2276
|
var init_state = __esm({
|
|
1798
2277
|
"src/auth/state.ts"() {
|
|
1799
2278
|
"use strict";
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
2279
|
+
import_fs7 = __toESM(require("fs"));
|
|
2280
|
+
import_path8 = __toESM(require("path"));
|
|
2281
|
+
import_os6 = __toESM(require("os"));
|
|
1803
2282
|
init_policy();
|
|
1804
|
-
PAUSED_FILE =
|
|
1805
|
-
TRUST_FILE =
|
|
2283
|
+
PAUSED_FILE = import_path8.default.join(import_os6.default.homedir(), ".node9", "PAUSED");
|
|
2284
|
+
TRUST_FILE = import_path8.default.join(import_os6.default.homedir(), ".node9", "trust.json");
|
|
1806
2285
|
}
|
|
1807
2286
|
});
|
|
1808
2287
|
|
|
1809
2288
|
// src/auth/daemon.ts
|
|
1810
2289
|
function getInternalToken() {
|
|
1811
2290
|
try {
|
|
1812
|
-
const pidFile =
|
|
1813
|
-
if (!
|
|
1814
|
-
const data = JSON.parse(
|
|
2291
|
+
const pidFile = import_path9.default.join(import_os7.default.homedir(), ".node9", "daemon.pid");
|
|
2292
|
+
if (!import_fs8.default.existsSync(pidFile)) return null;
|
|
2293
|
+
const data = JSON.parse(import_fs8.default.readFileSync(pidFile, "utf-8"));
|
|
1815
2294
|
process.kill(data.pid, 0);
|
|
1816
2295
|
return data.internalToken ?? null;
|
|
1817
2296
|
} catch {
|
|
@@ -1819,10 +2298,10 @@ function getInternalToken() {
|
|
|
1819
2298
|
}
|
|
1820
2299
|
}
|
|
1821
2300
|
function isDaemonRunning() {
|
|
1822
|
-
const pidFile =
|
|
1823
|
-
if (
|
|
2301
|
+
const pidFile = import_path9.default.join(import_os7.default.homedir(), ".node9", "daemon.pid");
|
|
2302
|
+
if (import_fs8.default.existsSync(pidFile)) {
|
|
1824
2303
|
try {
|
|
1825
|
-
const { pid, port } = JSON.parse(
|
|
2304
|
+
const { pid, port } = JSON.parse(import_fs8.default.readFileSync(pidFile, "utf-8"));
|
|
1826
2305
|
if (port !== DAEMON_PORT) return false;
|
|
1827
2306
|
process.kill(pid, 0);
|
|
1828
2307
|
return true;
|
|
@@ -1915,13 +2394,13 @@ async function resolveViaDaemon(id, decision, internalToken) {
|
|
|
1915
2394
|
signal: AbortSignal.timeout(3e3)
|
|
1916
2395
|
});
|
|
1917
2396
|
}
|
|
1918
|
-
var
|
|
2397
|
+
var import_fs8, import_path9, import_os7, import_child_process, DAEMON_PORT, DAEMON_HOST;
|
|
1919
2398
|
var init_daemon = __esm({
|
|
1920
2399
|
"src/auth/daemon.ts"() {
|
|
1921
2400
|
"use strict";
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
2401
|
+
import_fs8 = __toESM(require("fs"));
|
|
2402
|
+
import_path9 = __toESM(require("path"));
|
|
2403
|
+
import_os7 = __toESM(require("os"));
|
|
1925
2404
|
import_child_process = require("child_process");
|
|
1926
2405
|
DAEMON_PORT = 7391;
|
|
1927
2406
|
DAEMON_HOST = "127.0.0.1";
|
|
@@ -1980,7 +2459,7 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
|
|
|
1980
2459
|
intent = "EDIT";
|
|
1981
2460
|
if (obj.file_path) {
|
|
1982
2461
|
editFilePath = String(obj.file_path);
|
|
1983
|
-
editFileName =
|
|
2462
|
+
editFileName = import_path10.default.basename(editFilePath);
|
|
1984
2463
|
}
|
|
1985
2464
|
const result = extractContext(String(obj.new_string), matchedWord);
|
|
1986
2465
|
contextSnippet = result.snippet;
|
|
@@ -2012,11 +2491,11 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
|
|
|
2012
2491
|
...ruleName && { ruleName }
|
|
2013
2492
|
};
|
|
2014
2493
|
}
|
|
2015
|
-
var
|
|
2494
|
+
var import_path10, CODE_KEYS;
|
|
2016
2495
|
var init_context_sniper = __esm({
|
|
2017
2496
|
"src/context-sniper.ts"() {
|
|
2018
2497
|
"use strict";
|
|
2019
|
-
|
|
2498
|
+
import_path10 = __toESM(require("path"));
|
|
2020
2499
|
CODE_KEYS = [
|
|
2021
2500
|
"command",
|
|
2022
2501
|
"cmd",
|
|
@@ -2055,7 +2534,7 @@ function formatArgs(args, matchedField, matchedWord) {
|
|
|
2055
2534
|
if (typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2056
2535
|
const obj = parsed;
|
|
2057
2536
|
if (obj.old_string !== void 0 && obj.new_string !== void 0) {
|
|
2058
|
-
const file = obj.file_path ?
|
|
2537
|
+
const file = obj.file_path ? import_path11.default.basename(String(obj.file_path)) : "file";
|
|
2059
2538
|
const oldPreview = smartTruncate(String(obj.old_string), 120);
|
|
2060
2539
|
const newPreview = extractContext(String(obj.new_string), matchedWord).snippet;
|
|
2061
2540
|
return {
|
|
@@ -2228,12 +2707,12 @@ end run`;
|
|
|
2228
2707
|
}
|
|
2229
2708
|
});
|
|
2230
2709
|
}
|
|
2231
|
-
var import_child_process2,
|
|
2710
|
+
var import_child_process2, import_path11, isTestEnv;
|
|
2232
2711
|
var init_native = __esm({
|
|
2233
2712
|
"src/ui/native.ts"() {
|
|
2234
2713
|
"use strict";
|
|
2235
2714
|
import_child_process2 = require("child_process");
|
|
2236
|
-
|
|
2715
|
+
import_path11 = __toESM(require("path"));
|
|
2237
2716
|
init_context_sniper();
|
|
2238
2717
|
isTestEnv = () => {
|
|
2239
2718
|
return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || !!process.env.VITEST || process.env.CI === "true" || !!process.env.CI || process.env.NODE9_TESTING === "1";
|
|
@@ -2253,9 +2732,9 @@ function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
|
|
|
2253
2732
|
context: {
|
|
2254
2733
|
agent: meta?.agent,
|
|
2255
2734
|
mcpServer: meta?.mcpServer,
|
|
2256
|
-
hostname:
|
|
2735
|
+
hostname: import_os8.default.hostname(),
|
|
2257
2736
|
cwd: process.cwd(),
|
|
2258
|
-
platform:
|
|
2737
|
+
platform: import_os8.default.platform()
|
|
2259
2738
|
}
|
|
2260
2739
|
}),
|
|
2261
2740
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -2276,9 +2755,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
|
|
|
2276
2755
|
context: {
|
|
2277
2756
|
agent: meta?.agent,
|
|
2278
2757
|
mcpServer: meta?.mcpServer,
|
|
2279
|
-
hostname:
|
|
2758
|
+
hostname: import_os8.default.hostname(),
|
|
2280
2759
|
cwd: process.cwd(),
|
|
2281
|
-
platform:
|
|
2760
|
+
platform: import_os8.default.platform()
|
|
2282
2761
|
},
|
|
2283
2762
|
...riskMetadata && { riskMetadata }
|
|
2284
2763
|
}),
|
|
@@ -2334,26 +2813,26 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
2334
2813
|
});
|
|
2335
2814
|
clearTimeout(timer);
|
|
2336
2815
|
if (!res.ok) {
|
|
2337
|
-
|
|
2816
|
+
import_fs9.default.appendFileSync(
|
|
2338
2817
|
HOOK_DEBUG_LOG,
|
|
2339
2818
|
`[resolve-cloud] PATCH ${resolveUrl} \u2192 HTTP ${res.status}
|
|
2340
2819
|
`
|
|
2341
2820
|
);
|
|
2342
2821
|
}
|
|
2343
2822
|
} catch (err) {
|
|
2344
|
-
|
|
2823
|
+
import_fs9.default.appendFileSync(
|
|
2345
2824
|
HOOK_DEBUG_LOG,
|
|
2346
2825
|
`[resolve-cloud] PATCH failed for ${requestId}: ${err.message}
|
|
2347
2826
|
`
|
|
2348
2827
|
);
|
|
2349
2828
|
}
|
|
2350
2829
|
}
|
|
2351
|
-
var
|
|
2830
|
+
var import_fs9, import_os8;
|
|
2352
2831
|
var init_cloud = __esm({
|
|
2353
2832
|
"src/auth/cloud.ts"() {
|
|
2354
2833
|
"use strict";
|
|
2355
|
-
|
|
2356
|
-
|
|
2834
|
+
import_fs9 = __toESM(require("fs"));
|
|
2835
|
+
import_os8 = __toESM(require("os"));
|
|
2357
2836
|
init_audit();
|
|
2358
2837
|
}
|
|
2359
2838
|
});
|
|
@@ -2440,7 +2919,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2440
2919
|
}
|
|
2441
2920
|
if (config.settings.mode === "audit") {
|
|
2442
2921
|
if (!isIgnoredTool(toolName)) {
|
|
2443
|
-
const policyResult = await evaluatePolicy(toolName, args, meta?.agent);
|
|
2922
|
+
const policyResult = await evaluatePolicy(toolName, args, meta?.agent, options?.cwd);
|
|
2444
2923
|
if (policyResult.decision === "review") {
|
|
2445
2924
|
appendLocalAudit(toolName, args, "allow", "audit-mode", meta);
|
|
2446
2925
|
if (approvers.cloud && creds?.apiKey) {
|
|
@@ -2705,13 +3184,13 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
2705
3184
|
}
|
|
2706
3185
|
return finalResult;
|
|
2707
3186
|
}
|
|
2708
|
-
var import_net,
|
|
3187
|
+
var import_net, import_path12, import_os9, import_crypto2, ACTIVITY_SOCKET_PATH;
|
|
2709
3188
|
var init_orchestrator = __esm({
|
|
2710
3189
|
"src/auth/orchestrator.ts"() {
|
|
2711
3190
|
"use strict";
|
|
2712
3191
|
import_net = __toESM(require("net"));
|
|
2713
|
-
|
|
2714
|
-
|
|
3192
|
+
import_path12 = __toESM(require("path"));
|
|
3193
|
+
import_os9 = __toESM(require("os"));
|
|
2715
3194
|
import_crypto2 = require("crypto");
|
|
2716
3195
|
init_native();
|
|
2717
3196
|
init_context_sniper();
|
|
@@ -2722,7 +3201,7 @@ var init_orchestrator = __esm({
|
|
|
2722
3201
|
init_state();
|
|
2723
3202
|
init_daemon();
|
|
2724
3203
|
init_cloud();
|
|
2725
|
-
ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
3204
|
+
ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path12.default.join(import_os9.default.tmpdir(), "node9-activity.sock");
|
|
2726
3205
|
}
|
|
2727
3206
|
});
|
|
2728
3207
|
|
|
@@ -4215,11 +4694,11 @@ function markRejectionHandlerRegistered() {
|
|
|
4215
4694
|
daemonRejectionHandlerRegistered = true;
|
|
4216
4695
|
}
|
|
4217
4696
|
function atomicWriteSync2(filePath, data, options) {
|
|
4218
|
-
const dir =
|
|
4219
|
-
if (!
|
|
4697
|
+
const dir = import_path14.default.dirname(filePath);
|
|
4698
|
+
if (!import_fs11.default.existsSync(dir)) import_fs11.default.mkdirSync(dir, { recursive: true });
|
|
4220
4699
|
const tmpPath = `${filePath}.${(0, import_crypto3.randomUUID)()}.tmp`;
|
|
4221
|
-
|
|
4222
|
-
|
|
4700
|
+
import_fs11.default.writeFileSync(tmpPath, data, options);
|
|
4701
|
+
import_fs11.default.renameSync(tmpPath, filePath);
|
|
4223
4702
|
}
|
|
4224
4703
|
function redactArgs(value) {
|
|
4225
4704
|
if (!value || typeof value !== "object") return value;
|
|
@@ -4239,16 +4718,16 @@ function appendAuditLog(data) {
|
|
|
4239
4718
|
decision: data.decision,
|
|
4240
4719
|
source: "daemon"
|
|
4241
4720
|
};
|
|
4242
|
-
const dir =
|
|
4243
|
-
if (!
|
|
4244
|
-
|
|
4721
|
+
const dir = import_path14.default.dirname(AUDIT_LOG_FILE);
|
|
4722
|
+
if (!import_fs11.default.existsSync(dir)) import_fs11.default.mkdirSync(dir, { recursive: true });
|
|
4723
|
+
import_fs11.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
4245
4724
|
} catch {
|
|
4246
4725
|
}
|
|
4247
4726
|
}
|
|
4248
4727
|
function getAuditHistory(limit = 20) {
|
|
4249
4728
|
try {
|
|
4250
|
-
if (!
|
|
4251
|
-
const lines =
|
|
4729
|
+
if (!import_fs11.default.existsSync(AUDIT_LOG_FILE)) return [];
|
|
4730
|
+
const lines = import_fs11.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
4252
4731
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
4253
4732
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
4254
4733
|
} catch {
|
|
@@ -4257,19 +4736,19 @@ function getAuditHistory(limit = 20) {
|
|
|
4257
4736
|
}
|
|
4258
4737
|
function getOrgName() {
|
|
4259
4738
|
try {
|
|
4260
|
-
if (
|
|
4739
|
+
if (import_fs11.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
4261
4740
|
} catch {
|
|
4262
4741
|
}
|
|
4263
4742
|
return null;
|
|
4264
4743
|
}
|
|
4265
4744
|
function hasStoredSlackKey() {
|
|
4266
|
-
return
|
|
4745
|
+
return import_fs11.default.existsSync(CREDENTIALS_FILE);
|
|
4267
4746
|
}
|
|
4268
4747
|
function writeGlobalSetting(key, value) {
|
|
4269
4748
|
let config = {};
|
|
4270
4749
|
try {
|
|
4271
|
-
if (
|
|
4272
|
-
config = JSON.parse(
|
|
4750
|
+
if (import_fs11.default.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
4751
|
+
config = JSON.parse(import_fs11.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
4273
4752
|
}
|
|
4274
4753
|
} catch {
|
|
4275
4754
|
}
|
|
@@ -4281,8 +4760,8 @@ function writeTrustEntry(toolName, durationMs) {
|
|
|
4281
4760
|
try {
|
|
4282
4761
|
let trust = { entries: [] };
|
|
4283
4762
|
try {
|
|
4284
|
-
if (
|
|
4285
|
-
trust = JSON.parse(
|
|
4763
|
+
if (import_fs11.default.existsSync(TRUST_FILE2))
|
|
4764
|
+
trust = JSON.parse(import_fs11.default.readFileSync(TRUST_FILE2, "utf-8"));
|
|
4286
4765
|
} catch {
|
|
4287
4766
|
}
|
|
4288
4767
|
trust.entries = trust.entries.filter((e) => e.tool !== toolName && e.expiry > Date.now());
|
|
@@ -4293,8 +4772,8 @@ function writeTrustEntry(toolName, durationMs) {
|
|
|
4293
4772
|
}
|
|
4294
4773
|
function readPersistentDecisions() {
|
|
4295
4774
|
try {
|
|
4296
|
-
if (
|
|
4297
|
-
return JSON.parse(
|
|
4775
|
+
if (import_fs11.default.existsSync(DECISIONS_FILE)) {
|
|
4776
|
+
return JSON.parse(import_fs11.default.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
4298
4777
|
}
|
|
4299
4778
|
} catch {
|
|
4300
4779
|
}
|
|
@@ -4359,7 +4838,7 @@ function abandonPending() {
|
|
|
4359
4838
|
});
|
|
4360
4839
|
if (autoStarted) {
|
|
4361
4840
|
try {
|
|
4362
|
-
|
|
4841
|
+
import_fs11.default.unlinkSync(DAEMON_PID_FILE);
|
|
4363
4842
|
} catch {
|
|
4364
4843
|
}
|
|
4365
4844
|
setTimeout(() => {
|
|
@@ -4370,7 +4849,7 @@ function abandonPending() {
|
|
|
4370
4849
|
}
|
|
4371
4850
|
function startActivitySocket() {
|
|
4372
4851
|
try {
|
|
4373
|
-
|
|
4852
|
+
import_fs11.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
4374
4853
|
} catch {
|
|
4375
4854
|
}
|
|
4376
4855
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
@@ -4412,29 +4891,29 @@ function startActivitySocket() {
|
|
|
4412
4891
|
unixServer.listen(ACTIVITY_SOCKET_PATH2);
|
|
4413
4892
|
process.on("exit", () => {
|
|
4414
4893
|
try {
|
|
4415
|
-
|
|
4894
|
+
import_fs11.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
4416
4895
|
} catch {
|
|
4417
4896
|
}
|
|
4418
4897
|
});
|
|
4419
4898
|
}
|
|
4420
|
-
var import_net2,
|
|
4899
|
+
var import_net2, import_fs11, import_path14, import_os11, import_child_process3, import_crypto3, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, pending, sseClients, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, SECRET_KEY_RE;
|
|
4421
4900
|
var init_state2 = __esm({
|
|
4422
4901
|
"src/daemon/state.ts"() {
|
|
4423
4902
|
"use strict";
|
|
4424
4903
|
import_net2 = __toESM(require("net"));
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4904
|
+
import_fs11 = __toESM(require("fs"));
|
|
4905
|
+
import_path14 = __toESM(require("path"));
|
|
4906
|
+
import_os11 = __toESM(require("os"));
|
|
4428
4907
|
import_child_process3 = require("child_process");
|
|
4429
4908
|
import_crypto3 = require("crypto");
|
|
4430
4909
|
init_daemon();
|
|
4431
|
-
homeDir =
|
|
4432
|
-
DAEMON_PID_FILE =
|
|
4433
|
-
DECISIONS_FILE =
|
|
4434
|
-
AUDIT_LOG_FILE =
|
|
4435
|
-
TRUST_FILE2 =
|
|
4436
|
-
GLOBAL_CONFIG_FILE =
|
|
4437
|
-
CREDENTIALS_FILE =
|
|
4910
|
+
homeDir = import_os11.default.homedir();
|
|
4911
|
+
DAEMON_PID_FILE = import_path14.default.join(homeDir, ".node9", "daemon.pid");
|
|
4912
|
+
DECISIONS_FILE = import_path14.default.join(homeDir, ".node9", "decisions.json");
|
|
4913
|
+
AUDIT_LOG_FILE = import_path14.default.join(homeDir, ".node9", "audit.log");
|
|
4914
|
+
TRUST_FILE2 = import_path14.default.join(homeDir, ".node9", "trust.json");
|
|
4915
|
+
GLOBAL_CONFIG_FILE = import_path14.default.join(homeDir, ".node9", "config.json");
|
|
4916
|
+
CREDENTIALS_FILE = import_path14.default.join(homeDir, ".node9", "credentials.json");
|
|
4438
4917
|
pending = /* @__PURE__ */ new Map();
|
|
4439
4918
|
sseClients = /* @__PURE__ */ new Set();
|
|
4440
4919
|
_abandonTimer = null;
|
|
@@ -4448,7 +4927,7 @@ var init_state2 = __esm({
|
|
|
4448
4927
|
"2h": 2 * 60 * 6e4
|
|
4449
4928
|
};
|
|
4450
4929
|
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
4451
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
4930
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path14.default.join(import_os11.default.tmpdir(), "node9-activity.sock");
|
|
4452
4931
|
ACTIVITY_RING_SIZE = 100;
|
|
4453
4932
|
activityRing = [];
|
|
4454
4933
|
SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
|
|
@@ -4471,7 +4950,7 @@ function startDaemon() {
|
|
|
4471
4950
|
idleTimer = setTimeout(() => {
|
|
4472
4951
|
if (autoStarted) {
|
|
4473
4952
|
try {
|
|
4474
|
-
|
|
4953
|
+
import_fs12.default.unlinkSync(DAEMON_PID_FILE);
|
|
4475
4954
|
} catch {
|
|
4476
4955
|
}
|
|
4477
4956
|
}
|
|
@@ -4613,7 +5092,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
4613
5092
|
status: "pending"
|
|
4614
5093
|
});
|
|
4615
5094
|
}
|
|
4616
|
-
const projectCwd = typeof cwd === "string" &&
|
|
5095
|
+
const projectCwd = typeof cwd === "string" && import_path15.default.isAbsolute(cwd) ? cwd : void 0;
|
|
4617
5096
|
const projectConfig = getConfig(projectCwd);
|
|
4618
5097
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
4619
5098
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -4917,14 +5396,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
4917
5396
|
server.on("error", (e) => {
|
|
4918
5397
|
if (e.code === "EADDRINUSE") {
|
|
4919
5398
|
try {
|
|
4920
|
-
if (
|
|
4921
|
-
const { pid } = JSON.parse(
|
|
5399
|
+
if (import_fs12.default.existsSync(DAEMON_PID_FILE)) {
|
|
5400
|
+
const { pid } = JSON.parse(import_fs12.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
4922
5401
|
process.kill(pid, 0);
|
|
4923
5402
|
return process.exit(0);
|
|
4924
5403
|
}
|
|
4925
5404
|
} catch {
|
|
4926
5405
|
try {
|
|
4927
|
-
|
|
5406
|
+
import_fs12.default.unlinkSync(DAEMON_PID_FILE);
|
|
4928
5407
|
} catch {
|
|
4929
5408
|
}
|
|
4930
5409
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -4983,13 +5462,13 @@ data: ${JSON.stringify(item.data)}
|
|
|
4983
5462
|
}
|
|
4984
5463
|
startActivitySocket();
|
|
4985
5464
|
}
|
|
4986
|
-
var import_http,
|
|
5465
|
+
var import_http, import_fs12, import_path15, import_crypto4, import_child_process4, import_chalk2;
|
|
4987
5466
|
var init_server = __esm({
|
|
4988
5467
|
"src/daemon/server.ts"() {
|
|
4989
5468
|
"use strict";
|
|
4990
5469
|
import_http = __toESM(require("http"));
|
|
4991
|
-
|
|
4992
|
-
|
|
5470
|
+
import_fs12 = __toESM(require("fs"));
|
|
5471
|
+
import_path15 = __toESM(require("path"));
|
|
4993
5472
|
import_crypto4 = require("crypto");
|
|
4994
5473
|
import_child_process4 = require("child_process");
|
|
4995
5474
|
import_chalk2 = __toESM(require("chalk"));
|
|
@@ -5002,24 +5481,24 @@ var init_server = __esm({
|
|
|
5002
5481
|
|
|
5003
5482
|
// src/daemon/index.ts
|
|
5004
5483
|
function stopDaemon() {
|
|
5005
|
-
if (!
|
|
5484
|
+
if (!import_fs13.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk3.default.yellow("Not running."));
|
|
5006
5485
|
try {
|
|
5007
|
-
const { pid } = JSON.parse(
|
|
5486
|
+
const { pid } = JSON.parse(import_fs13.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
5008
5487
|
process.kill(pid, "SIGTERM");
|
|
5009
5488
|
console.log(import_chalk3.default.green("\u2705 Stopped."));
|
|
5010
5489
|
} catch {
|
|
5011
5490
|
console.log(import_chalk3.default.gray("Cleaned up stale PID file."));
|
|
5012
5491
|
} finally {
|
|
5013
5492
|
try {
|
|
5014
|
-
|
|
5493
|
+
import_fs13.default.unlinkSync(DAEMON_PID_FILE);
|
|
5015
5494
|
} catch {
|
|
5016
5495
|
}
|
|
5017
5496
|
}
|
|
5018
5497
|
}
|
|
5019
5498
|
function daemonStatus() {
|
|
5020
|
-
if (
|
|
5499
|
+
if (import_fs13.default.existsSync(DAEMON_PID_FILE)) {
|
|
5021
5500
|
try {
|
|
5022
|
-
const { pid } = JSON.parse(
|
|
5501
|
+
const { pid } = JSON.parse(import_fs13.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
5023
5502
|
process.kill(pid, 0);
|
|
5024
5503
|
console.log(import_chalk3.default.green("Node9 daemon: running"));
|
|
5025
5504
|
return;
|
|
@@ -5038,11 +5517,11 @@ function daemonStatus() {
|
|
|
5038
5517
|
console.log(import_chalk3.default.yellow("Node9 daemon: not running"));
|
|
5039
5518
|
}
|
|
5040
5519
|
}
|
|
5041
|
-
var
|
|
5520
|
+
var import_fs13, import_chalk3, import_child_process5;
|
|
5042
5521
|
var init_daemon2 = __esm({
|
|
5043
5522
|
"src/daemon/index.ts"() {
|
|
5044
5523
|
"use strict";
|
|
5045
|
-
|
|
5524
|
+
import_fs13 = __toESM(require("fs"));
|
|
5046
5525
|
import_chalk3 = __toESM(require("chalk"));
|
|
5047
5526
|
import_child_process5 = require("child_process");
|
|
5048
5527
|
init_server();
|
|
@@ -5093,9 +5572,9 @@ function renderPending(activity) {
|
|
|
5093
5572
|
}
|
|
5094
5573
|
async function ensureDaemon() {
|
|
5095
5574
|
let pidPort = null;
|
|
5096
|
-
if (
|
|
5575
|
+
if (import_fs20.default.existsSync(PID_FILE)) {
|
|
5097
5576
|
try {
|
|
5098
|
-
const { port } = JSON.parse(
|
|
5577
|
+
const { port } = JSON.parse(import_fs20.default.readFileSync(PID_FILE, "utf-8"));
|
|
5099
5578
|
pidPort = port;
|
|
5100
5579
|
} catch {
|
|
5101
5580
|
console.error(import_chalk14.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -5260,8 +5739,8 @@ async function startTail(options = {}) {
|
|
|
5260
5739
|
process.stdout.write(SHOW_CURSOR);
|
|
5261
5740
|
postDecisionHttp(req2.id, decision, csrfToken, port).catch((err) => {
|
|
5262
5741
|
try {
|
|
5263
|
-
|
|
5264
|
-
|
|
5742
|
+
import_fs20.default.appendFileSync(
|
|
5743
|
+
import_path22.default.join(import_os18.default.homedir(), ".node9", "hook-debug.log"),
|
|
5265
5744
|
`[tail] POST /decision failed: ${String(err)}
|
|
5266
5745
|
`
|
|
5267
5746
|
);
|
|
@@ -5446,20 +5925,20 @@ async function startTail(options = {}) {
|
|
|
5446
5925
|
process.exit(1);
|
|
5447
5926
|
});
|
|
5448
5927
|
}
|
|
5449
|
-
var import_http2, import_chalk14,
|
|
5928
|
+
var import_http2, import_chalk14, import_fs20, import_os18, import_path22, import_readline3, import_child_process13, PID_FILE, ICONS, RESET, BOLD, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, SAVE_CURSOR, RESTORE_CURSOR;
|
|
5450
5929
|
var init_tail = __esm({
|
|
5451
5930
|
"src/tui/tail.ts"() {
|
|
5452
5931
|
"use strict";
|
|
5453
5932
|
import_http2 = __toESM(require("http"));
|
|
5454
5933
|
import_chalk14 = __toESM(require("chalk"));
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5934
|
+
import_fs20 = __toESM(require("fs"));
|
|
5935
|
+
import_os18 = __toESM(require("os"));
|
|
5936
|
+
import_path22 = __toESM(require("path"));
|
|
5458
5937
|
import_readline3 = __toESM(require("readline"));
|
|
5459
5938
|
import_child_process13 = require("child_process");
|
|
5460
5939
|
init_daemon2();
|
|
5461
5940
|
init_core();
|
|
5462
|
-
PID_FILE =
|
|
5941
|
+
PID_FILE = import_path22.default.join(import_os18.default.homedir(), ".node9", "daemon.pid");
|
|
5463
5942
|
ICONS = {
|
|
5464
5943
|
bash: "\u{1F4BB}",
|
|
5465
5944
|
shell: "\u{1F4BB}",
|
|
@@ -5497,9 +5976,9 @@ var import_commander = require("commander");
|
|
|
5497
5976
|
init_core();
|
|
5498
5977
|
|
|
5499
5978
|
// src/setup.ts
|
|
5500
|
-
var
|
|
5501
|
-
var
|
|
5502
|
-
var
|
|
5979
|
+
var import_fs10 = __toESM(require("fs"));
|
|
5980
|
+
var import_path13 = __toESM(require("path"));
|
|
5981
|
+
var import_os10 = __toESM(require("os"));
|
|
5503
5982
|
var import_chalk = __toESM(require("chalk"));
|
|
5504
5983
|
var import_prompts = require("@inquirer/prompts");
|
|
5505
5984
|
function printDaemonTip() {
|
|
@@ -5516,26 +5995,26 @@ function fullPathCommand(subcommand) {
|
|
|
5516
5995
|
}
|
|
5517
5996
|
function readJson(filePath) {
|
|
5518
5997
|
try {
|
|
5519
|
-
if (
|
|
5520
|
-
return JSON.parse(
|
|
5998
|
+
if (import_fs10.default.existsSync(filePath)) {
|
|
5999
|
+
return JSON.parse(import_fs10.default.readFileSync(filePath, "utf-8"));
|
|
5521
6000
|
}
|
|
5522
6001
|
} catch {
|
|
5523
6002
|
}
|
|
5524
6003
|
return null;
|
|
5525
6004
|
}
|
|
5526
6005
|
function writeJson(filePath, data) {
|
|
5527
|
-
const dir =
|
|
5528
|
-
if (!
|
|
5529
|
-
|
|
6006
|
+
const dir = import_path13.default.dirname(filePath);
|
|
6007
|
+
if (!import_fs10.default.existsSync(dir)) import_fs10.default.mkdirSync(dir, { recursive: true });
|
|
6008
|
+
import_fs10.default.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
5530
6009
|
}
|
|
5531
6010
|
function isNode9Hook(cmd) {
|
|
5532
6011
|
if (!cmd) return false;
|
|
5533
6012
|
return /(?:^|[\s/\\])node9 (?:check|log)/.test(cmd) || /(?:^|[\s/\\])cli\.js (?:check|log)/.test(cmd);
|
|
5534
6013
|
}
|
|
5535
6014
|
function teardownClaude() {
|
|
5536
|
-
const homeDir2 =
|
|
5537
|
-
const hooksPath =
|
|
5538
|
-
const mcpPath =
|
|
6015
|
+
const homeDir2 = import_os10.default.homedir();
|
|
6016
|
+
const hooksPath = import_path13.default.join(homeDir2, ".claude", "settings.json");
|
|
6017
|
+
const mcpPath = import_path13.default.join(homeDir2, ".claude.json");
|
|
5539
6018
|
let changed = false;
|
|
5540
6019
|
const settings = readJson(hooksPath);
|
|
5541
6020
|
if (settings?.hooks) {
|
|
@@ -5583,8 +6062,8 @@ function teardownClaude() {
|
|
|
5583
6062
|
}
|
|
5584
6063
|
}
|
|
5585
6064
|
function teardownGemini() {
|
|
5586
|
-
const homeDir2 =
|
|
5587
|
-
const settingsPath =
|
|
6065
|
+
const homeDir2 = import_os10.default.homedir();
|
|
6066
|
+
const settingsPath = import_path13.default.join(homeDir2, ".gemini", "settings.json");
|
|
5588
6067
|
const settings = readJson(settingsPath);
|
|
5589
6068
|
if (!settings) {
|
|
5590
6069
|
console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.gemini/settings.json not found \u2014 nothing to remove"));
|
|
@@ -5622,8 +6101,8 @@ function teardownGemini() {
|
|
|
5622
6101
|
}
|
|
5623
6102
|
}
|
|
5624
6103
|
function teardownCursor() {
|
|
5625
|
-
const homeDir2 =
|
|
5626
|
-
const mcpPath =
|
|
6104
|
+
const homeDir2 = import_os10.default.homedir();
|
|
6105
|
+
const mcpPath = import_path13.default.join(homeDir2, ".cursor", "mcp.json");
|
|
5627
6106
|
const mcpConfig = readJson(mcpPath);
|
|
5628
6107
|
if (!mcpConfig?.mcpServers) {
|
|
5629
6108
|
console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.cursor/mcp.json not found \u2014 nothing to remove"));
|
|
@@ -5649,9 +6128,9 @@ function teardownCursor() {
|
|
|
5649
6128
|
}
|
|
5650
6129
|
}
|
|
5651
6130
|
async function setupClaude() {
|
|
5652
|
-
const homeDir2 =
|
|
5653
|
-
const mcpPath =
|
|
5654
|
-
const hooksPath =
|
|
6131
|
+
const homeDir2 = import_os10.default.homedir();
|
|
6132
|
+
const mcpPath = import_path13.default.join(homeDir2, ".claude.json");
|
|
6133
|
+
const hooksPath = import_path13.default.join(homeDir2, ".claude", "settings.json");
|
|
5655
6134
|
const claudeConfig = readJson(mcpPath) ?? {};
|
|
5656
6135
|
const settings = readJson(hooksPath) ?? {};
|
|
5657
6136
|
const servers = claudeConfig.mcpServers ?? {};
|
|
@@ -5725,8 +6204,8 @@ async function setupClaude() {
|
|
|
5725
6204
|
}
|
|
5726
6205
|
}
|
|
5727
6206
|
async function setupGemini() {
|
|
5728
|
-
const homeDir2 =
|
|
5729
|
-
const settingsPath =
|
|
6207
|
+
const homeDir2 = import_os10.default.homedir();
|
|
6208
|
+
const settingsPath = import_path13.default.join(homeDir2, ".gemini", "settings.json");
|
|
5730
6209
|
const settings = readJson(settingsPath) ?? {};
|
|
5731
6210
|
const servers = settings.mcpServers ?? {};
|
|
5732
6211
|
let anythingChanged = false;
|
|
@@ -5808,8 +6287,8 @@ async function setupGemini() {
|
|
|
5808
6287
|
}
|
|
5809
6288
|
}
|
|
5810
6289
|
async function setupCursor() {
|
|
5811
|
-
const homeDir2 =
|
|
5812
|
-
const mcpPath =
|
|
6290
|
+
const homeDir2 = import_os10.default.homedir();
|
|
6291
|
+
const mcpPath = import_path13.default.join(homeDir2, ".cursor", "mcp.json");
|
|
5813
6292
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
5814
6293
|
const servers = mcpConfig.mcpServers ?? {};
|
|
5815
6294
|
let anythingChanged = false;
|
|
@@ -5866,9 +6345,9 @@ async function setupCursor() {
|
|
|
5866
6345
|
// src/cli.ts
|
|
5867
6346
|
init_daemon2();
|
|
5868
6347
|
var import_chalk15 = __toESM(require("chalk"));
|
|
5869
|
-
var
|
|
5870
|
-
var
|
|
5871
|
-
var
|
|
6348
|
+
var import_fs21 = __toESM(require("fs"));
|
|
6349
|
+
var import_path23 = __toESM(require("path"));
|
|
6350
|
+
var import_os19 = __toESM(require("os"));
|
|
5872
6351
|
var import_prompts3 = require("@inquirer/prompts");
|
|
5873
6352
|
|
|
5874
6353
|
// src/utils/duration.ts
|
|
@@ -6089,9 +6568,9 @@ async function autoStartDaemonAndWait() {
|
|
|
6089
6568
|
|
|
6090
6569
|
// src/cli/commands/check.ts
|
|
6091
6570
|
var import_chalk5 = __toESM(require("chalk"));
|
|
6092
|
-
var
|
|
6093
|
-
var
|
|
6094
|
-
var
|
|
6571
|
+
var import_fs15 = __toESM(require("fs"));
|
|
6572
|
+
var import_path17 = __toESM(require("path"));
|
|
6573
|
+
var import_os13 = __toESM(require("os"));
|
|
6095
6574
|
init_orchestrator();
|
|
6096
6575
|
init_daemon();
|
|
6097
6576
|
init_config();
|
|
@@ -6100,25 +6579,25 @@ init_policy();
|
|
|
6100
6579
|
// src/undo.ts
|
|
6101
6580
|
var import_child_process8 = require("child_process");
|
|
6102
6581
|
var import_crypto5 = __toESM(require("crypto"));
|
|
6103
|
-
var
|
|
6104
|
-
var
|
|
6105
|
-
var
|
|
6106
|
-
var SNAPSHOT_STACK_PATH =
|
|
6107
|
-
var UNDO_LATEST_PATH =
|
|
6582
|
+
var import_fs14 = __toESM(require("fs"));
|
|
6583
|
+
var import_path16 = __toESM(require("path"));
|
|
6584
|
+
var import_os12 = __toESM(require("os"));
|
|
6585
|
+
var SNAPSHOT_STACK_PATH = import_path16.default.join(import_os12.default.homedir(), ".node9", "snapshots.json");
|
|
6586
|
+
var UNDO_LATEST_PATH = import_path16.default.join(import_os12.default.homedir(), ".node9", "undo_latest.txt");
|
|
6108
6587
|
var MAX_SNAPSHOTS = 10;
|
|
6109
6588
|
var GIT_TIMEOUT = 15e3;
|
|
6110
6589
|
function readStack() {
|
|
6111
6590
|
try {
|
|
6112
|
-
if (
|
|
6113
|
-
return JSON.parse(
|
|
6591
|
+
if (import_fs14.default.existsSync(SNAPSHOT_STACK_PATH))
|
|
6592
|
+
return JSON.parse(import_fs14.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
6114
6593
|
} catch {
|
|
6115
6594
|
}
|
|
6116
6595
|
return [];
|
|
6117
6596
|
}
|
|
6118
6597
|
function writeStack(stack) {
|
|
6119
|
-
const dir =
|
|
6120
|
-
if (!
|
|
6121
|
-
|
|
6598
|
+
const dir = import_path16.default.dirname(SNAPSHOT_STACK_PATH);
|
|
6599
|
+
if (!import_fs14.default.existsSync(dir)) import_fs14.default.mkdirSync(dir, { recursive: true });
|
|
6600
|
+
import_fs14.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
6122
6601
|
}
|
|
6123
6602
|
function buildArgsSummary(tool, args) {
|
|
6124
6603
|
if (!args || typeof args !== "object") return "";
|
|
@@ -6134,7 +6613,7 @@ function buildArgsSummary(tool, args) {
|
|
|
6134
6613
|
function normalizeCwdForHash(cwd) {
|
|
6135
6614
|
let normalized;
|
|
6136
6615
|
try {
|
|
6137
|
-
normalized =
|
|
6616
|
+
normalized = import_fs14.default.realpathSync(cwd);
|
|
6138
6617
|
} catch {
|
|
6139
6618
|
normalized = cwd;
|
|
6140
6619
|
}
|
|
@@ -6144,16 +6623,16 @@ function normalizeCwdForHash(cwd) {
|
|
|
6144
6623
|
}
|
|
6145
6624
|
function getShadowRepoDir(cwd) {
|
|
6146
6625
|
const hash = import_crypto5.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
6147
|
-
return
|
|
6626
|
+
return import_path16.default.join(import_os12.default.homedir(), ".node9", "snapshots", hash);
|
|
6148
6627
|
}
|
|
6149
6628
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
6150
6629
|
try {
|
|
6151
6630
|
const cutoff = Date.now() - 6e4;
|
|
6152
|
-
for (const f of
|
|
6631
|
+
for (const f of import_fs14.default.readdirSync(shadowDir)) {
|
|
6153
6632
|
if (f.startsWith("index_")) {
|
|
6154
|
-
const fp =
|
|
6633
|
+
const fp = import_path16.default.join(shadowDir, f);
|
|
6155
6634
|
try {
|
|
6156
|
-
if (
|
|
6635
|
+
if (import_fs14.default.statSync(fp).mtimeMs < cutoff) import_fs14.default.unlinkSync(fp);
|
|
6157
6636
|
} catch {
|
|
6158
6637
|
}
|
|
6159
6638
|
}
|
|
@@ -6165,7 +6644,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
6165
6644
|
const hardcoded = [".git", ".node9"];
|
|
6166
6645
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
6167
6646
|
try {
|
|
6168
|
-
|
|
6647
|
+
import_fs14.default.writeFileSync(import_path16.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
6169
6648
|
} catch {
|
|
6170
6649
|
}
|
|
6171
6650
|
}
|
|
@@ -6178,25 +6657,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
6178
6657
|
timeout: 3e3
|
|
6179
6658
|
});
|
|
6180
6659
|
if (check.status === 0) {
|
|
6181
|
-
const ptPath =
|
|
6660
|
+
const ptPath = import_path16.default.join(shadowDir, "project-path.txt");
|
|
6182
6661
|
try {
|
|
6183
|
-
const stored =
|
|
6662
|
+
const stored = import_fs14.default.readFileSync(ptPath, "utf8").trim();
|
|
6184
6663
|
if (stored === normalizedCwd) return true;
|
|
6185
6664
|
if (process.env.NODE9_DEBUG === "1")
|
|
6186
6665
|
console.error(
|
|
6187
6666
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
6188
6667
|
);
|
|
6189
|
-
|
|
6668
|
+
import_fs14.default.rmSync(shadowDir, { recursive: true, force: true });
|
|
6190
6669
|
} catch {
|
|
6191
6670
|
try {
|
|
6192
|
-
|
|
6671
|
+
import_fs14.default.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
6193
6672
|
} catch {
|
|
6194
6673
|
}
|
|
6195
6674
|
return true;
|
|
6196
6675
|
}
|
|
6197
6676
|
}
|
|
6198
6677
|
try {
|
|
6199
|
-
|
|
6678
|
+
import_fs14.default.mkdirSync(shadowDir, { recursive: true });
|
|
6200
6679
|
} catch {
|
|
6201
6680
|
}
|
|
6202
6681
|
const init = (0, import_child_process8.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -6205,7 +6684,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
6205
6684
|
console.error("[Node9] git init --bare failed:", init.stderr?.toString());
|
|
6206
6685
|
return false;
|
|
6207
6686
|
}
|
|
6208
|
-
const configFile =
|
|
6687
|
+
const configFile = import_path16.default.join(shadowDir, "config");
|
|
6209
6688
|
(0, import_child_process8.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
6210
6689
|
timeout: 3e3
|
|
6211
6690
|
});
|
|
@@ -6213,7 +6692,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
6213
6692
|
timeout: 3e3
|
|
6214
6693
|
});
|
|
6215
6694
|
try {
|
|
6216
|
-
|
|
6695
|
+
import_fs14.default.writeFileSync(import_path16.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
6217
6696
|
} catch {
|
|
6218
6697
|
}
|
|
6219
6698
|
return true;
|
|
@@ -6236,7 +6715,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
6236
6715
|
const shadowDir = getShadowRepoDir(cwd);
|
|
6237
6716
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
6238
6717
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
6239
|
-
indexFile =
|
|
6718
|
+
indexFile = import_path16.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
6240
6719
|
const shadowEnv = {
|
|
6241
6720
|
...process.env,
|
|
6242
6721
|
GIT_DIR: shadowDir,
|
|
@@ -6265,7 +6744,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
6265
6744
|
const shouldGc = stack.length % 5 === 0;
|
|
6266
6745
|
if (stack.length > MAX_SNAPSHOTS) stack.splice(0, stack.length - MAX_SNAPSHOTS);
|
|
6267
6746
|
writeStack(stack);
|
|
6268
|
-
|
|
6747
|
+
import_fs14.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
6269
6748
|
if (shouldGc) {
|
|
6270
6749
|
(0, import_child_process8.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
6271
6750
|
}
|
|
@@ -6276,7 +6755,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
6276
6755
|
} finally {
|
|
6277
6756
|
if (indexFile) {
|
|
6278
6757
|
try {
|
|
6279
|
-
|
|
6758
|
+
import_fs14.default.unlinkSync(indexFile);
|
|
6280
6759
|
} catch {
|
|
6281
6760
|
}
|
|
6282
6761
|
}
|
|
@@ -6345,9 +6824,9 @@ function applyUndo(hash, cwd) {
|
|
|
6345
6824
|
timeout: GIT_TIMEOUT
|
|
6346
6825
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
6347
6826
|
for (const file of [...tracked, ...untracked]) {
|
|
6348
|
-
const fullPath =
|
|
6349
|
-
if (!snapshotFiles.has(file) &&
|
|
6350
|
-
|
|
6827
|
+
const fullPath = import_path16.default.join(dir, file);
|
|
6828
|
+
if (!snapshotFiles.has(file) && import_fs14.default.existsSync(fullPath)) {
|
|
6829
|
+
import_fs14.default.unlinkSync(fullPath);
|
|
6351
6830
|
}
|
|
6352
6831
|
}
|
|
6353
6832
|
return true;
|
|
@@ -6371,9 +6850,9 @@ function registerCheckCommand(program2) {
|
|
|
6371
6850
|
} catch (err) {
|
|
6372
6851
|
const tempConfig = getConfig();
|
|
6373
6852
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
6374
|
-
const logPath =
|
|
6853
|
+
const logPath = import_path17.default.join(import_os13.default.homedir(), ".node9", "hook-debug.log");
|
|
6375
6854
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
6376
|
-
|
|
6855
|
+
import_fs15.default.appendFileSync(
|
|
6377
6856
|
logPath,
|
|
6378
6857
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
6379
6858
|
RAW: ${raw}
|
|
@@ -6384,10 +6863,10 @@ RAW: ${raw}
|
|
|
6384
6863
|
}
|
|
6385
6864
|
const config = getConfig(payload.cwd || void 0);
|
|
6386
6865
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
6387
|
-
const logPath =
|
|
6388
|
-
if (!
|
|
6389
|
-
|
|
6390
|
-
|
|
6866
|
+
const logPath = import_path17.default.join(import_os13.default.homedir(), ".node9", "hook-debug.log");
|
|
6867
|
+
if (!import_fs15.default.existsSync(import_path17.default.dirname(logPath)))
|
|
6868
|
+
import_fs15.default.mkdirSync(import_path17.default.dirname(logPath), { recursive: true });
|
|
6869
|
+
import_fs15.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
6391
6870
|
`);
|
|
6392
6871
|
}
|
|
6393
6872
|
const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
|
|
@@ -6400,8 +6879,8 @@ RAW: ${raw}
|
|
|
6400
6879
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
6401
6880
|
let ttyFd = null;
|
|
6402
6881
|
try {
|
|
6403
|
-
ttyFd =
|
|
6404
|
-
const writeTty = (line) =>
|
|
6882
|
+
ttyFd = import_fs15.default.openSync("/dev/tty", "w");
|
|
6883
|
+
const writeTty = (line) => import_fs15.default.writeSync(ttyFd, line + "\n");
|
|
6405
6884
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
6406
6885
|
writeTty(import_chalk5.default.bgRed.white.bold(`
|
|
6407
6886
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -6417,7 +6896,7 @@ RAW: ${raw}
|
|
|
6417
6896
|
} finally {
|
|
6418
6897
|
if (ttyFd !== null)
|
|
6419
6898
|
try {
|
|
6420
|
-
|
|
6899
|
+
import_fs15.default.closeSync(ttyFd);
|
|
6421
6900
|
} catch {
|
|
6422
6901
|
}
|
|
6423
6902
|
}
|
|
@@ -6448,7 +6927,7 @@ RAW: ${raw}
|
|
|
6448
6927
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
6449
6928
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
6450
6929
|
}
|
|
6451
|
-
const safeCwdForAuth = typeof payload.cwd === "string" &&
|
|
6930
|
+
const safeCwdForAuth = typeof payload.cwd === "string" && import_path17.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
6452
6931
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
6453
6932
|
cwd: safeCwdForAuth
|
|
6454
6933
|
});
|
|
@@ -6460,12 +6939,12 @@ RAW: ${raw}
|
|
|
6460
6939
|
}
|
|
6461
6940
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
6462
6941
|
try {
|
|
6463
|
-
const tty =
|
|
6464
|
-
|
|
6942
|
+
const tty = import_fs15.default.openSync("/dev/tty", "w");
|
|
6943
|
+
import_fs15.default.writeSync(
|
|
6465
6944
|
tty,
|
|
6466
6945
|
import_chalk5.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
6467
6946
|
);
|
|
6468
|
-
|
|
6947
|
+
import_fs15.default.closeSync(tty);
|
|
6469
6948
|
} catch {
|
|
6470
6949
|
}
|
|
6471
6950
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -6492,9 +6971,9 @@ RAW: ${raw}
|
|
|
6492
6971
|
});
|
|
6493
6972
|
} catch (err) {
|
|
6494
6973
|
if (process.env.NODE9_DEBUG === "1") {
|
|
6495
|
-
const logPath =
|
|
6974
|
+
const logPath = import_path17.default.join(import_os13.default.homedir(), ".node9", "hook-debug.log");
|
|
6496
6975
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
6497
|
-
|
|
6976
|
+
import_fs15.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
6498
6977
|
`);
|
|
6499
6978
|
}
|
|
6500
6979
|
process.exit(0);
|
|
@@ -6528,9 +7007,9 @@ RAW: ${raw}
|
|
|
6528
7007
|
}
|
|
6529
7008
|
|
|
6530
7009
|
// src/cli/commands/log.ts
|
|
6531
|
-
var
|
|
6532
|
-
var
|
|
6533
|
-
var
|
|
7010
|
+
var import_fs16 = __toESM(require("fs"));
|
|
7011
|
+
var import_path18 = __toESM(require("path"));
|
|
7012
|
+
var import_os14 = __toESM(require("os"));
|
|
6534
7013
|
init_audit();
|
|
6535
7014
|
init_config();
|
|
6536
7015
|
init_policy();
|
|
@@ -6552,11 +7031,11 @@ function registerLogCommand(program2) {
|
|
|
6552
7031
|
decision: "allowed",
|
|
6553
7032
|
source: "post-hook"
|
|
6554
7033
|
};
|
|
6555
|
-
const logPath =
|
|
6556
|
-
if (!
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
const safeCwd = typeof payload.cwd === "string" &&
|
|
7034
|
+
const logPath = import_path18.default.join(import_os14.default.homedir(), ".node9", "audit.log");
|
|
7035
|
+
if (!import_fs16.default.existsSync(import_path18.default.dirname(logPath)))
|
|
7036
|
+
import_fs16.default.mkdirSync(import_path18.default.dirname(logPath), { recursive: true });
|
|
7037
|
+
import_fs16.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
7038
|
+
const safeCwd = typeof payload.cwd === "string" && import_path18.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
6560
7039
|
const config = getConfig(safeCwd);
|
|
6561
7040
|
if (shouldSnapshot(tool, {}, config)) {
|
|
6562
7041
|
await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
|
|
@@ -6565,9 +7044,9 @@ function registerLogCommand(program2) {
|
|
|
6565
7044
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6566
7045
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
6567
7046
|
`);
|
|
6568
|
-
const debugPath =
|
|
7047
|
+
const debugPath = import_path18.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
|
|
6569
7048
|
try {
|
|
6570
|
-
|
|
7049
|
+
import_fs16.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
6571
7050
|
`);
|
|
6572
7051
|
} catch {
|
|
6573
7052
|
}
|
|
@@ -6871,14 +7350,14 @@ function registerConfigShowCommand(program2) {
|
|
|
6871
7350
|
|
|
6872
7351
|
// src/cli/commands/doctor.ts
|
|
6873
7352
|
var import_chalk7 = __toESM(require("chalk"));
|
|
6874
|
-
var
|
|
6875
|
-
var
|
|
6876
|
-
var
|
|
7353
|
+
var import_fs17 = __toESM(require("fs"));
|
|
7354
|
+
var import_path19 = __toESM(require("path"));
|
|
7355
|
+
var import_os15 = __toESM(require("os"));
|
|
6877
7356
|
var import_child_process9 = require("child_process");
|
|
6878
7357
|
init_daemon();
|
|
6879
7358
|
function registerDoctorCommand(program2, version2) {
|
|
6880
7359
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
|
|
6881
|
-
const homeDir2 =
|
|
7360
|
+
const homeDir2 = import_os15.default.homedir();
|
|
6882
7361
|
let failures = 0;
|
|
6883
7362
|
function pass(msg) {
|
|
6884
7363
|
console.log(import_chalk7.default.green(" \u2705 ") + msg);
|
|
@@ -6927,10 +7406,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
6927
7406
|
);
|
|
6928
7407
|
}
|
|
6929
7408
|
section("Configuration");
|
|
6930
|
-
const globalConfigPath =
|
|
6931
|
-
if (
|
|
7409
|
+
const globalConfigPath = import_path19.default.join(homeDir2, ".node9", "config.json");
|
|
7410
|
+
if (import_fs17.default.existsSync(globalConfigPath)) {
|
|
6932
7411
|
try {
|
|
6933
|
-
JSON.parse(
|
|
7412
|
+
JSON.parse(import_fs17.default.readFileSync(globalConfigPath, "utf-8"));
|
|
6934
7413
|
pass("~/.node9/config.json found and valid");
|
|
6935
7414
|
} catch {
|
|
6936
7415
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -6938,10 +7417,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
6938
7417
|
} else {
|
|
6939
7418
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
6940
7419
|
}
|
|
6941
|
-
const projectConfigPath =
|
|
6942
|
-
if (
|
|
7420
|
+
const projectConfigPath = import_path19.default.join(process.cwd(), "node9.config.json");
|
|
7421
|
+
if (import_fs17.default.existsSync(projectConfigPath)) {
|
|
6943
7422
|
try {
|
|
6944
|
-
JSON.parse(
|
|
7423
|
+
JSON.parse(import_fs17.default.readFileSync(projectConfigPath, "utf-8"));
|
|
6945
7424
|
pass("node9.config.json found and valid (project)");
|
|
6946
7425
|
} catch {
|
|
6947
7426
|
fail(
|
|
@@ -6950,8 +7429,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
6950
7429
|
);
|
|
6951
7430
|
}
|
|
6952
7431
|
}
|
|
6953
|
-
const credsPath =
|
|
6954
|
-
if (
|
|
7432
|
+
const credsPath = import_path19.default.join(homeDir2, ".node9", "credentials.json");
|
|
7433
|
+
if (import_fs17.default.existsSync(credsPath)) {
|
|
6955
7434
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
6956
7435
|
} else {
|
|
6957
7436
|
warn(
|
|
@@ -6960,10 +7439,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
6960
7439
|
);
|
|
6961
7440
|
}
|
|
6962
7441
|
section("Agent Hooks");
|
|
6963
|
-
const claudeSettingsPath =
|
|
6964
|
-
if (
|
|
7442
|
+
const claudeSettingsPath = import_path19.default.join(homeDir2, ".claude", "settings.json");
|
|
7443
|
+
if (import_fs17.default.existsSync(claudeSettingsPath)) {
|
|
6965
7444
|
try {
|
|
6966
|
-
const cs = JSON.parse(
|
|
7445
|
+
const cs = JSON.parse(import_fs17.default.readFileSync(claudeSettingsPath, "utf-8"));
|
|
6967
7446
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
6968
7447
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
6969
7448
|
);
|
|
@@ -6979,10 +7458,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
6979
7458
|
} else {
|
|
6980
7459
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
6981
7460
|
}
|
|
6982
|
-
const geminiSettingsPath =
|
|
6983
|
-
if (
|
|
7461
|
+
const geminiSettingsPath = import_path19.default.join(homeDir2, ".gemini", "settings.json");
|
|
7462
|
+
if (import_fs17.default.existsSync(geminiSettingsPath)) {
|
|
6984
7463
|
try {
|
|
6985
|
-
const gs = JSON.parse(
|
|
7464
|
+
const gs = JSON.parse(import_fs17.default.readFileSync(geminiSettingsPath, "utf-8"));
|
|
6986
7465
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
6987
7466
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
6988
7467
|
);
|
|
@@ -6998,10 +7477,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
6998
7477
|
} else {
|
|
6999
7478
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
7000
7479
|
}
|
|
7001
|
-
const cursorHooksPath =
|
|
7002
|
-
if (
|
|
7480
|
+
const cursorHooksPath = import_path19.default.join(homeDir2, ".cursor", "hooks.json");
|
|
7481
|
+
if (import_fs17.default.existsSync(cursorHooksPath)) {
|
|
7003
7482
|
try {
|
|
7004
|
-
const cur = JSON.parse(
|
|
7483
|
+
const cur = JSON.parse(import_fs17.default.readFileSync(cursorHooksPath, "utf-8"));
|
|
7005
7484
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
7006
7485
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
7007
7486
|
);
|
|
@@ -7039,9 +7518,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
7039
7518
|
|
|
7040
7519
|
// src/cli/commands/audit.ts
|
|
7041
7520
|
var import_chalk8 = __toESM(require("chalk"));
|
|
7042
|
-
var
|
|
7043
|
-
var
|
|
7044
|
-
var
|
|
7521
|
+
var import_fs18 = __toESM(require("fs"));
|
|
7522
|
+
var import_path20 = __toESM(require("path"));
|
|
7523
|
+
var import_os16 = __toESM(require("os"));
|
|
7045
7524
|
function formatRelativeTime(timestamp) {
|
|
7046
7525
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
7047
7526
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -7054,14 +7533,14 @@ function formatRelativeTime(timestamp) {
|
|
|
7054
7533
|
}
|
|
7055
7534
|
function registerAuditCommand(program2) {
|
|
7056
7535
|
program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
|
|
7057
|
-
const logPath =
|
|
7058
|
-
if (!
|
|
7536
|
+
const logPath = import_path20.default.join(import_os16.default.homedir(), ".node9", "audit.log");
|
|
7537
|
+
if (!import_fs18.default.existsSync(logPath)) {
|
|
7059
7538
|
console.log(
|
|
7060
7539
|
import_chalk8.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
7061
7540
|
);
|
|
7062
7541
|
return;
|
|
7063
7542
|
}
|
|
7064
|
-
const raw =
|
|
7543
|
+
const raw = import_fs18.default.readFileSync(logPath, "utf-8");
|
|
7065
7544
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
7066
7545
|
let entries = lines.flatMap((line) => {
|
|
7067
7546
|
try {
|
|
@@ -7179,9 +7658,9 @@ function registerDaemonCommand(program2) {
|
|
|
7179
7658
|
|
|
7180
7659
|
// src/cli/commands/status.ts
|
|
7181
7660
|
var import_chalk10 = __toESM(require("chalk"));
|
|
7182
|
-
var
|
|
7183
|
-
var
|
|
7184
|
-
var
|
|
7661
|
+
var import_fs19 = __toESM(require("fs"));
|
|
7662
|
+
var import_path21 = __toESM(require("path"));
|
|
7663
|
+
var import_os17 = __toESM(require("os"));
|
|
7185
7664
|
init_core();
|
|
7186
7665
|
init_daemon();
|
|
7187
7666
|
function registerStatusCommand(program2) {
|
|
@@ -7218,13 +7697,13 @@ function registerStatusCommand(program2) {
|
|
|
7218
7697
|
console.log("");
|
|
7219
7698
|
const modeLabel = settings.mode === "audit" ? import_chalk10.default.blue("audit") : settings.mode === "strict" ? import_chalk10.default.red("strict") : import_chalk10.default.white("standard");
|
|
7220
7699
|
console.log(` Mode: ${modeLabel}`);
|
|
7221
|
-
const projectConfig =
|
|
7222
|
-
const globalConfig =
|
|
7700
|
+
const projectConfig = import_path21.default.join(process.cwd(), "node9.config.json");
|
|
7701
|
+
const globalConfig = import_path21.default.join(import_os17.default.homedir(), ".node9", "config.json");
|
|
7223
7702
|
console.log(
|
|
7224
|
-
` Local: ${
|
|
7703
|
+
` Local: ${import_fs19.default.existsSync(projectConfig) ? import_chalk10.default.green("Active (node9.config.json)") : import_chalk10.default.gray("Not present")}`
|
|
7225
7704
|
);
|
|
7226
7705
|
console.log(
|
|
7227
|
-
` Global: ${
|
|
7706
|
+
` Global: ${import_fs19.default.existsSync(globalConfig) ? import_chalk10.default.green("Active (~/.node9/config.json)") : import_chalk10.default.gray("Not present")}`
|
|
7228
7707
|
);
|
|
7229
7708
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
7230
7709
|
console.log(
|
|
@@ -7403,6 +7882,7 @@ var import_chalk13 = __toESM(require("chalk"));
|
|
|
7403
7882
|
var import_child_process12 = require("child_process");
|
|
7404
7883
|
var import_execa3 = require("execa");
|
|
7405
7884
|
init_orchestrator();
|
|
7885
|
+
init_provenance();
|
|
7406
7886
|
function sanitize4(value) {
|
|
7407
7887
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
7408
7888
|
}
|
|
@@ -7415,7 +7895,7 @@ function extractMcpServer(toolName) {
|
|
|
7415
7895
|
const match = toolName.match(/^mcp__([^_](?:[^_]|_(?!_))*?)__/i);
|
|
7416
7896
|
return match?.[1];
|
|
7417
7897
|
}
|
|
7418
|
-
function
|
|
7898
|
+
function tokenize3(cmd) {
|
|
7419
7899
|
const tokens = [];
|
|
7420
7900
|
let current = "";
|
|
7421
7901
|
let inDouble = false;
|
|
@@ -7450,7 +7930,7 @@ function tokenize2(cmd) {
|
|
|
7450
7930
|
return tokens;
|
|
7451
7931
|
}
|
|
7452
7932
|
async function runMcpGateway(upstreamCommand) {
|
|
7453
|
-
const commandParts =
|
|
7933
|
+
const commandParts = tokenize3(upstreamCommand);
|
|
7454
7934
|
const cmd = commandParts[0];
|
|
7455
7935
|
const cmdArgs = commandParts.slice(1);
|
|
7456
7936
|
let executable = cmd;
|
|
@@ -7459,6 +7939,15 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
7459
7939
|
if (stdout) executable = stdout.trim();
|
|
7460
7940
|
} catch {
|
|
7461
7941
|
}
|
|
7942
|
+
const prov = checkProvenance(executable);
|
|
7943
|
+
if (prov.trustLevel === "suspect") {
|
|
7944
|
+
console.error(
|
|
7945
|
+
import_chalk13.default.red(
|
|
7946
|
+
`\u26A0\uFE0F Node9: Upstream MCP server binary is suspect \u2014 ${prov.reason} (${prov.resolvedPath})`
|
|
7947
|
+
)
|
|
7948
|
+
);
|
|
7949
|
+
console.error(import_chalk13.default.red(" Verify this binary is trusted before proceeding."));
|
|
7950
|
+
}
|
|
7462
7951
|
console.error(import_chalk13.default.green(`\u{1F680} Node9 MCP Gateway: Monitoring [${upstreamCommand}]`));
|
|
7463
7952
|
const UPSTREAM_INJECTOR_VARS = /* @__PURE__ */ new Set([
|
|
7464
7953
|
"NODE_OPTIONS",
|
|
@@ -7600,20 +8089,20 @@ function registerMcpGatewayCommand(program2) {
|
|
|
7600
8089
|
|
|
7601
8090
|
// src/cli.ts
|
|
7602
8091
|
var { version } = JSON.parse(
|
|
7603
|
-
|
|
8092
|
+
import_fs21.default.readFileSync(import_path23.default.join(__dirname, "../package.json"), "utf-8")
|
|
7604
8093
|
);
|
|
7605
8094
|
var program = new import_commander.Command();
|
|
7606
8095
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
7607
8096
|
program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
|
|
7608
8097
|
const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
|
|
7609
|
-
const credPath =
|
|
7610
|
-
if (!
|
|
7611
|
-
|
|
8098
|
+
const credPath = import_path23.default.join(import_os19.default.homedir(), ".node9", "credentials.json");
|
|
8099
|
+
if (!import_fs21.default.existsSync(import_path23.default.dirname(credPath)))
|
|
8100
|
+
import_fs21.default.mkdirSync(import_path23.default.dirname(credPath), { recursive: true });
|
|
7612
8101
|
const profileName = options.profile || "default";
|
|
7613
8102
|
let existingCreds = {};
|
|
7614
8103
|
try {
|
|
7615
|
-
if (
|
|
7616
|
-
const raw = JSON.parse(
|
|
8104
|
+
if (import_fs21.default.existsSync(credPath)) {
|
|
8105
|
+
const raw = JSON.parse(import_fs21.default.readFileSync(credPath, "utf-8"));
|
|
7617
8106
|
if (raw.apiKey) {
|
|
7618
8107
|
existingCreds = {
|
|
7619
8108
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL }
|
|
@@ -7625,13 +8114,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
7625
8114
|
} catch {
|
|
7626
8115
|
}
|
|
7627
8116
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
|
|
7628
|
-
|
|
8117
|
+
import_fs21.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
7629
8118
|
if (profileName === "default") {
|
|
7630
|
-
const configPath =
|
|
8119
|
+
const configPath = import_path23.default.join(import_os19.default.homedir(), ".node9", "config.json");
|
|
7631
8120
|
let config = {};
|
|
7632
8121
|
try {
|
|
7633
|
-
if (
|
|
7634
|
-
config = JSON.parse(
|
|
8122
|
+
if (import_fs21.default.existsSync(configPath))
|
|
8123
|
+
config = JSON.parse(import_fs21.default.readFileSync(configPath, "utf-8"));
|
|
7635
8124
|
} catch {
|
|
7636
8125
|
}
|
|
7637
8126
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -7646,9 +8135,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
7646
8135
|
approvers.cloud = false;
|
|
7647
8136
|
}
|
|
7648
8137
|
s.approvers = approvers;
|
|
7649
|
-
if (!
|
|
7650
|
-
|
|
7651
|
-
|
|
8138
|
+
if (!import_fs21.default.existsSync(import_path23.default.dirname(configPath)))
|
|
8139
|
+
import_fs21.default.mkdirSync(import_path23.default.dirname(configPath), { recursive: true });
|
|
8140
|
+
import_fs21.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
7652
8141
|
}
|
|
7653
8142
|
if (options.profile && profileName !== "default") {
|
|
7654
8143
|
console.log(import_chalk15.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
@@ -7734,15 +8223,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
7734
8223
|
}
|
|
7735
8224
|
}
|
|
7736
8225
|
if (options.purge) {
|
|
7737
|
-
const node9Dir =
|
|
7738
|
-
if (
|
|
8226
|
+
const node9Dir = import_path23.default.join(import_os19.default.homedir(), ".node9");
|
|
8227
|
+
if (import_fs21.default.existsSync(node9Dir)) {
|
|
7739
8228
|
const confirmed = await (0, import_prompts3.confirm)({
|
|
7740
8229
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
7741
8230
|
default: false
|
|
7742
8231
|
});
|
|
7743
8232
|
if (confirmed) {
|
|
7744
|
-
|
|
7745
|
-
if (
|
|
8233
|
+
import_fs21.default.rmSync(node9Dir, { recursive: true });
|
|
8234
|
+
if (import_fs21.default.existsSync(node9Dir)) {
|
|
7746
8235
|
console.error(
|
|
7747
8236
|
import_chalk15.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
7748
8237
|
);
|
|
@@ -7842,8 +8331,8 @@ program.command("explain").description(
|
|
|
7842
8331
|
console.log("");
|
|
7843
8332
|
});
|
|
7844
8333
|
program.command("init").description("Create ~/.node9/config.json with default policy (safe to run multiple times)").option("--force", "Overwrite existing config").option("-m, --mode <mode>", "Set initial security mode (standard, strict, audit)", "standard").action((options) => {
|
|
7845
|
-
const configPath =
|
|
7846
|
-
if (
|
|
8334
|
+
const configPath = import_path23.default.join(import_os19.default.homedir(), ".node9", "config.json");
|
|
8335
|
+
if (import_fs21.default.existsSync(configPath) && !options.force) {
|
|
7847
8336
|
console.log(import_chalk15.default.yellow(`\u2139\uFE0F Global config already exists: ${configPath}`));
|
|
7848
8337
|
console.log(import_chalk15.default.gray(` Run with --force to overwrite.`));
|
|
7849
8338
|
return;
|
|
@@ -7857,9 +8346,9 @@ program.command("init").description("Create ~/.node9/config.json with default po
|
|
|
7857
8346
|
mode: safeMode
|
|
7858
8347
|
}
|
|
7859
8348
|
};
|
|
7860
|
-
const dir =
|
|
7861
|
-
if (!
|
|
7862
|
-
|
|
8349
|
+
const dir = import_path23.default.dirname(configPath);
|
|
8350
|
+
if (!import_fs21.default.existsSync(dir)) import_fs21.default.mkdirSync(dir, { recursive: true });
|
|
8351
|
+
import_fs21.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2));
|
|
7863
8352
|
console.log(import_chalk15.default.green(`\u2705 Global config created: ${configPath}`));
|
|
7864
8353
|
console.log(import_chalk15.default.cyan(` Mode set to: ${safeMode}`));
|
|
7865
8354
|
console.log(
|
|
@@ -7977,9 +8466,9 @@ if (process.argv[2] !== "daemon") {
|
|
|
7977
8466
|
const isCheckHook = process.argv[2] === "check";
|
|
7978
8467
|
if (isCheckHook) {
|
|
7979
8468
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
7980
|
-
const logPath =
|
|
8469
|
+
const logPath = import_path23.default.join(import_os19.default.homedir(), ".node9", "hook-debug.log");
|
|
7981
8470
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
7982
|
-
|
|
8471
|
+
import_fs21.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
7983
8472
|
`);
|
|
7984
8473
|
}
|
|
7985
8474
|
process.exit(0);
|