@solongate/proxy 0.83.31 → 0.83.33

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.
@@ -692,7 +692,7 @@ var USAGE = usage("solongate policy", "manage cloud policies", [
692
692
  ["policy deny <id> [--command|--path|--url <val>]", "append a DENY rule"],
693
693
  ["policy revoke <id> <ruleId>", "remove a rule"],
694
694
  ["policy active", "show the resolved active policy"],
695
- ["policy activate <id> | --clear", "pin / unpin the active policy"],
695
+ ["policy activate <id> | --off", "pin the active policy, or enforce nothing"],
696
696
  ["policy dry-run <id|file.json> [--limit N] [--mode denylist|whitelist]", "replay recent traffic against rules"]
697
697
  ], "Add --json to any read command for machine-readable output.");
698
698
  async function run(argv) {
@@ -798,13 +798,15 @@ async function run(argv) {
798
798
  return 0;
799
799
  }
800
800
  case "activate": {
801
- if (flagBool(flags, "clear")) {
801
+ if (flagBool(flags, "off") || flagBool(flags, "clear")) {
802
802
  const res2 = await api.policies.setActive(null);
803
803
  if (json) return printJson(res2), 0;
804
- return err(green(" \u2713 Cleared active-policy pin.")), 0;
804
+ err(green(" \u2713 Deactivated - this project now enforces no policy."));
805
+ err(dim(" Pin one again with: solongate policy activate <id>"));
806
+ return 0;
805
807
  }
806
808
  const id = positionals[1];
807
- if (!id) return err(" Usage: policy activate <id> | policy activate --clear"), 1;
809
+ if (!id) return err(" Usage: policy activate <id> | policy activate --off"), 1;
808
810
  const res = await api.policies.setActive(id);
809
811
  if (json) return printJson(res), 0;
810
812
  err(green(` \u2713 Pinned active policy \u2192 ${res.active}`));
package/dist/index.js CHANGED
@@ -7125,6 +7125,30 @@ import { access, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync
7125
7125
  import { homedir as homedir3 } from "os";
7126
7126
  import { dirname as dirname2, join as join5, sep } from "path";
7127
7127
  import { fileURLToPath as fileURLToPath2 } from "url";
7128
+ function lockHeldAt() {
7129
+ try {
7130
+ const ts = Number(readFileSync5(LOCK_FILE, "utf-8").trim().split(/\s+/)[1] ?? 0);
7131
+ if (!Number.isFinite(ts) || Date.now() - ts >= LOCK_STALE_MS) return null;
7132
+ return ts;
7133
+ } catch {
7134
+ return null;
7135
+ }
7136
+ }
7137
+ function takeInstallLock() {
7138
+ try {
7139
+ mkdirSync4(join5(homedir3(), ".solongate"), { recursive: true });
7140
+ writeFileSync4(LOCK_FILE, `${process.pid} ${Date.now()}
7141
+ `);
7142
+ } catch {
7143
+ }
7144
+ }
7145
+ function releaseInstallLock() {
7146
+ try {
7147
+ writeFileSync4(LOCK_FILE, `${process.pid} 0
7148
+ `);
7149
+ } catch {
7150
+ }
7151
+ }
7128
7152
  function readState() {
7129
7153
  try {
7130
7154
  const s = JSON.parse(readFileSync5(STATE_FILE, "utf-8"));
@@ -7191,8 +7215,10 @@ async function fetchLatest() {
7191
7215
  }
7192
7216
  }
7193
7217
  function spawnGlobalInstall(version) {
7218
+ if (lockHeldAt() !== null) return false;
7194
7219
  try {
7195
7220
  mkdirSync4(join5(homedir3(), ".solongate"), { recursive: true });
7221
+ takeInstallLock();
7196
7222
  const log3 = openSync(LOG_FILE, "a");
7197
7223
  const p = spawn2("npm", ["install", "-g", `${PKG}@${version}`], {
7198
7224
  stdio: ["ignore", log3, log3],
@@ -7242,7 +7268,7 @@ async function runUpdateCommand() {
7242
7268
  return 1;
7243
7269
  }
7244
7270
  out2(` updating ${cur} -> ${latest} ...`);
7245
- const r = await runGlobalInstall2(latest);
7271
+ const r = await installGlobally(latest, out2);
7246
7272
  if (!r.ok) {
7247
7273
  if (r.needsAdmin) {
7248
7274
  writeState({ ...readState(), needsAdmin: latest });
@@ -7250,7 +7276,8 @@ async function runUpdateCommand() {
7250
7276
  out2(" Run these two:");
7251
7277
  for (const s of adminUpdateSteps()) out2(" " + s);
7252
7278
  } else {
7253
- out2(" update failed. Try: npm i -g @solongate/proxy@latest");
7279
+ out2(" update failed \u2014 the CLI may not be installed right now. Run:");
7280
+ out2(` npm i -g ${PKG}@${latest}`);
7254
7281
  }
7255
7282
  return 1;
7256
7283
  }
@@ -7300,6 +7327,26 @@ ${output}
7300
7327
  }
7301
7328
  });
7302
7329
  }
7330
+ async function installGlobally(version, out2) {
7331
+ const heldSince = lockHeldAt();
7332
+ if (heldSince !== null) {
7333
+ out2(" another install is already running \u2014 waiting for it to finish ...");
7334
+ const until = Date.now() + LOCK_WAIT_MS;
7335
+ while (Date.now() < until && lockHeldAt() !== null) await sleep(1e3);
7336
+ }
7337
+ takeInstallLock();
7338
+ try {
7339
+ let r = await runGlobalInstall2(version);
7340
+ if (!r.ok && !r.needsAdmin) {
7341
+ await sleep(2e3);
7342
+ out2(" install did not take \u2014 retrying once ...");
7343
+ r = await runGlobalInstall2(version);
7344
+ }
7345
+ return r;
7346
+ } finally {
7347
+ releaseInstallLock();
7348
+ }
7349
+ }
7303
7350
  function adminInstallCommand() {
7304
7351
  return process.platform === "win32" ? `npm i -g ${PKG}@latest (in an Administrator terminal)` : `sudo npm i -g ${PKG}@latest`;
7305
7352
  }
@@ -7443,7 +7490,7 @@ function maybeSelfUpdate(notify = (l) => process.stderr.write(l + "\n")) {
7443
7490
  }
7444
7491
  })();
7445
7492
  }
7446
- var PKG, CHECK_EVERY_MS, ATTEMPT_EVERY_MS, STATE_FILE, LOG_FILE, NEEDS_ADMIN_RE, prefixCheck;
7493
+ var PKG, CHECK_EVERY_MS, ATTEMPT_EVERY_MS, STATE_FILE, LOG_FILE, LOCK_FILE, LOCK_STALE_MS, LOCK_WAIT_MS, sleep, NEEDS_ADMIN_RE, prefixCheck;
7447
7494
  var init_self_update = __esm({
7448
7495
  "src/self-update.ts"() {
7449
7496
  "use strict";
@@ -7453,6 +7500,10 @@ var init_self_update = __esm({
7453
7500
  ATTEMPT_EVERY_MS = 6 * 60 * 60 * 1e3;
7454
7501
  STATE_FILE = join5(homedir3(), ".solongate", ".self-update.json");
7455
7502
  LOG_FILE = join5(homedir3(), ".solongate", "self-update.log");
7503
+ LOCK_FILE = join5(homedir3(), ".solongate", ".update-install.lock");
7504
+ LOCK_STALE_MS = 3 * 6e4;
7505
+ LOCK_WAIT_MS = 9e4;
7506
+ sleep = (ms) => new Promise((r) => setTimeout(r, ms));
7456
7507
  NEEDS_ADMIN_RE = /\bEACCES\b|\bEPERM\b|permission denied|operation not permitted/i;
7457
7508
  prefixCheck = null;
7458
7509
  }
@@ -13041,13 +13092,15 @@ async function run2(argv) {
13041
13092
  return 0;
13042
13093
  }
13043
13094
  case "activate": {
13044
- if (flagBool(flags, "clear")) {
13095
+ if (flagBool(flags, "off") || flagBool(flags, "clear")) {
13045
13096
  const res2 = await api.policies.setActive(null);
13046
13097
  if (json) return printJson(res2), 0;
13047
- return err(green(" \u2713 Cleared active-policy pin.")), 0;
13098
+ err(green(" \u2713 Deactivated - this project now enforces no policy."));
13099
+ err(dim(" Pin one again with: solongate policy activate <id>"));
13100
+ return 0;
13048
13101
  }
13049
13102
  const id = positionals[1];
13050
- if (!id) return err(" Usage: policy activate <id> | policy activate --clear"), 1;
13103
+ if (!id) return err(" Usage: policy activate <id> | policy activate --off"), 1;
13051
13104
  const res = await api.policies.setActive(id);
13052
13105
  if (json) return printJson(res), 0;
13053
13106
  err(green(` \u2713 Pinned active policy \u2192 ${res.active}`));
@@ -13116,7 +13169,7 @@ var init_policy = __esm({
13116
13169
  ["policy deny <id> [--command|--path|--url <val>]", "append a DENY rule"],
13117
13170
  ["policy revoke <id> <ruleId>", "remove a rule"],
13118
13171
  ["policy active", "show the resolved active policy"],
13119
- ["policy activate <id> | --clear", "pin / unpin the active policy"],
13172
+ ["policy activate <id> | --off", "pin the active policy, or enforce nothing"],
13120
13173
  ["policy dry-run <id|file.json> [--limit N] [--mode denylist|whitelist]", "replay recent traffic against rules"]
13121
13174
  ], "Add --json to any read command for machine-readable output.");
13122
13175
  }
@@ -17464,7 +17517,7 @@ function printHelp() {
17464
17517
  cmd("policy allow <id> [--command|--path|--url <val>]", "add an ALLOW rule");
17465
17518
  cmd("policy deny <id> [--command|--path|--url <val>]", "add a DENY rule");
17466
17519
  cmd("policy revoke <id> <ruleId>", "remove a rule");
17467
- cmd("policy activate <id> | --clear", "pin / unpin the active policy");
17520
+ cmd("policy activate <id> | --off", "pin the active policy, or enforce nothing");
17468
17521
  cmd("policy active", "show the resolved active policy");
17469
17522
  cmd("policy dry-run <id|file.json> [--mode denylist|whitelist]", "replay recent traffic against rules");
17470
17523
  head("Rate limits");
@@ -17553,7 +17606,7 @@ async function main() {
17553
17606
  }
17554
17607
  if (IS_HUMAN_CLI) {
17555
17608
  const tuiBound = subcommand === "dataroom" || process.argv.length <= 2 && process.stdout.isTTY && process.stdin.isTTY;
17556
- if (!tuiBound) {
17609
+ if (!tuiBound && subcommand !== "update") {
17557
17610
  const { maybeSelfUpdate: maybeSelfUpdate2 } = await Promise.resolve().then(() => (init_self_update(), self_update_exports));
17558
17611
  maybeSelfUpdate2();
17559
17612
  }
package/dist/tui/index.js CHANGED
@@ -4720,6 +4720,8 @@ var CHECK_EVERY_MS = 30 * 60 * 1e3;
4720
4720
  var ATTEMPT_EVERY_MS = 6 * 60 * 60 * 1e3;
4721
4721
  var STATE_FILE2 = join9(homedir9(), ".solongate", ".self-update.json");
4722
4722
  var LOG_FILE2 = join9(homedir9(), ".solongate", "self-update.log");
4723
+ var LOCK_FILE = join9(homedir9(), ".solongate", ".update-install.lock");
4724
+ var LOCK_STALE_MS = 3 * 6e4;
4723
4725
  var NEEDS_ADMIN_RE = /\bEACCES\b|\bEPERM\b|permission denied|operation not permitted/i;
4724
4726
  function readState2() {
4725
4727
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.83.31",
3
+ "version": "0.83.33",
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": {
@@ -61,12 +61,12 @@
61
61
  "node": ">=20.0.0"
62
62
  },
63
63
  "optionalDependencies": {
64
- "@solongate/guard-linux-x64": "0.83.31",
65
- "@solongate/guard-linux-arm64": "0.83.31",
66
- "@solongate/guard-darwin-x64": "0.83.31",
67
- "@solongate/guard-darwin-arm64": "0.83.31",
68
- "@solongate/guard-win32-x64": "0.83.31",
69
- "@solongate/guard-win32-arm64": "0.83.31"
64
+ "@solongate/guard-linux-x64": "0.83.33",
65
+ "@solongate/guard-linux-arm64": "0.83.33",
66
+ "@solongate/guard-darwin-x64": "0.83.33",
67
+ "@solongate/guard-darwin-arm64": "0.83.33",
68
+ "@solongate/guard-win32-x64": "0.83.33",
69
+ "@solongate/guard-win32-arm64": "0.83.33"
70
70
  },
71
71
  "dependencies": {
72
72
  "@modelcontextprotocol/sdk": "^1.26.0",