@node9/proxy 2.48.0 → 2.48.1

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-main.js CHANGED
@@ -26965,7 +26965,7 @@ var init_negotiation = __esm({
26965
26965
  init_cjs_shims();
26966
26966
  init_dist();
26967
26967
  USER_LINE_ICON = "\u{1F6E1}\uFE0F";
26968
- QUOTE_INSTRUCTION = "Start your reply with the line above, exactly as written, on its own line.";
26968
+ QUOTE_INSTRUCTION = "Start your reply about this blocked action with the line above, exactly as written, on its own line. Leave it out of later replies.";
26969
26969
  USER_LINE_PART_MAX = 160;
26970
26970
  ONE_LINE_INPUT_MAX = 1e3;
26971
26971
  PICTOGRAPHS = /[\p{Extended_Pictographic}\u200D\uFE0F]/gu;
@@ -74924,6 +74924,7 @@ var import_fs74 = __toESM(require("fs"));
74924
74924
  var import_os61 = __toESM(require("os"));
74925
74925
  var import_path74 = __toESM(require("path"));
74926
74926
  init_dist();
74927
+ init_atomic_write();
74927
74928
  var ICON2 = "\u{1F6E1}\uFE0F";
74928
74929
  var LEVEL_NAMES = {
74929
74930
  audit: "Audit",
@@ -74932,23 +74933,27 @@ var LEVEL_NAMES = {
74932
74933
  strict: "Strict",
74933
74934
  "block-all": "Block all"
74934
74935
  };
74935
- var SESSION_NOTICE_INSTRUCTION = "Start your next reply to the user with the line above, exactly as written, on its own line.";
74936
+ var SESSION_NOTICE_NOTE = "node9 shows the user this line itself. Do not mention it unless the user asks about node9.";
74936
74937
  var FLAG_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
74937
74938
  var SAFE_SESSION_ID = /^[A-Za-z0-9_-]{1,128}$/;
74938
- function sessionNoticeLine(mode) {
74939
+ function sessionNoticeLine(mode, kind = "first") {
74939
74940
  const level = levelOf(mode);
74941
+ const name = LEVEL_NAMES[level];
74942
+ if (kind === "changed") {
74943
+ return level === "audit" ? `${ICON2} Node9 level changed to Audit: actions are logged, nothing is blocked.` : `${ICON2} Node9 level changed to ${name}. Every action in this session is checked.`;
74944
+ }
74940
74945
  if (level === "audit") {
74941
74946
  return `${ICON2} Node9 is in Audit mode: actions are logged, nothing is blocked.`;
74942
74947
  }
74943
- return `${ICON2} Node9 is on (${LEVEL_NAMES[level]}). Every action in this session is checked.`;
74948
+ return `${ICON2} Node9 is on (${name}). Every action in this session is checked.`;
74944
74949
  }
74945
- function sessionNoticeOutput(mode) {
74946
- const line = sessionNoticeLine(mode);
74950
+ function sessionNoticeOutput(mode, kind = "first") {
74951
+ const line = sessionNoticeLine(mode, kind);
74947
74952
  return JSON.stringify({
74948
74953
  hookSpecificOutput: {
74949
74954
  hookEventName: "PreToolUse",
74950
74955
  additionalContext: `${line}
74951
- ${SESSION_NOTICE_INSTRUCTION}`
74956
+ ${SESSION_NOTICE_NOTE}`
74952
74957
  },
74953
74958
  systemMessage: line
74954
74959
  });
@@ -74957,19 +74962,61 @@ function sessionNoticeEnabled(env = process.env) {
74957
74962
  if (env.NODE9_SESSION_NOTICE === "1") return true;
74958
74963
  return env.NODE9_TESTING !== "1";
74959
74964
  }
74960
- function claimSessionNotice(sessionId, home = import_os61.default.homedir()) {
74961
- if (!sessionId || !SAFE_SESSION_ID.test(sessionId)) return false;
74965
+ var FLAG_MAX_BYTES = 32;
74966
+ function claimSessionNotice(sessionId, home = import_os61.default.homedir(), level = "standard") {
74967
+ if (!sessionId || !SAFE_SESSION_ID.test(sessionId)) return null;
74962
74968
  const dir2 = import_path74.default.join(home, ".node9", "notice-sessions");
74963
74969
  const seen = ensureOwnDir(dir2);
74964
- if (!seen) return false;
74970
+ if (!seen) return null;
74971
+ const flag = import_path74.default.join(dir2, sessionId + SESSION_FLAG_SUFFIX);
74972
+ let created = false;
74965
74973
  try {
74966
- if (!isSameOwnDir(dir2, seen)) return false;
74967
- import_fs74.default.closeSync(import_fs74.default.openSync(import_path74.default.join(dir2, sessionId + SESSION_FLAG_SUFFIX), "wx"));
74968
- } catch {
74969
- return false;
74974
+ if (!isSameOwnDir(dir2, seen)) return null;
74975
+ const fd = import_fs74.default.openSync(flag, "wx", 384);
74976
+ created = true;
74977
+ try {
74978
+ import_fs74.default.writeSync(fd, level);
74979
+ } finally {
74980
+ import_fs74.default.closeSync(fd);
74981
+ }
74982
+ } catch (err2) {
74983
+ if (created) {
74984
+ try {
74985
+ import_fs74.default.unlinkSync(flag);
74986
+ } catch {
74987
+ }
74988
+ return null;
74989
+ }
74990
+ if (err2.code !== "EEXIST") return null;
74991
+ return levelChange(dir2, seen, flag, level);
74970
74992
  }
74971
74993
  removeOldFlags(dir2, seen);
74972
- return true;
74994
+ return "first";
74995
+ }
74996
+ function levelChange(dir2, seen, flag, level) {
74997
+ try {
74998
+ const told = readFlag(flag);
74999
+ if (told === null) return null;
75000
+ if (told === level) return null;
75001
+ if (!isSameOwnDir(dir2, seen)) return null;
75002
+ atomicWriteSync(flag, level, { mode: 384 });
75003
+ return told === "" ? null : "changed";
75004
+ } catch {
75005
+ return null;
75006
+ }
75007
+ }
75008
+ function readFlag(flag) {
75009
+ const c = import_fs74.default.constants;
75010
+ const fd = import_fs74.default.openSync(flag, c.O_RDONLY | (c.O_NOFOLLOW ?? 0) | (c.O_NONBLOCK ?? 0));
75011
+ try {
75012
+ const st = import_fs74.default.fstatSync(fd);
75013
+ if (!st.isFile() || st.nlink !== 1 || st.size > FLAG_MAX_BYTES) return null;
75014
+ const buf = Buffer.alloc(FLAG_MAX_BYTES);
75015
+ const n = import_fs74.default.readSync(fd, buf, 0, FLAG_MAX_BYTES, 0);
75016
+ return buf.toString("utf8", 0, n).trim();
75017
+ } finally {
75018
+ import_fs74.default.closeSync(fd);
75019
+ }
74973
75020
  }
74974
75021
  function removeOldFlags(dir2, seen) {
74975
75022
  const cutoff = Date.now() - FLAG_TTL_MS;
@@ -75526,9 +75573,10 @@ RAW: ${raw}
75526
75573
  }
75527
75574
  const isSubagentCall = typeof payload.agent_id === "string" && payload.agent_id !== "";
75528
75575
  const allowExit = (checkedBy) => {
75529
- if (checkedBy !== "paused" && agent === "Claude Code" && !isSubagentCall && sessionNoticeEnabled() && claimSessionNotice(safeSessionId)) {
75576
+ const notice = checkedBy !== "paused" && agent === "Claude Code" && !isSubagentCall && sessionNoticeEnabled() ? claimSessionNotice(safeSessionId, import_os62.default.homedir(), levelOf(config.settings.mode)) : null;
75577
+ if (notice) {
75530
75578
  try {
75531
- import_fs75.default.writeSync(1, sessionNoticeOutput(config.settings.mode) + "\n");
75579
+ import_fs75.default.writeSync(1, sessionNoticeOutput(config.settings.mode, notice) + "\n");
75532
75580
  } catch {
75533
75581
  }
75534
75582
  }
package/dist/hook.js CHANGED
@@ -24161,7 +24161,7 @@ function recordAndCheck(tool, args, threshold = 3, windowMs = 12e4) {
24161
24161
  init_cjs_shims();
24162
24162
  init_dist();
24163
24163
  var USER_LINE_ICON = "\u{1F6E1}\uFE0F";
24164
- var QUOTE_INSTRUCTION = "Start your reply with the line above, exactly as written, on its own line.";
24164
+ var QUOTE_INSTRUCTION = "Start your reply about this blocked action with the line above, exactly as written, on its own line. Leave it out of later replies.";
24165
24165
  var USER_LINE_PART_MAX = 160;
24166
24166
  var ONE_LINE_INPUT_MAX = 1e3;
24167
24167
  var PICTOGRAPHS = /[\p{Extended_Pictographic}\u200D\uFE0F]/gu;
@@ -26487,23 +26487,27 @@ var LEVEL_NAMES = {
26487
26487
  strict: "Strict",
26488
26488
  "block-all": "Block all"
26489
26489
  };
26490
- var SESSION_NOTICE_INSTRUCTION = "Start your next reply to the user with the line above, exactly as written, on its own line.";
26490
+ var SESSION_NOTICE_NOTE = "node9 shows the user this line itself. Do not mention it unless the user asks about node9.";
26491
26491
  var FLAG_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
26492
26492
  var SAFE_SESSION_ID = /^[A-Za-z0-9_-]{1,128}$/;
26493
- function sessionNoticeLine(mode) {
26493
+ function sessionNoticeLine(mode, kind = "first") {
26494
26494
  const level = levelOf(mode);
26495
+ const name = LEVEL_NAMES[level];
26496
+ if (kind === "changed") {
26497
+ return level === "audit" ? `${ICON} Node9 level changed to Audit: actions are logged, nothing is blocked.` : `${ICON} Node9 level changed to ${name}. Every action in this session is checked.`;
26498
+ }
26495
26499
  if (level === "audit") {
26496
26500
  return `${ICON} Node9 is in Audit mode: actions are logged, nothing is blocked.`;
26497
26501
  }
26498
- return `${ICON} Node9 is on (${LEVEL_NAMES[level]}). Every action in this session is checked.`;
26502
+ return `${ICON} Node9 is on (${name}). Every action in this session is checked.`;
26499
26503
  }
26500
- function sessionNoticeOutput(mode) {
26501
- const line = sessionNoticeLine(mode);
26504
+ function sessionNoticeOutput(mode, kind = "first") {
26505
+ const line = sessionNoticeLine(mode, kind);
26502
26506
  return JSON.stringify({
26503
26507
  hookSpecificOutput: {
26504
26508
  hookEventName: "PreToolUse",
26505
26509
  additionalContext: `${line}
26506
- ${SESSION_NOTICE_INSTRUCTION}`
26510
+ ${SESSION_NOTICE_NOTE}`
26507
26511
  },
26508
26512
  systemMessage: line
26509
26513
  });
@@ -26512,19 +26516,61 @@ function sessionNoticeEnabled(env = process.env) {
26512
26516
  if (env.NODE9_SESSION_NOTICE === "1") return true;
26513
26517
  return env.NODE9_TESTING !== "1";
26514
26518
  }
26515
- function claimSessionNotice(sessionId, home = import_os26.default.homedir()) {
26516
- if (!sessionId || !SAFE_SESSION_ID.test(sessionId)) return false;
26519
+ var FLAG_MAX_BYTES = 32;
26520
+ function claimSessionNotice(sessionId, home = import_os26.default.homedir(), level = "standard") {
26521
+ if (!sessionId || !SAFE_SESSION_ID.test(sessionId)) return null;
26517
26522
  const dir2 = import_path36.default.join(home, ".node9", "notice-sessions");
26518
26523
  const seen = ensureOwnDir(dir2);
26519
- if (!seen) return false;
26524
+ if (!seen) return null;
26525
+ const flag = import_path36.default.join(dir2, sessionId + SESSION_FLAG_SUFFIX);
26526
+ let created = false;
26520
26527
  try {
26521
- if (!isSameOwnDir(dir2, seen)) return false;
26522
- import_fs34.default.closeSync(import_fs34.default.openSync(import_path36.default.join(dir2, sessionId + SESSION_FLAG_SUFFIX), "wx"));
26523
- } catch {
26524
- return false;
26528
+ if (!isSameOwnDir(dir2, seen)) return null;
26529
+ const fd = import_fs34.default.openSync(flag, "wx", 384);
26530
+ created = true;
26531
+ try {
26532
+ import_fs34.default.writeSync(fd, level);
26533
+ } finally {
26534
+ import_fs34.default.closeSync(fd);
26535
+ }
26536
+ } catch (err) {
26537
+ if (created) {
26538
+ try {
26539
+ import_fs34.default.unlinkSync(flag);
26540
+ } catch {
26541
+ }
26542
+ return null;
26543
+ }
26544
+ if (err.code !== "EEXIST") return null;
26545
+ return levelChange(dir2, seen, flag, level);
26525
26546
  }
26526
26547
  removeOldFlags(dir2, seen);
26527
- return true;
26548
+ return "first";
26549
+ }
26550
+ function levelChange(dir2, seen, flag, level) {
26551
+ try {
26552
+ const told = readFlag(flag);
26553
+ if (told === null) return null;
26554
+ if (told === level) return null;
26555
+ if (!isSameOwnDir(dir2, seen)) return null;
26556
+ atomicWriteSync(flag, level, { mode: 384 });
26557
+ return told === "" ? null : "changed";
26558
+ } catch {
26559
+ return null;
26560
+ }
26561
+ }
26562
+ function readFlag(flag) {
26563
+ const c = import_fs34.default.constants;
26564
+ const fd = import_fs34.default.openSync(flag, c.O_RDONLY | (c.O_NOFOLLOW ?? 0) | (c.O_NONBLOCK ?? 0));
26565
+ try {
26566
+ const st = import_fs34.default.fstatSync(fd);
26567
+ if (!st.isFile() || st.nlink !== 1 || st.size > FLAG_MAX_BYTES) return null;
26568
+ const buf = Buffer.alloc(FLAG_MAX_BYTES);
26569
+ const n = import_fs34.default.readSync(fd, buf, 0, FLAG_MAX_BYTES, 0);
26570
+ return buf.toString("utf8", 0, n).trim();
26571
+ } finally {
26572
+ import_fs34.default.closeSync(fd);
26573
+ }
26528
26574
  }
26529
26575
  function removeOldFlags(dir2, seen) {
26530
26576
  const cutoff = Date.now() - FLAG_TTL_MS;
@@ -27081,9 +27127,10 @@ RAW: ${raw}
27081
27127
  }
27082
27128
  const isSubagentCall = typeof payload.agent_id === "string" && payload.agent_id !== "";
27083
27129
  const allowExit = (checkedBy) => {
27084
- if (checkedBy !== "paused" && agent === "Claude Code" && !isSubagentCall && sessionNoticeEnabled() && claimSessionNotice(safeSessionId)) {
27130
+ const notice = checkedBy !== "paused" && agent === "Claude Code" && !isSubagentCall && sessionNoticeEnabled() ? claimSessionNotice(safeSessionId, import_os27.default.homedir(), levelOf(config.settings.mode)) : null;
27131
+ if (notice) {
27085
27132
  try {
27086
- import_fs35.default.writeSync(1, sessionNoticeOutput(config.settings.mode) + "\n");
27133
+ import_fs35.default.writeSync(1, sessionNoticeOutput(config.settings.mode, notice) + "\n");
27087
27134
  } catch {
27088
27135
  }
27089
27136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "2.48.0",
3
+ "version": "2.48.1",
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",