@solongate/proxy 0.82.48 → 0.82.49

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/index.js CHANGED
@@ -9222,6 +9222,7 @@ function PoliciesPanel({ focused }) {
9222
9222
  const [editing, setEditing] = useState4(false);
9223
9223
  const [editVal, setEditVal] = useState4("");
9224
9224
  const [status, setStatus] = useState4(null);
9225
+ const [pendingDel, setPendingDel] = useState4(null);
9225
9226
  const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
9226
9227
  const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
9227
9228
  useEffect4(() => {
@@ -9244,6 +9245,11 @@ function PoliciesPanel({ focused }) {
9244
9245
  };
9245
9246
  const save = async () => {
9246
9247
  if (!selected || !detail.data) return;
9248
+ const blanket = rules.findIndex(isBlanketRule);
9249
+ if (blanket !== -1) {
9250
+ setStatus(`\u2717 rule ${blanket + 1} matches EVERYTHING \u2014 pick a Constraint + Match value, or narrow Permissions`);
9251
+ return;
9252
+ }
9247
9253
  setStatus("Saving\u2026");
9248
9254
  try {
9249
9255
  const res = await api.policies.update(selected.id, {
@@ -9292,9 +9298,13 @@ function PoliciesPanel({ focused }) {
9292
9298
  return;
9293
9299
  }
9294
9300
  if (view === "list") {
9295
- if (key.upArrow) setPi((n) => Math.max(0, n - 1));
9296
- else if (key.downArrow) setPi((n) => Math.min(policies.length - 1, n + 1));
9297
- else if (key.return || key.rightArrow) {
9301
+ if (key.upArrow) {
9302
+ setPendingDel(null);
9303
+ setPi((n) => Math.max(0, n - 1));
9304
+ } else if (key.downArrow) {
9305
+ setPendingDel(null);
9306
+ setPi((n) => Math.min(policies.length - 1, n + 1));
9307
+ } else if (key.return || key.rightArrow) {
9298
9308
  setStatus(null);
9299
9309
  setView("rules");
9300
9310
  } else if (input === "a") {
@@ -9314,6 +9324,22 @@ function PoliciesPanel({ focused }) {
9314
9324
  setStatus("\u2713 active policy: NONE \u2014 no rules enforced, w/b grants are refused");
9315
9325
  activeQ.reload();
9316
9326
  }).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
9327
+ } else if (input === "d") {
9328
+ if (!selected) return;
9329
+ if (pendingDel !== selected.id) {
9330
+ setPendingDel(selected.id);
9331
+ setStatus(`\u26A0 d again to DELETE policy "${selected.name}" \u2014 cannot be undone`);
9332
+ return;
9333
+ }
9334
+ setPendingDel(null);
9335
+ setStatus("Deleting\u2026");
9336
+ const name = selected.name;
9337
+ void api.policies.remove(selected.id).then(() => {
9338
+ setStatus(`\u2713 deleted policy "${name}"`);
9339
+ setPi(0);
9340
+ list5.reload();
9341
+ activeQ.reload();
9342
+ }).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
9317
9343
  }
9318
9344
  return;
9319
9345
  }
@@ -9329,7 +9355,7 @@ function PoliciesPanel({ focused }) {
9329
9355
  } else if (input === " ") {
9330
9356
  if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
9331
9357
  } else if (input === "e") {
9332
- if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, effect: r.effect === "ALLOW" ? "DENY" : "ALLOW" } : r));
9358
+ if (rules[ri]) mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
9333
9359
  } else if (input === "d") {
9334
9360
  if (rules[ri]) {
9335
9361
  const next = rules.filter((_, i) => i !== ri);
@@ -9360,9 +9386,16 @@ function PoliciesPanel({ focused }) {
9360
9386
  else if (key.upArrow) setFi((n) => Math.max(0, n - 1));
9361
9387
  else if (key.downArrow) setFi((n) => Math.min(FIELDS.length - 1, n + 1));
9362
9388
  else if (field.kind === "effect" && (input === " " || key.rightArrow)) {
9363
- mutate(rules.map((r, i) => i === ri ? { ...r, effect: r.effect === "ALLOW" ? "DENY" : "ALLOW" } : r));
9389
+ mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
9364
9390
  } else if (field.kind === "enabled" && (input === " " || key.rightArrow)) {
9365
9391
  mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
9392
+ } else if (field.kind === "ctype" && (input === " " || key.rightArrow || key.leftArrow)) {
9393
+ const dir = key.leftArrow ? -1 : 1;
9394
+ const nextT = CTYPES[(CTYPES.indexOf(currentCType(rule2)) + dir + CTYPES.length) % CTYPES.length];
9395
+ mutate(rules.map((r, i) => i === ri ? setCType(r, nextT) : r));
9396
+ } else if (field.kind === "perms" && (input === "r" || input === "w" || input === "e" || input === "n")) {
9397
+ const p = { r: "READ", w: "WRITE", e: "EXECUTE", n: "NETWORK" }[input];
9398
+ mutate(rules.map((rr, i) => i === ri ? togglePerm(rr, p) : rr));
9366
9399
  } else if (field.kind === "priority" && (key.rightArrow || key.leftArrow)) {
9367
9400
  const d = key.rightArrow ? 1 : -1;
9368
9401
  mutate(rules.map((r, i) => i === ri ? { ...r, priority: Math.max(0, r.priority + d) } : r));
@@ -9392,7 +9425,7 @@ function PoliciesPanel({ focused }) {
9392
9425
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
9393
9426
  ] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
9394
9427
  ] }),
9395
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new policy \xB7 a activate \xB7 x deactivate \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
9428
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new \xB7 a activate \xB7 x deactivate \xB7 d delete \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
9396
9429
  creating !== null ? /* @__PURE__ */ jsxs4(Box4, { children: [
9397
9430
  /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "new policy name: " }),
9398
9431
  /* @__PURE__ */ jsx4(TextInput3, { value: creating, onChange: setCreating, onSubmit: commitNewPolicy })
@@ -9467,7 +9500,7 @@ function PoliciesPanel({ focused }) {
9467
9500
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: rule?.id ?? "" }),
9468
9501
  dirtyTag
9469
9502
  ] }),
9470
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 s save \xB7 \u2190 back" }),
9503
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 r/w/e/n perms \xB7 s save \xB7 \u2190 back" }),
9471
9504
  /* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
9472
9505
  const val = rule ? f.get(rule) : "";
9473
9506
  const active2 = i === fi;
@@ -9496,7 +9529,7 @@ function PoliciesPanel({ focused }) {
9496
9529
  status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
9497
9530
  ] });
9498
9531
  }
9499
- var constr, setConstr, FIELDS;
9532
+ var CTYPES, CTYPE_GROUP, currentCType, clearConstraints, setCType, cValue, setCValue, migrateEffect, PERMS, permList, permIsAny, togglePerm, permDisplay, ruleHasValues, isBlanketRule, FIELDS;
9500
9533
  var init_Policies = __esm({
9501
9534
  "src/tui/panels/Policies.tsx"() {
9502
9535
  "use strict";
@@ -9506,25 +9539,79 @@ var init_Policies = __esm({
9506
9539
  init_components();
9507
9540
  init_hooks();
9508
9541
  init_theme();
9509
- constr = (r, g, side) => (r[g]?.[side] ?? []).join(", ");
9510
- setConstr = (r, g, side, val) => {
9542
+ CTYPES = ["none", "command", "path", "filename", "url"];
9543
+ CTYPE_GROUP = {
9544
+ command: "commandConstraints",
9545
+ path: "pathConstraints",
9546
+ filename: "filenameConstraints",
9547
+ url: "urlConstraints"
9548
+ };
9549
+ currentCType = (r) => {
9550
+ if (r.filenameConstraints) return "filename";
9551
+ if (r.urlConstraints) return "url";
9552
+ if (r.commandConstraints) return "command";
9553
+ if (r.pathConstraints) return "path";
9554
+ return "none";
9555
+ };
9556
+ clearConstraints = (r) => ({
9557
+ ...r,
9558
+ commandConstraints: void 0,
9559
+ pathConstraints: void 0,
9560
+ filenameConstraints: void 0,
9561
+ urlConstraints: void 0
9562
+ });
9563
+ setCType = (r, t) => {
9564
+ const base = clearConstraints(r);
9565
+ if (t === "none") return base;
9566
+ const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
9567
+ return { ...base, [CTYPE_GROUP[t]]: { [listKey]: [] } };
9568
+ };
9569
+ cValue = (r) => {
9570
+ const t = currentCType(r);
9571
+ if (t === "none") return "";
9572
+ const c2 = r[CTYPE_GROUP[t]];
9573
+ return [...c2?.allowed ?? [], ...c2?.denied ?? []].join(", ");
9574
+ };
9575
+ setCValue = (r, val) => {
9576
+ const t = currentCType(r);
9577
+ if (t === "none") return r;
9511
9578
  const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
9512
- const prev = r[g] ?? {};
9513
- return { ...r, [g]: { ...prev, [side]: arr } };
9579
+ const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
9580
+ return { ...r, [CTYPE_GROUP[t]]: { [listKey]: arr } };
9581
+ };
9582
+ migrateEffect = (r, nextEffect) => {
9583
+ const flipped = { ...r, effect: nextEffect };
9584
+ const t = currentCType(r);
9585
+ if (t === "none") return flipped;
9586
+ const c2 = r[CTYPE_GROUP[t]];
9587
+ const items = [...c2?.allowed ?? [], ...c2?.denied ?? []];
9588
+ const listKey = nextEffect === "ALLOW" ? "allowed" : "denied";
9589
+ return { ...flipped, [CTYPE_GROUP[t]]: { [listKey]: items } };
9590
+ };
9591
+ PERMS = ["READ", "WRITE", "EXECUTE", "NETWORK"];
9592
+ permList = (r) => !r.permission ? [...PERMS] : Array.isArray(r.permission) ? r.permission : [r.permission];
9593
+ permIsAny = (r) => PERMS.every((p) => permList(r).includes(p));
9594
+ togglePerm = (r, p) => {
9595
+ const cur = permList(r);
9596
+ let next = cur.includes(p) ? cur.filter((x) => x !== p) : [...cur, p];
9597
+ if (next.length === 0) next = [p];
9598
+ return { ...r, permission: PERMS.every((x) => next.includes(x)) ? void 0 : next };
9599
+ };
9600
+ permDisplay = (r) => permIsAny(r) ? "any (READ WRITE EXECUTE NETWORK)" : permList(r).join(" ");
9601
+ ruleHasValues = (r) => {
9602
+ const t = currentCType(r);
9603
+ if (t === "none") return false;
9604
+ const c2 = r[CTYPE_GROUP[t]];
9605
+ return (c2?.allowed?.length ?? 0) + (c2?.denied?.length ?? 0) > 0;
9514
9606
  };
9607
+ isBlanketRule = (r) => !ruleHasValues(r) && permIsAny(r);
9515
9608
  FIELDS = [
9516
9609
  { label: "Effect", kind: "effect", get: (r) => r.effect },
9517
- { label: "Enabled", kind: "enabled", get: (r) => r.enabled ? "yes" : "no" },
9610
+ { label: "Constraint", kind: "ctype", get: (r) => currentCType(r) },
9611
+ { label: "Permissions", kind: "perms", get: permDisplay },
9518
9612
  { label: "Priority", kind: "priority", get: (r) => String(r.priority) },
9519
9613
  { label: "Description", kind: "text", get: (r) => r.description ?? "", set: (r, v) => ({ ...r, description: v }) },
9520
- { label: "Cmd allow", kind: "text", get: (r) => constr(r, "commandConstraints", "allowed"), set: (r, v) => setConstr(r, "commandConstraints", "allowed", v) },
9521
- { label: "Cmd deny", kind: "text", get: (r) => constr(r, "commandConstraints", "denied"), set: (r, v) => setConstr(r, "commandConstraints", "denied", v) },
9522
- { label: "Path allow", kind: "text", get: (r) => constr(r, "pathConstraints", "allowed"), set: (r, v) => setConstr(r, "pathConstraints", "allowed", v) },
9523
- { label: "Path deny", kind: "text", get: (r) => constr(r, "pathConstraints", "denied"), set: (r, v) => setConstr(r, "pathConstraints", "denied", v) },
9524
- { label: "File allow", kind: "text", get: (r) => constr(r, "filenameConstraints", "allowed"), set: (r, v) => setConstr(r, "filenameConstraints", "allowed", v) },
9525
- { label: "File deny", kind: "text", get: (r) => constr(r, "filenameConstraints", "denied"), set: (r, v) => setConstr(r, "filenameConstraints", "denied", v) },
9526
- { label: "URL allow", kind: "text", get: (r) => constr(r, "urlConstraints", "allowed"), set: (r, v) => setConstr(r, "urlConstraints", "allowed", v) },
9527
- { label: "URL deny", kind: "text", get: (r) => constr(r, "urlConstraints", "denied"), set: (r, v) => setConstr(r, "urlConstraints", "denied", v) }
9614
+ { label: "Match", kind: "text", get: cValue, set: setCValue }
9528
9615
  ];
9529
9616
  }
9530
9617
  });
package/dist/tui/index.js CHANGED
@@ -2420,25 +2420,79 @@ function onSectionRequest(fn) {
2420
2420
 
2421
2421
  // src/tui/panels/Policies.tsx
2422
2422
  import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2423
- var constr = (r, g, side) => (r[g]?.[side] ?? []).join(", ");
2424
- var setConstr = (r, g, side, val) => {
2423
+ var CTYPES = ["none", "command", "path", "filename", "url"];
2424
+ var CTYPE_GROUP = {
2425
+ command: "commandConstraints",
2426
+ path: "pathConstraints",
2427
+ filename: "filenameConstraints",
2428
+ url: "urlConstraints"
2429
+ };
2430
+ var currentCType = (r) => {
2431
+ if (r.filenameConstraints) return "filename";
2432
+ if (r.urlConstraints) return "url";
2433
+ if (r.commandConstraints) return "command";
2434
+ if (r.pathConstraints) return "path";
2435
+ return "none";
2436
+ };
2437
+ var clearConstraints = (r) => ({
2438
+ ...r,
2439
+ commandConstraints: void 0,
2440
+ pathConstraints: void 0,
2441
+ filenameConstraints: void 0,
2442
+ urlConstraints: void 0
2443
+ });
2444
+ var setCType = (r, t) => {
2445
+ const base = clearConstraints(r);
2446
+ if (t === "none") return base;
2447
+ const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
2448
+ return { ...base, [CTYPE_GROUP[t]]: { [listKey]: [] } };
2449
+ };
2450
+ var cValue = (r) => {
2451
+ const t = currentCType(r);
2452
+ if (t === "none") return "";
2453
+ const c2 = r[CTYPE_GROUP[t]];
2454
+ return [...c2?.allowed ?? [], ...c2?.denied ?? []].join(", ");
2455
+ };
2456
+ var setCValue = (r, val) => {
2457
+ const t = currentCType(r);
2458
+ if (t === "none") return r;
2425
2459
  const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
2426
- const prev = r[g] ?? {};
2427
- return { ...r, [g]: { ...prev, [side]: arr } };
2460
+ const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
2461
+ return { ...r, [CTYPE_GROUP[t]]: { [listKey]: arr } };
2462
+ };
2463
+ var migrateEffect = (r, nextEffect) => {
2464
+ const flipped = { ...r, effect: nextEffect };
2465
+ const t = currentCType(r);
2466
+ if (t === "none") return flipped;
2467
+ const c2 = r[CTYPE_GROUP[t]];
2468
+ const items = [...c2?.allowed ?? [], ...c2?.denied ?? []];
2469
+ const listKey = nextEffect === "ALLOW" ? "allowed" : "denied";
2470
+ return { ...flipped, [CTYPE_GROUP[t]]: { [listKey]: items } };
2471
+ };
2472
+ var PERMS = ["READ", "WRITE", "EXECUTE", "NETWORK"];
2473
+ var permList = (r) => !r.permission ? [...PERMS] : Array.isArray(r.permission) ? r.permission : [r.permission];
2474
+ var permIsAny = (r) => PERMS.every((p) => permList(r).includes(p));
2475
+ var togglePerm = (r, p) => {
2476
+ const cur = permList(r);
2477
+ let next = cur.includes(p) ? cur.filter((x) => x !== p) : [...cur, p];
2478
+ if (next.length === 0) next = [p];
2479
+ return { ...r, permission: PERMS.every((x) => next.includes(x)) ? void 0 : next };
2480
+ };
2481
+ var permDisplay = (r) => permIsAny(r) ? "any (READ WRITE EXECUTE NETWORK)" : permList(r).join(" ");
2482
+ var ruleHasValues = (r) => {
2483
+ const t = currentCType(r);
2484
+ if (t === "none") return false;
2485
+ const c2 = r[CTYPE_GROUP[t]];
2486
+ return (c2?.allowed?.length ?? 0) + (c2?.denied?.length ?? 0) > 0;
2428
2487
  };
2488
+ var isBlanketRule = (r) => !ruleHasValues(r) && permIsAny(r);
2429
2489
  var FIELDS = [
2430
2490
  { label: "Effect", kind: "effect", get: (r) => r.effect },
2431
- { label: "Enabled", kind: "enabled", get: (r) => r.enabled ? "yes" : "no" },
2491
+ { label: "Constraint", kind: "ctype", get: (r) => currentCType(r) },
2492
+ { label: "Permissions", kind: "perms", get: permDisplay },
2432
2493
  { label: "Priority", kind: "priority", get: (r) => String(r.priority) },
2433
2494
  { label: "Description", kind: "text", get: (r) => r.description ?? "", set: (r, v) => ({ ...r, description: v }) },
2434
- { label: "Cmd allow", kind: "text", get: (r) => constr(r, "commandConstraints", "allowed"), set: (r, v) => setConstr(r, "commandConstraints", "allowed", v) },
2435
- { label: "Cmd deny", kind: "text", get: (r) => constr(r, "commandConstraints", "denied"), set: (r, v) => setConstr(r, "commandConstraints", "denied", v) },
2436
- { label: "Path allow", kind: "text", get: (r) => constr(r, "pathConstraints", "allowed"), set: (r, v) => setConstr(r, "pathConstraints", "allowed", v) },
2437
- { label: "Path deny", kind: "text", get: (r) => constr(r, "pathConstraints", "denied"), set: (r, v) => setConstr(r, "pathConstraints", "denied", v) },
2438
- { label: "File allow", kind: "text", get: (r) => constr(r, "filenameConstraints", "allowed"), set: (r, v) => setConstr(r, "filenameConstraints", "allowed", v) },
2439
- { label: "File deny", kind: "text", get: (r) => constr(r, "filenameConstraints", "denied"), set: (r, v) => setConstr(r, "filenameConstraints", "denied", v) },
2440
- { label: "URL allow", kind: "text", get: (r) => constr(r, "urlConstraints", "allowed"), set: (r, v) => setConstr(r, "urlConstraints", "allowed", v) },
2441
- { label: "URL deny", kind: "text", get: (r) => constr(r, "urlConstraints", "denied"), set: (r, v) => setConstr(r, "urlConstraints", "denied", v) }
2495
+ { label: "Match", kind: "text", get: cValue, set: setCValue }
2442
2496
  ];
2443
2497
  function ruleSummary(r) {
2444
2498
  const bits = [];
@@ -2466,6 +2520,7 @@ function PoliciesPanel({ focused }) {
2466
2520
  const [editing, setEditing] = useState4(false);
2467
2521
  const [editVal, setEditVal] = useState4("");
2468
2522
  const [status, setStatus] = useState4(null);
2523
+ const [pendingDel, setPendingDel] = useState4(null);
2469
2524
  const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
2470
2525
  const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
2471
2526
  useEffect4(() => {
@@ -2488,6 +2543,11 @@ function PoliciesPanel({ focused }) {
2488
2543
  };
2489
2544
  const save = async () => {
2490
2545
  if (!selected || !detail.data) return;
2546
+ const blanket = rules.findIndex(isBlanketRule);
2547
+ if (blanket !== -1) {
2548
+ setStatus(`\u2717 rule ${blanket + 1} matches EVERYTHING \u2014 pick a Constraint + Match value, or narrow Permissions`);
2549
+ return;
2550
+ }
2491
2551
  setStatus("Saving\u2026");
2492
2552
  try {
2493
2553
  const res = await api.policies.update(selected.id, {
@@ -2536,9 +2596,13 @@ function PoliciesPanel({ focused }) {
2536
2596
  return;
2537
2597
  }
2538
2598
  if (view === "list") {
2539
- if (key.upArrow) setPi((n) => Math.max(0, n - 1));
2540
- else if (key.downArrow) setPi((n) => Math.min(policies.length - 1, n + 1));
2541
- else if (key.return || key.rightArrow) {
2599
+ if (key.upArrow) {
2600
+ setPendingDel(null);
2601
+ setPi((n) => Math.max(0, n - 1));
2602
+ } else if (key.downArrow) {
2603
+ setPendingDel(null);
2604
+ setPi((n) => Math.min(policies.length - 1, n + 1));
2605
+ } else if (key.return || key.rightArrow) {
2542
2606
  setStatus(null);
2543
2607
  setView("rules");
2544
2608
  } else if (input === "a") {
@@ -2558,6 +2622,22 @@ function PoliciesPanel({ focused }) {
2558
2622
  setStatus("\u2713 active policy: NONE \u2014 no rules enforced, w/b grants are refused");
2559
2623
  activeQ.reload();
2560
2624
  }).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
2625
+ } else if (input === "d") {
2626
+ if (!selected) return;
2627
+ if (pendingDel !== selected.id) {
2628
+ setPendingDel(selected.id);
2629
+ setStatus(`\u26A0 d again to DELETE policy "${selected.name}" \u2014 cannot be undone`);
2630
+ return;
2631
+ }
2632
+ setPendingDel(null);
2633
+ setStatus("Deleting\u2026");
2634
+ const name = selected.name;
2635
+ void api.policies.remove(selected.id).then(() => {
2636
+ setStatus(`\u2713 deleted policy "${name}"`);
2637
+ setPi(0);
2638
+ list5.reload();
2639
+ activeQ.reload();
2640
+ }).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
2561
2641
  }
2562
2642
  return;
2563
2643
  }
@@ -2573,7 +2653,7 @@ function PoliciesPanel({ focused }) {
2573
2653
  } else if (input === " ") {
2574
2654
  if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
2575
2655
  } else if (input === "e") {
2576
- if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, effect: r.effect === "ALLOW" ? "DENY" : "ALLOW" } : r));
2656
+ if (rules[ri]) mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
2577
2657
  } else if (input === "d") {
2578
2658
  if (rules[ri]) {
2579
2659
  const next = rules.filter((_, i) => i !== ri);
@@ -2604,9 +2684,16 @@ function PoliciesPanel({ focused }) {
2604
2684
  else if (key.upArrow) setFi((n) => Math.max(0, n - 1));
2605
2685
  else if (key.downArrow) setFi((n) => Math.min(FIELDS.length - 1, n + 1));
2606
2686
  else if (field.kind === "effect" && (input === " " || key.rightArrow)) {
2607
- mutate(rules.map((r, i) => i === ri ? { ...r, effect: r.effect === "ALLOW" ? "DENY" : "ALLOW" } : r));
2687
+ mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
2608
2688
  } else if (field.kind === "enabled" && (input === " " || key.rightArrow)) {
2609
2689
  mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
2690
+ } else if (field.kind === "ctype" && (input === " " || key.rightArrow || key.leftArrow)) {
2691
+ const dir = key.leftArrow ? -1 : 1;
2692
+ const nextT = CTYPES[(CTYPES.indexOf(currentCType(rule2)) + dir + CTYPES.length) % CTYPES.length];
2693
+ mutate(rules.map((r, i) => i === ri ? setCType(r, nextT) : r));
2694
+ } else if (field.kind === "perms" && (input === "r" || input === "w" || input === "e" || input === "n")) {
2695
+ const p = { r: "READ", w: "WRITE", e: "EXECUTE", n: "NETWORK" }[input];
2696
+ mutate(rules.map((rr, i) => i === ri ? togglePerm(rr, p) : rr));
2610
2697
  } else if (field.kind === "priority" && (key.rightArrow || key.leftArrow)) {
2611
2698
  const d = key.rightArrow ? 1 : -1;
2612
2699
  mutate(rules.map((r, i) => i === ri ? { ...r, priority: Math.max(0, r.priority + d) } : r));
@@ -2636,7 +2723,7 @@ function PoliciesPanel({ focused }) {
2636
2723
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
2637
2724
  ] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
2638
2725
  ] }),
2639
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new policy \xB7 a activate \xB7 x deactivate \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
2726
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new \xB7 a activate \xB7 x deactivate \xB7 d delete \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
2640
2727
  creating !== null ? /* @__PURE__ */ jsxs4(Box4, { children: [
2641
2728
  /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "new policy name: " }),
2642
2729
  /* @__PURE__ */ jsx4(TextInput3, { value: creating, onChange: setCreating, onSubmit: commitNewPolicy })
@@ -2711,7 +2798,7 @@ function PoliciesPanel({ focused }) {
2711
2798
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: rule?.id ?? "" }),
2712
2799
  dirtyTag
2713
2800
  ] }),
2714
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 s save \xB7 \u2190 back" }),
2801
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 r/w/e/n perms \xB7 s save \xB7 \u2190 back" }),
2715
2802
  /* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
2716
2803
  const val = rule ? f.get(rule) : "";
2717
2804
  const active2 = i === fi;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.82.48",
3
+ "version": "0.82.49",
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": {