@solongate/proxy 0.81.38 → 0.81.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  > **Don't `npm i` this.** There's nothing to import. Run the two commands below to pair your machine:
4
4
  > ```bash
5
- > npx -y @solongate/proxy@latest
6
- > npx -y @solongate/proxy@latest login
5
+ > npm i -g @solongate/proxy
6
+ > solongate
7
7
  > ```
8
8
 
9
9
  **Security for AI agents.** SolonGate checks every action an AI agent takes. Each tool call (shell command, file read or write, network request) is allowed, blocked, or logged before it runs, based on a policy you control. No code changes.
@@ -24,8 +24,8 @@ SolonGate is fully managed. There is nothing to host: pair your machine with one
24
24
  Pair the machine and turn on the guard. There are no API keys to copy. First fetch SolonGate, then log in:
25
25
 
26
26
  ```bash
27
- npx -y @solongate/proxy@latest
28
- npx -y @solongate/proxy@latest login
27
+ npm i -g @solongate/proxy
28
+ solongate
29
29
  ```
30
30
 
31
31
  This opens your browser to authorize the device. Approve it from your signed-in dashboard and SolonGate installs a global guard hook that intercepts every tool call from every AI session on the machine and checks it against your active policy.
@@ -60,12 +60,6 @@ Prefer to keep everything local? In **Settings**, turn on **Local log storage**
60
60
 
61
61
  Set a threshold in **Settings** and SolonGate notifies you by Telegram, email, or webhook when blocks spike.
62
62
 
63
- ## Uninstall
64
-
65
- ```bash
66
- npx @solongate/proxy logout
67
- ```
68
-
69
63
  ## Why SolonGate?
70
64
 
71
65
  AI agents get direct access to your system: shell, file system, databases, network. SolonGate sits between the agent and the action, enforcing your policy on every tool call before it runs, and recording every decision for audit.
package/dist/index.js CHANGED
@@ -271,7 +271,7 @@ function parseArgs(argv) {
271
271
  }
272
272
  if (!apiKey) {
273
273
  throw new Error(
274
- "Not logged in. Run this once to get started:\n\n npx -y @solongate/proxy@latest login\n"
274
+ "Not logged in. Run this once to get started:\n\n solongate login\n"
275
275
  );
276
276
  }
277
277
  if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
@@ -327,7 +327,7 @@ function parseArgs(argv) {
327
327
  }
328
328
  if (upstreamArgs.length === 0) {
329
329
  throw new Error(
330
- "No upstream server command provided.\n\nIf you just want to get started, run:\n npx -y @solongate/proxy@latest login\n"
330
+ "No upstream server command provided.\n\nIf you just want to get started, run:\n solongate login\n"
331
331
  );
332
332
  }
333
333
  const [command, ...commandArgs] = upstreamArgs;
@@ -7815,9 +7815,12 @@ function LivePanel({ active: active2 }) {
7815
7815
  const signalOk = (e) => signal === "none" ? true : signal === "deny" ? e.decision !== "ALLOW" : signal === "dlp" ? e.dlp : e.burst;
7816
7816
  const filtered = (filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e))).filter(matches).filter(signalOk);
7817
7817
  const visibleDesc = filtered.slice().reverse();
7818
+ const activeAlert = alerts.filter((a) => nowMs < a.until).slice(-1)[0];
7819
+ const activeAction = actionMsg && nowMs < actionMsg.until ? actionMsg : null;
7820
+ const bannerRows = (activeAlert ? 1 : 0) + (activeAction ? 1 : 0);
7818
7821
  const chartH = rows >= 36 ? 6 : 4;
7819
7822
  const colH = rows >= 32 ? 7 : 5;
7820
- const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
7823
+ const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH - bannerRows);
7821
7824
  const innerW = cols - 2;
7822
7825
  const leftW = Math.floor(innerW * 0.55);
7823
7826
  const rightW = innerW - leftW - 2;
@@ -8165,18 +8168,17 @@ function LivePanel({ active: active2 }) {
8165
8168
  /* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
8166
8169
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
8167
8170
  ] }),
8168
- alerts.filter((a) => nowMs < a.until).slice(-1).map((a) => /* @__PURE__ */ jsx2(
8171
+ activeAlert ? /* @__PURE__ */ jsx2(
8169
8172
  Text2,
8170
8173
  {
8171
8174
  wrap: "truncate",
8172
- backgroundColor: a.level === "bad" ? "#3d1220" : "#3d2a12",
8173
- color: a.level === "bad" ? "#ff6b6b" : "#ffb454",
8175
+ backgroundColor: activeAlert.level === "bad" ? "#3d1220" : "#3d2a12",
8176
+ color: activeAlert.level === "bad" ? "#ff6b6b" : "#ffb454",
8174
8177
  bold: true,
8175
- children: ` ${a.level === "bad" ? "\u26D4 ALERT" : "\u23F8 IDLE"} \xB7 ${truncate2(a.msg, innerW - 14)} `
8176
- },
8177
- a.id
8178
- )),
8179
- actionMsg && nowMs < actionMsg.until ? /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: actionMsg.level === "bad" ? "#3d1220" : "#123d1f", color: actionMsg.level === "bad" ? "#ff6b6b" : "#7bd88f", bold: true, children: ` ${truncate2(actionMsg.text, innerW - 4)} ` }) : null,
8178
+ children: ` ${activeAlert.level === "bad" ? "\u26D4 ALERT" : "\u23F8 IDLE"} \xB7 ${truncate2(activeAlert.msg, innerW - 14)} `
8179
+ }
8180
+ ) : null,
8181
+ activeAction ? /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: activeAction.level === "bad" ? "#3d1220" : "#123d1f", color: activeAction.level === "bad" ? "#ff6b6b" : "#7bd88f", bold: true, children: ` ${truncate2(activeAction.text, innerW - 4)} ` }) : null,
8180
8182
  /* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, children: [
8181
8183
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
8182
8184
  /* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
@@ -10034,6 +10036,8 @@ function App() {
10034
10036
  if (accounts.length < 2) return;
10035
10037
  viewAccount(accounts[(acctIdx + 1) % accounts.length]);
10036
10038
  };
10039
+ const locked = accounts.length === 0;
10040
+ const effectiveSection = locked ? ACCOUNTS_SECTION : section;
10037
10041
  useInput8((input, key) => {
10038
10042
  if (help) {
10039
10043
  setHelp(false);
@@ -10043,6 +10047,12 @@ function App() {
10043
10047
  setHelp(true);
10044
10048
  return;
10045
10049
  }
10050
+ if (locked) {
10051
+ if (key.rightArrow || key.return || key.tab) setFocus("panel");
10052
+ else if (key.escape) setFocus("nav");
10053
+ else if (input === "q" && focus === "nav") exit();
10054
+ return;
10055
+ }
10046
10056
  if (focus === "nav") {
10047
10057
  if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
10048
10058
  else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
@@ -10055,7 +10065,7 @@ function App() {
10055
10065
  }
10056
10066
  });
10057
10067
  const { cols, rows } = useTermSize();
10058
- const current = SECTIONS[section];
10068
+ const current = SECTIONS[effectiveSection];
10059
10069
  if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
10060
10070
  if (current.label === "Live" && focus === "panel") {
10061
10071
  return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }, viewNonce) });
@@ -10066,14 +10076,18 @@ function App() {
10066
10076
  /* @__PURE__ */ jsx9(Banner, { cols }),
10067
10077
  /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
10068
10078
  /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
10069
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: acctLabel }),
10070
- accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Accounts to manage)` }) : accounts.length === 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Accounts to add another" }) : null
10079
+ /* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel }),
10080
+ locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Accounts to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Accounts to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Accounts to add another" })
10071
10081
  ] }),
10072
10082
  /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
10073
- /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
10083
+ /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
10084
+ const isCur = i === effectiveSection;
10085
+ const disabled = locked && s.label !== "Accounts";
10086
+ return /* @__PURE__ */ jsx9(Text9, { color: isCur ? theme.accentBright : disabled ? theme.dim : void 0, bold: isCur, dimColor: disabled, children: (isCur ? "\u25B8 " : disabled ? "\u2298 " : " ") + s.label }, s.label);
10087
+ }) }),
10074
10088
  /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: panelBody })
10075
10089
  ] }, viewNonce),
10076
- /* @__PURE__ */ jsx9(Box9, { children: focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ...accounts.length > 1 ? [["a", "account"]] : [], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
10090
+ /* @__PURE__ */ jsx9(Box9, { children: locked ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2192/enter", "open Accounts"], ["n", "log in"], ["q", "quit"]] }) : focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ...accounts.length > 1 ? [["a", "account"]] : [], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
10077
10091
  ] });
10078
10092
  }
10079
10093
  function HelpOverlay({ cols, rows }) {
@@ -11312,17 +11326,6 @@ var init_commands = __esm({
11312
11326
  });
11313
11327
 
11314
11328
  // src/global-install.ts
11315
- var global_install_exports = {};
11316
- __export(global_install_exports, {
11317
- globalPaths: () => globalPaths,
11318
- installClaudeShim: () => installClaudeShim,
11319
- installGlobalWithKey: () => installGlobalWithKey,
11320
- lockProtected: () => lockProtected,
11321
- removeClaudeShim: () => removeClaudeShim,
11322
- runGlobalInstall: () => runGlobalInstall,
11323
- runGlobalRestore: () => runGlobalRestore,
11324
- unlockProtected: () => unlockProtected
11325
- });
11326
11329
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, existsSync as existsSync6, mkdirSync as mkdirSync6 } from "fs";
11327
11330
  import { resolve as resolve4, join as join11, dirname } from "path";
11328
11331
  import { homedir as homedir9 } from "os";
@@ -11431,26 +11434,6 @@ function ask(question) {
11431
11434
  res(a.trim());
11432
11435
  }));
11433
11436
  }
11434
- function runGlobalRestore() {
11435
- const p = globalPaths();
11436
- unlockProtected();
11437
- removeClaudeShim();
11438
- if (existsSync6(p.backupPath)) {
11439
- writeFileSync7(p.settingsPath, readFileSync8(p.backupPath, "utf-8"));
11440
- console.log(` Restored ${p.settingsPath} from backup.`);
11441
- } else if (existsSync6(p.settingsPath)) {
11442
- try {
11443
- const s = JSON.parse(readFileSync8(p.settingsPath, "utf-8"));
11444
- delete s.hooks;
11445
- writeFileSync7(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
11446
- console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
11447
- } catch {
11448
- }
11449
- } else {
11450
- console.log(" Nothing to restore \u2014 no global Claude Code settings found.");
11451
- }
11452
- console.log(" Global SolonGate enforcement uninstalled. Restart Claude Code.");
11453
- }
11454
11437
  function escapeRe(s) {
11455
11438
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11456
11439
  }
@@ -11512,14 +11495,6 @@ ${SHIM_END}`;
11512
11495
  }
11513
11496
  }
11514
11497
  }
11515
- function removeClaudeShim() {
11516
- for (const file of shimTargets()) {
11517
- try {
11518
- writeShimBlock(file, null);
11519
- } catch {
11520
- }
11521
- }
11522
- }
11523
11498
  async function runGlobalInstall(opts = {}) {
11524
11499
  const p = globalPaths();
11525
11500
  let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
@@ -11583,9 +11558,6 @@ async function runGlobalInstall(opts = {}) {
11583
11558
  console.log(" Locked protection files (OS-level read-only/immutable).");
11584
11559
  }
11585
11560
  }
11586
- async function installGlobalWithKey(apiKey, apiUrl) {
11587
- await runGlobalInstall({ apiKey, apiUrl });
11588
- }
11589
11561
  var __dirname, HOOKS_DIR, SHIM_BEGIN, SHIM_END;
11590
11562
  var init_global_install = __esm({
11591
11563
  "src/global-install.ts"() {
@@ -11721,7 +11693,6 @@ async function main() {
11721
11693
  console.log(" \u2502 Every Claude Code session on this machine is \u2502");
11722
11694
  console.log(" \u2502 now guarded by your cloud policy (OPA WASM). \u2502");
11723
11695
  console.log(" \u2502 \u2502");
11724
- console.log(" \u2502 Undo: npx @solongate/proxy logout \u2502");
11725
11696
  console.log(" \u2502 Manage policy: https://dashboard.solongate.com \u2502");
11726
11697
  console.log(" \u2502 Restart Claude Code to apply. \u2502");
11727
11698
  console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
@@ -16222,7 +16193,7 @@ ${msg.content.text}`;
16222
16193
 
16223
16194
  // src/index.ts
16224
16195
  init_cli_utils();
16225
- var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "logout", "shield", "create", "inject", "pull", "push", "list", "ls", "logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks", "dataroom"]);
16196
+ var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "shield", "create", "inject", "pull", "push", "list", "ls", "logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks", "dataroom"]);
16226
16197
  var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "");
16227
16198
  if (!IS_HUMAN_CLI) {
16228
16199
  console.log = (...args) => {
@@ -16244,14 +16215,14 @@ function printWelcome() {
16244
16215
  console.log("");
16245
16216
  console.log(" Get started with one command:");
16246
16217
  console.log("");
16247
- console.log(` ${c.cyan}npx -y @solongate/proxy@latest login${c.reset}`);
16218
+ console.log(` ${c.cyan}solongate login${c.reset}`);
16248
16219
  console.log("");
16249
16220
  console.log(` ${c.dim}This pairs your device and protects every Claude Code session${c.reset}`);
16250
16221
  console.log(` ${c.dim}on this machine with your cloud policy. Manage it at${c.reset}`);
16251
16222
  console.log(` ${c.cyan}https://dashboard.solongate.com${c.reset}`);
16252
16223
  console.log("");
16253
16224
  console.log(` ${c.dim}Using local log storage? Show it live in the dashboard with${c.reset}`);
16254
- console.log(` ${c.cyan}npx -y @solongate/proxy@latest logs-server${c.reset}`);
16225
+ console.log(` ${c.cyan}solongate logs-server${c.reset}`);
16255
16226
  console.log("");
16256
16227
  }
16257
16228
  async function main5() {
@@ -16280,11 +16251,6 @@ async function main5() {
16280
16251
  await Promise.resolve().then(() => (init_login(), login_exports));
16281
16252
  return;
16282
16253
  }
16283
- if (subcommand === "logout") {
16284
- const { runGlobalRestore: runGlobalRestore2 } = await Promise.resolve().then(() => (init_global_install(), global_install_exports));
16285
- runGlobalRestore2();
16286
- return;
16287
- }
16288
16254
  if (subcommand === "shield") {
16289
16255
  const { runShield: runShield2 } = await Promise.resolve().then(() => (init_shield(), shield_exports));
16290
16256
  await runShield2();
package/dist/login.js CHANGED
@@ -410,7 +410,6 @@ async function main() {
410
410
  console.log(" \u2502 Every Claude Code session on this machine is \u2502");
411
411
  console.log(" \u2502 now guarded by your cloud policy (OPA WASM). \u2502");
412
412
  console.log(" \u2502 \u2502");
413
- console.log(" \u2502 Undo: npx @solongate/proxy logout \u2502");
414
413
  console.log(" \u2502 Manage policy: https://dashboard.solongate.com \u2502");
415
414
  console.log(" \u2502 Restart Claude Code to apply. \u2502");
416
415
  console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
package/dist/tui/index.js CHANGED
@@ -1248,9 +1248,12 @@ function LivePanel({ active: active2 }) {
1248
1248
  const signalOk = (e) => signal === "none" ? true : signal === "deny" ? e.decision !== "ALLOW" : signal === "dlp" ? e.dlp : e.burst;
1249
1249
  const filtered = (filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e))).filter(matches).filter(signalOk);
1250
1250
  const visibleDesc = filtered.slice().reverse();
1251
+ const activeAlert = alerts.filter((a) => nowMs < a.until).slice(-1)[0];
1252
+ const activeAction = actionMsg && nowMs < actionMsg.until ? actionMsg : null;
1253
+ const bannerRows = (activeAlert ? 1 : 0) + (activeAction ? 1 : 0);
1251
1254
  const chartH = rows >= 36 ? 6 : 4;
1252
1255
  const colH = rows >= 32 ? 7 : 5;
1253
- const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
1256
+ const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH - bannerRows);
1254
1257
  const innerW = cols - 2;
1255
1258
  const leftW = Math.floor(innerW * 0.55);
1256
1259
  const rightW = innerW - leftW - 2;
@@ -1598,18 +1601,17 @@ function LivePanel({ active: active2 }) {
1598
1601
  /* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
1599
1602
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
1600
1603
  ] }),
1601
- alerts.filter((a) => nowMs < a.until).slice(-1).map((a) => /* @__PURE__ */ jsx2(
1604
+ activeAlert ? /* @__PURE__ */ jsx2(
1602
1605
  Text2,
1603
1606
  {
1604
1607
  wrap: "truncate",
1605
- backgroundColor: a.level === "bad" ? "#3d1220" : "#3d2a12",
1606
- color: a.level === "bad" ? "#ff6b6b" : "#ffb454",
1608
+ backgroundColor: activeAlert.level === "bad" ? "#3d1220" : "#3d2a12",
1609
+ color: activeAlert.level === "bad" ? "#ff6b6b" : "#ffb454",
1607
1610
  bold: true,
1608
- children: ` ${a.level === "bad" ? "\u26D4 ALERT" : "\u23F8 IDLE"} \xB7 ${truncate(a.msg, innerW - 14)} `
1609
- },
1610
- a.id
1611
- )),
1612
- actionMsg && nowMs < actionMsg.until ? /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: actionMsg.level === "bad" ? "#3d1220" : "#123d1f", color: actionMsg.level === "bad" ? "#ff6b6b" : "#7bd88f", bold: true, children: ` ${truncate(actionMsg.text, innerW - 4)} ` }) : null,
1611
+ children: ` ${activeAlert.level === "bad" ? "\u26D4 ALERT" : "\u23F8 IDLE"} \xB7 ${truncate(activeAlert.msg, innerW - 14)} `
1612
+ }
1613
+ ) : null,
1614
+ activeAction ? /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: activeAction.level === "bad" ? "#3d1220" : "#123d1f", color: activeAction.level === "bad" ? "#ff6b6b" : "#7bd88f", bold: true, children: ` ${truncate(activeAction.text, innerW - 4)} ` }) : null,
1613
1615
  /* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, children: [
1614
1616
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
1615
1617
  /* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
@@ -3344,6 +3346,8 @@ function App() {
3344
3346
  if (accounts.length < 2) return;
3345
3347
  viewAccount(accounts[(acctIdx + 1) % accounts.length]);
3346
3348
  };
3349
+ const locked = accounts.length === 0;
3350
+ const effectiveSection = locked ? ACCOUNTS_SECTION : section;
3347
3351
  useInput8((input, key) => {
3348
3352
  if (help) {
3349
3353
  setHelp(false);
@@ -3353,6 +3357,12 @@ function App() {
3353
3357
  setHelp(true);
3354
3358
  return;
3355
3359
  }
3360
+ if (locked) {
3361
+ if (key.rightArrow || key.return || key.tab) setFocus("panel");
3362
+ else if (key.escape) setFocus("nav");
3363
+ else if (input === "q" && focus === "nav") exit();
3364
+ return;
3365
+ }
3356
3366
  if (focus === "nav") {
3357
3367
  if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
3358
3368
  else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
@@ -3365,7 +3375,7 @@ function App() {
3365
3375
  }
3366
3376
  });
3367
3377
  const { cols, rows } = useTermSize();
3368
- const current = SECTIONS[section];
3378
+ const current = SECTIONS[effectiveSection];
3369
3379
  if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
3370
3380
  if (current.label === "Live" && focus === "panel") {
3371
3381
  return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }, viewNonce) });
@@ -3376,14 +3386,18 @@ function App() {
3376
3386
  /* @__PURE__ */ jsx9(Banner, { cols }),
3377
3387
  /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
3378
3388
  /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
3379
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: acctLabel }),
3380
- accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Accounts to manage)` }) : accounts.length === 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Accounts to add another" }) : null
3389
+ /* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel }),
3390
+ locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Accounts to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Accounts to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Accounts to add another" })
3381
3391
  ] }),
3382
3392
  /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
3383
- /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
3393
+ /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
3394
+ const isCur = i === effectiveSection;
3395
+ const disabled = locked && s.label !== "Accounts";
3396
+ return /* @__PURE__ */ jsx9(Text9, { color: isCur ? theme.accentBright : disabled ? theme.dim : void 0, bold: isCur, dimColor: disabled, children: (isCur ? "\u25B8 " : disabled ? "\u2298 " : " ") + s.label }, s.label);
3397
+ }) }),
3384
3398
  /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: panelBody })
3385
3399
  ] }, viewNonce),
3386
- /* @__PURE__ */ jsx9(Box9, { children: focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ...accounts.length > 1 ? [["a", "account"]] : [], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
3400
+ /* @__PURE__ */ jsx9(Box9, { children: locked ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2192/enter", "open Accounts"], ["n", "log in"], ["q", "quit"]] }) : focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ...accounts.length > 1 ? [["a", "account"]] : [], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
3387
3401
  ] });
3388
3402
  }
3389
3403
  var HELP = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.38",
3
+ "version": "0.81.40",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {