@node9/proxy 2.28.1 → 2.29.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/dist/cli.js +82 -10
- package/dist/cli.mjs +82 -10
- package/dist/dashboard.mjs +11 -2
- package/dist/index.js +10 -1
- package/dist/index.mjs +10 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11103,6 +11103,14 @@ async function checkStatePredicates(predicates) {
|
|
|
11103
11103
|
return null;
|
|
11104
11104
|
}
|
|
11105
11105
|
}
|
|
11106
|
+
function resolveDaemonPort(env = process.env) {
|
|
11107
|
+
if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
|
|
11108
|
+
const raw = env.NODE9_DAEMON_PORT;
|
|
11109
|
+
if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
|
|
11110
|
+
const port = Number(raw);
|
|
11111
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
|
|
11112
|
+
return port;
|
|
11113
|
+
}
|
|
11106
11114
|
function getInternalToken() {
|
|
11107
11115
|
try {
|
|
11108
11116
|
const pidFile = import_path10.default.join(import_os8.default.homedir(), ".node9", "daemon.pid");
|
|
@@ -11376,7 +11384,7 @@ async function resolveViaDaemon(id, decision, internalToken, source) {
|
|
|
11376
11384
|
signal: AbortSignal.timeout(3e3)
|
|
11377
11385
|
});
|
|
11378
11386
|
}
|
|
11379
|
-
var import_fs10, import_net, import_path10, import_os8, ACTIVITY_SOCKET_PATH, DAEMON_PORT, DAEMON_HOST;
|
|
11387
|
+
var import_fs10, import_net, import_path10, import_os8, ACTIVITY_SOCKET_PATH, DEFAULT_DAEMON_PORT, DAEMON_PORT, DAEMON_HOST;
|
|
11380
11388
|
var init_daemon = __esm({
|
|
11381
11389
|
"src/auth/daemon.ts"() {
|
|
11382
11390
|
"use strict";
|
|
@@ -11385,7 +11393,8 @@ var init_daemon = __esm({
|
|
|
11385
11393
|
import_path10 = __toESM(require("path"));
|
|
11386
11394
|
import_os8 = __toESM(require("os"));
|
|
11387
11395
|
ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path10.default.join(import_os8.default.tmpdir(), "node9-activity.sock");
|
|
11388
|
-
|
|
11396
|
+
DEFAULT_DAEMON_PORT = 7391;
|
|
11397
|
+
DAEMON_PORT = resolveDaemonPort();
|
|
11389
11398
|
DAEMON_HOST = "127.0.0.1";
|
|
11390
11399
|
}
|
|
11391
11400
|
});
|
|
@@ -58994,6 +59003,42 @@ function shellOutputText(toolResponse) {
|
|
|
58994
59003
|
}
|
|
58995
59004
|
return collectToolOutputText(toolResponse).text;
|
|
58996
59005
|
}
|
|
59006
|
+
function mapToolOutputStrings(toolResponse, fn) {
|
|
59007
|
+
const walk = (v, depth) => {
|
|
59008
|
+
if (typeof v === "string") return fn(v);
|
|
59009
|
+
if (depth >= MAX_DEPTH3 || v === null || typeof v !== "object") return v;
|
|
59010
|
+
if (Array.isArray(v)) return v.map((x) => walk(x, depth + 1));
|
|
59011
|
+
const out = {};
|
|
59012
|
+
for (const [k, x] of Object.entries(v)) out[k] = walk(x, depth + 1);
|
|
59013
|
+
return out;
|
|
59014
|
+
};
|
|
59015
|
+
return walk(toolResponse, 0);
|
|
59016
|
+
}
|
|
59017
|
+
function frameToolOutputLeaves(toolResponse, isSuspect, frame, neutralize) {
|
|
59018
|
+
const suspect = [];
|
|
59019
|
+
let largestAt = -1;
|
|
59020
|
+
let largestLen = 0;
|
|
59021
|
+
mapToolOutputStrings(toolResponse, (s) => {
|
|
59022
|
+
const i2 = suspect.length;
|
|
59023
|
+
suspect.push(s.length > 0 && isSuspect(s));
|
|
59024
|
+
if (s.length > largestLen) {
|
|
59025
|
+
largestLen = s.length;
|
|
59026
|
+
largestAt = i2;
|
|
59027
|
+
}
|
|
59028
|
+
return s;
|
|
59029
|
+
});
|
|
59030
|
+
const anySuspect = suspect.some(Boolean);
|
|
59031
|
+
let i = 0;
|
|
59032
|
+
return mapToolOutputStrings(toolResponse, (s) => {
|
|
59033
|
+
const at = i++;
|
|
59034
|
+
const pick2 = anySuspect ? suspect[at] : at === largestAt;
|
|
59035
|
+
if (!pick2) return neutralize(s);
|
|
59036
|
+
const f = frame();
|
|
59037
|
+
return `${f.header}
|
|
59038
|
+
${neutralize(s)}
|
|
59039
|
+
${f.footer}`;
|
|
59040
|
+
});
|
|
59041
|
+
}
|
|
58997
59042
|
|
|
58998
59043
|
// src/cli/commands/log.ts
|
|
58999
59044
|
init_hasher();
|
|
@@ -59224,19 +59269,36 @@ function registerLogCommand(program2) {
|
|
|
59224
59269
|
process.stdout.write(JSON.stringify({ redacted: out, found, injection }) + "\n");
|
|
59225
59270
|
} else {
|
|
59226
59271
|
const warnings = [];
|
|
59272
|
+
const inPlace = agent === "Claude Code" && config.policy.dlp.enabled;
|
|
59273
|
+
const redactedNames = /* @__PURE__ */ new Set();
|
|
59274
|
+
let updatedToolOutput = void 0;
|
|
59275
|
+
if (inPlace) {
|
|
59276
|
+
const mapped = mapToolOutputStrings(payload.tool_response, (leaf) => {
|
|
59277
|
+
const r = redactText(leaf);
|
|
59278
|
+
for (const name of r.found) redactedNames.add(name);
|
|
59279
|
+
return r.result;
|
|
59280
|
+
});
|
|
59281
|
+
if (redactedNames.size > 0) updatedToolOutput = mapped;
|
|
59282
|
+
}
|
|
59227
59283
|
const hit = scanText(toolOutput);
|
|
59228
|
-
|
|
59229
|
-
|
|
59230
|
-
|
|
59231
|
-
`output-secret:${hit.patternName}`
|
|
59232
|
-
);
|
|
59284
|
+
const secretName = hit?.patternName ?? [...redactedNames][0];
|
|
59285
|
+
if (secretName) {
|
|
59286
|
+
await notifySessionTaint(payloadSessionId ?? "", `output-secret:${secretName}`);
|
|
59233
59287
|
warnings.push(
|
|
59234
|
-
`\u26A0\uFE0F node9: this tool output contained a credential (${
|
|
59288
|
+
updatedToolOutput !== void 0 ? `\u26A0\uFE0F node9: this tool output contained a credential (${secretName}); node9 replaced it with [node9-redacted:\u2026] before you read it. Treat the credential as compromised. node9 has flagged this session: the next network or write action will require approval.` : `\u26A0\uFE0F node9: this tool output contained a credential (${secretName}). Do not echo, store, or transmit it \u2014 treat it as compromised and rotate it. node9 has flagged this session: the next network or write action will require approval.`
|
|
59235
59289
|
);
|
|
59236
59290
|
}
|
|
59237
59291
|
if (injectionOn) {
|
|
59238
59292
|
const m = scanInjection(toolOutput, { tool: rawToolName });
|
|
59239
59293
|
if (m && atLeastConfidence(m.confidence, inj.minConfidence)) {
|
|
59294
|
+
if (agent === "Claude Code") {
|
|
59295
|
+
updatedToolOutput = frameToolOutputLeaves(
|
|
59296
|
+
updatedToolOutput ?? payload.tool_response,
|
|
59297
|
+
(leaf) => scanInjection(leaf, { tool: rawToolName }) !== null,
|
|
59298
|
+
newUntrustedFrame,
|
|
59299
|
+
neutralizeMarkers
|
|
59300
|
+
);
|
|
59301
|
+
}
|
|
59240
59302
|
await notifySessionTaint(
|
|
59241
59303
|
payloadSessionId ?? "",
|
|
59242
59304
|
`output-injection:${m.signals.join("+")}`
|
|
@@ -59246,12 +59308,22 @@ function registerLogCommand(program2) {
|
|
|
59246
59308
|
);
|
|
59247
59309
|
}
|
|
59248
59310
|
}
|
|
59249
|
-
if (
|
|
59311
|
+
if (redactedNames.size > 0) {
|
|
59312
|
+
appendToLog(LOCAL_AUDIT_LOG, {
|
|
59313
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
59314
|
+
tool,
|
|
59315
|
+
source: "output-redacted",
|
|
59316
|
+
outputRedacted: [...redactedNames],
|
|
59317
|
+
...payloadSessionId ? { sessionId: payloadSessionId } : {}
|
|
59318
|
+
});
|
|
59319
|
+
}
|
|
59320
|
+
if ((warnings.length > 0 || updatedToolOutput !== void 0) && (agent === "Claude Code" || agent === "Codex")) {
|
|
59250
59321
|
process.stdout.write(
|
|
59251
59322
|
JSON.stringify({
|
|
59252
59323
|
hookSpecificOutput: {
|
|
59253
59324
|
hookEventName: "PostToolUse",
|
|
59254
|
-
additionalContext: warnings.join("\n\n")
|
|
59325
|
+
...warnings.length > 0 ? { additionalContext: warnings.join("\n\n") } : {},
|
|
59326
|
+
...updatedToolOutput !== void 0 ? { updatedToolOutput } : {}
|
|
59255
59327
|
}
|
|
59256
59328
|
}) + "\n"
|
|
59257
59329
|
);
|
package/dist/cli.mjs
CHANGED
|
@@ -11109,6 +11109,14 @@ async function checkStatePredicates(predicates) {
|
|
|
11109
11109
|
return null;
|
|
11110
11110
|
}
|
|
11111
11111
|
}
|
|
11112
|
+
function resolveDaemonPort(env = process.env) {
|
|
11113
|
+
if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
|
|
11114
|
+
const raw = env.NODE9_DAEMON_PORT;
|
|
11115
|
+
if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
|
|
11116
|
+
const port = Number(raw);
|
|
11117
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
|
|
11118
|
+
return port;
|
|
11119
|
+
}
|
|
11112
11120
|
function getInternalToken() {
|
|
11113
11121
|
try {
|
|
11114
11122
|
const pidFile = path10.join(os8.homedir(), ".node9", "daemon.pid");
|
|
@@ -11382,12 +11390,13 @@ async function resolveViaDaemon(id, decision, internalToken, source) {
|
|
|
11382
11390
|
signal: AbortSignal.timeout(3e3)
|
|
11383
11391
|
});
|
|
11384
11392
|
}
|
|
11385
|
-
var ACTIVITY_SOCKET_PATH, DAEMON_PORT, DAEMON_HOST;
|
|
11393
|
+
var ACTIVITY_SOCKET_PATH, DEFAULT_DAEMON_PORT, DAEMON_PORT, DAEMON_HOST;
|
|
11386
11394
|
var init_daemon = __esm({
|
|
11387
11395
|
"src/auth/daemon.ts"() {
|
|
11388
11396
|
"use strict";
|
|
11389
11397
|
ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path10.join(os8.tmpdir(), "node9-activity.sock");
|
|
11390
|
-
|
|
11398
|
+
DEFAULT_DAEMON_PORT = 7391;
|
|
11399
|
+
DAEMON_PORT = resolveDaemonPort();
|
|
11391
11400
|
DAEMON_HOST = "127.0.0.1";
|
|
11392
11401
|
}
|
|
11393
11402
|
});
|
|
@@ -58976,6 +58985,42 @@ function shellOutputText(toolResponse) {
|
|
|
58976
58985
|
}
|
|
58977
58986
|
return collectToolOutputText(toolResponse).text;
|
|
58978
58987
|
}
|
|
58988
|
+
function mapToolOutputStrings(toolResponse, fn) {
|
|
58989
|
+
const walk = (v, depth) => {
|
|
58990
|
+
if (typeof v === "string") return fn(v);
|
|
58991
|
+
if (depth >= MAX_DEPTH3 || v === null || typeof v !== "object") return v;
|
|
58992
|
+
if (Array.isArray(v)) return v.map((x) => walk(x, depth + 1));
|
|
58993
|
+
const out = {};
|
|
58994
|
+
for (const [k, x] of Object.entries(v)) out[k] = walk(x, depth + 1);
|
|
58995
|
+
return out;
|
|
58996
|
+
};
|
|
58997
|
+
return walk(toolResponse, 0);
|
|
58998
|
+
}
|
|
58999
|
+
function frameToolOutputLeaves(toolResponse, isSuspect, frame, neutralize) {
|
|
59000
|
+
const suspect = [];
|
|
59001
|
+
let largestAt = -1;
|
|
59002
|
+
let largestLen = 0;
|
|
59003
|
+
mapToolOutputStrings(toolResponse, (s) => {
|
|
59004
|
+
const i2 = suspect.length;
|
|
59005
|
+
suspect.push(s.length > 0 && isSuspect(s));
|
|
59006
|
+
if (s.length > largestLen) {
|
|
59007
|
+
largestLen = s.length;
|
|
59008
|
+
largestAt = i2;
|
|
59009
|
+
}
|
|
59010
|
+
return s;
|
|
59011
|
+
});
|
|
59012
|
+
const anySuspect = suspect.some(Boolean);
|
|
59013
|
+
let i = 0;
|
|
59014
|
+
return mapToolOutputStrings(toolResponse, (s) => {
|
|
59015
|
+
const at = i++;
|
|
59016
|
+
const pick2 = anySuspect ? suspect[at] : at === largestAt;
|
|
59017
|
+
if (!pick2) return neutralize(s);
|
|
59018
|
+
const f = frame();
|
|
59019
|
+
return `${f.header}
|
|
59020
|
+
${neutralize(s)}
|
|
59021
|
+
${f.footer}`;
|
|
59022
|
+
});
|
|
59023
|
+
}
|
|
58979
59024
|
|
|
58980
59025
|
// src/cli/commands/log.ts
|
|
58981
59026
|
init_hasher();
|
|
@@ -59206,19 +59251,36 @@ function registerLogCommand(program2) {
|
|
|
59206
59251
|
process.stdout.write(JSON.stringify({ redacted: out, found, injection }) + "\n");
|
|
59207
59252
|
} else {
|
|
59208
59253
|
const warnings = [];
|
|
59254
|
+
const inPlace = agent === "Claude Code" && config.policy.dlp.enabled;
|
|
59255
|
+
const redactedNames = /* @__PURE__ */ new Set();
|
|
59256
|
+
let updatedToolOutput = void 0;
|
|
59257
|
+
if (inPlace) {
|
|
59258
|
+
const mapped = mapToolOutputStrings(payload.tool_response, (leaf) => {
|
|
59259
|
+
const r = redactText(leaf);
|
|
59260
|
+
for (const name of r.found) redactedNames.add(name);
|
|
59261
|
+
return r.result;
|
|
59262
|
+
});
|
|
59263
|
+
if (redactedNames.size > 0) updatedToolOutput = mapped;
|
|
59264
|
+
}
|
|
59209
59265
|
const hit = scanText(toolOutput);
|
|
59210
|
-
|
|
59211
|
-
|
|
59212
|
-
|
|
59213
|
-
`output-secret:${hit.patternName}`
|
|
59214
|
-
);
|
|
59266
|
+
const secretName = hit?.patternName ?? [...redactedNames][0];
|
|
59267
|
+
if (secretName) {
|
|
59268
|
+
await notifySessionTaint(payloadSessionId ?? "", `output-secret:${secretName}`);
|
|
59215
59269
|
warnings.push(
|
|
59216
|
-
`\u26A0\uFE0F node9: this tool output contained a credential (${
|
|
59270
|
+
updatedToolOutput !== void 0 ? `\u26A0\uFE0F node9: this tool output contained a credential (${secretName}); node9 replaced it with [node9-redacted:\u2026] before you read it. Treat the credential as compromised. node9 has flagged this session: the next network or write action will require approval.` : `\u26A0\uFE0F node9: this tool output contained a credential (${secretName}). Do not echo, store, or transmit it \u2014 treat it as compromised and rotate it. node9 has flagged this session: the next network or write action will require approval.`
|
|
59217
59271
|
);
|
|
59218
59272
|
}
|
|
59219
59273
|
if (injectionOn) {
|
|
59220
59274
|
const m = scanInjection(toolOutput, { tool: rawToolName });
|
|
59221
59275
|
if (m && atLeastConfidence(m.confidence, inj.minConfidence)) {
|
|
59276
|
+
if (agent === "Claude Code") {
|
|
59277
|
+
updatedToolOutput = frameToolOutputLeaves(
|
|
59278
|
+
updatedToolOutput ?? payload.tool_response,
|
|
59279
|
+
(leaf) => scanInjection(leaf, { tool: rawToolName }) !== null,
|
|
59280
|
+
newUntrustedFrame,
|
|
59281
|
+
neutralizeMarkers
|
|
59282
|
+
);
|
|
59283
|
+
}
|
|
59222
59284
|
await notifySessionTaint(
|
|
59223
59285
|
payloadSessionId ?? "",
|
|
59224
59286
|
`output-injection:${m.signals.join("+")}`
|
|
@@ -59228,12 +59290,22 @@ function registerLogCommand(program2) {
|
|
|
59228
59290
|
);
|
|
59229
59291
|
}
|
|
59230
59292
|
}
|
|
59231
|
-
if (
|
|
59293
|
+
if (redactedNames.size > 0) {
|
|
59294
|
+
appendToLog(LOCAL_AUDIT_LOG, {
|
|
59295
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
59296
|
+
tool,
|
|
59297
|
+
source: "output-redacted",
|
|
59298
|
+
outputRedacted: [...redactedNames],
|
|
59299
|
+
...payloadSessionId ? { sessionId: payloadSessionId } : {}
|
|
59300
|
+
});
|
|
59301
|
+
}
|
|
59302
|
+
if ((warnings.length > 0 || updatedToolOutput !== void 0) && (agent === "Claude Code" || agent === "Codex")) {
|
|
59232
59303
|
process.stdout.write(
|
|
59233
59304
|
JSON.stringify({
|
|
59234
59305
|
hookSpecificOutput: {
|
|
59235
59306
|
hookEventName: "PostToolUse",
|
|
59236
|
-
additionalContext: warnings.join("\n\n")
|
|
59307
|
+
...warnings.length > 0 ? { additionalContext: warnings.join("\n\n") } : {},
|
|
59308
|
+
...updatedToolOutput !== void 0 ? { updatedToolOutput } : {}
|
|
59237
59309
|
}
|
|
59238
59310
|
}) + "\n"
|
|
59239
59311
|
);
|
package/dist/dashboard.mjs
CHANGED
|
@@ -5380,6 +5380,14 @@ var init_blast = __esm({
|
|
|
5380
5380
|
import fs4 from "fs";
|
|
5381
5381
|
import path5 from "path";
|
|
5382
5382
|
import os5 from "os";
|
|
5383
|
+
function resolveDaemonPort(env = process.env) {
|
|
5384
|
+
if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
|
|
5385
|
+
const raw = env.NODE9_DAEMON_PORT;
|
|
5386
|
+
if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
|
|
5387
|
+
const port = Number(raw);
|
|
5388
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
|
|
5389
|
+
return port;
|
|
5390
|
+
}
|
|
5383
5391
|
function getInternalToken() {
|
|
5384
5392
|
try {
|
|
5385
5393
|
const pidFile = path5.join(os5.homedir(), ".node9", "daemon.pid");
|
|
@@ -5391,12 +5399,13 @@ function getInternalToken() {
|
|
|
5391
5399
|
return null;
|
|
5392
5400
|
}
|
|
5393
5401
|
}
|
|
5394
|
-
var ACTIVITY_SOCKET_PATH, DAEMON_PORT, DAEMON_HOST;
|
|
5402
|
+
var ACTIVITY_SOCKET_PATH, DEFAULT_DAEMON_PORT, DAEMON_PORT, DAEMON_HOST;
|
|
5395
5403
|
var init_daemon = __esm({
|
|
5396
5404
|
"src/auth/daemon.ts"() {
|
|
5397
5405
|
"use strict";
|
|
5398
5406
|
ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path5.join(os5.tmpdir(), "node9-activity.sock");
|
|
5399
|
-
|
|
5407
|
+
DEFAULT_DAEMON_PORT = 7391;
|
|
5408
|
+
DAEMON_PORT = resolveDaemonPort();
|
|
5400
5409
|
DAEMON_HOST = "127.0.0.1";
|
|
5401
5410
|
}
|
|
5402
5411
|
});
|
package/dist/index.js
CHANGED
|
@@ -9625,7 +9625,16 @@ async function checkStatePredicates(predicates) {
|
|
|
9625
9625
|
return null;
|
|
9626
9626
|
}
|
|
9627
9627
|
}
|
|
9628
|
-
var
|
|
9628
|
+
var DEFAULT_DAEMON_PORT = 7391;
|
|
9629
|
+
function resolveDaemonPort(env = process.env) {
|
|
9630
|
+
if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
|
|
9631
|
+
const raw = env.NODE9_DAEMON_PORT;
|
|
9632
|
+
if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
|
|
9633
|
+
const port = Number(raw);
|
|
9634
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
|
|
9635
|
+
return port;
|
|
9636
|
+
}
|
|
9637
|
+
var DAEMON_PORT = resolveDaemonPort();
|
|
9629
9638
|
var DAEMON_HOST = "127.0.0.1";
|
|
9630
9639
|
function getInternalToken() {
|
|
9631
9640
|
try {
|
package/dist/index.mjs
CHANGED
|
@@ -9595,7 +9595,16 @@ async function checkStatePredicates(predicates) {
|
|
|
9595
9595
|
return null;
|
|
9596
9596
|
}
|
|
9597
9597
|
}
|
|
9598
|
-
var
|
|
9598
|
+
var DEFAULT_DAEMON_PORT = 7391;
|
|
9599
|
+
function resolveDaemonPort(env = process.env) {
|
|
9600
|
+
if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
|
|
9601
|
+
const raw = env.NODE9_DAEMON_PORT;
|
|
9602
|
+
if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
|
|
9603
|
+
const port = Number(raw);
|
|
9604
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
|
|
9605
|
+
return port;
|
|
9606
|
+
}
|
|
9607
|
+
var DAEMON_PORT = resolveDaemonPort();
|
|
9599
9608
|
var DAEMON_HOST = "127.0.0.1";
|
|
9600
9609
|
function getInternalToken() {
|
|
9601
9610
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.29.0",
|
|
4
4
|
"description": "Access control for AI agents. Set what Claude Code, Codex, Gemini, Cursor and any MCP server are allowed to do, review risky actions before they run, and keep every action on the record.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|