@solongate/proxy 0.83.4 → 0.83.5

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.
@@ -8,13 +8,5 @@ export interface Me {
8
8
  name?: string;
9
9
  slug?: string;
10
10
  } | null;
11
- /**
12
- * Present only for a guest account, and carrying only whether the allowance is
13
- * spent. The size of it and how much is left are the operator's numbers and
14
- * are not sent here.
15
- */
16
- guest?: {
17
- exhausted?: boolean;
18
- } | null;
19
11
  }
20
12
  export declare function me(): Promise<Me>;
@@ -18,16 +18,6 @@ export declare function saveAccount(acc: SavedAccount): void;
18
18
  /** Remove a saved account from this device (does NOT revoke the cloud key). */
19
19
  export declare function removeAccount(apiKey: string): void;
20
20
  export declare function setViewCredentials(creds: Credentials | null): void;
21
- /** The unroutable address a guest ("Skip for now") account is stored under. */
22
- export declare const GUEST_EMAIL_SUFFIX = "@anonymous.solongate.invalid";
23
- /**
24
- * True when the account this device is enforcing with is a guest one.
25
- *
26
- * A guest has no dashboard rights beyond reading, and the CLI is the same
27
- * account reached a different way, so the management commands answer the same
28
- * way the dashboard does rather than half-working here.
29
- */
30
- export declare function isGuestAccount(): boolean;
31
21
  /** The key this device enforces with, or null when it is not paired. */
32
22
  export declare function enforcingKey(): string | null;
33
23
  /** True when the given key is the ACTIVE one the guard hooks use. */
@@ -56,10 +46,6 @@ export declare class ApiError extends Error {
56
46
  readonly code: string;
57
47
  constructor(status: number, code: string, message: string);
58
48
  }
59
- /** Raised when a guest account tries to change something. */
60
- export declare class GuestReadOnlyError extends Error {
61
- constructor();
62
- }
63
49
  /** Raised when no API key can be found — tells the user to run `solongate` and pair. */
64
50
  export declare class NotAuthenticatedError extends Error {
65
51
  constructor();
@@ -135,31 +135,7 @@ function installedGuardVersion() {
135
135
 
136
136
  // src/api-client/client.ts
137
137
  var DEFAULT_API_URL = "https://api.solongate.com";
138
- var accountsFile = () => join2(homedir2(), ".solongate", "accounts.json");
139
- function listAccounts() {
140
- let list5 = [];
141
- try {
142
- const raw = JSON.parse(readFileSync2(accountsFile(), "utf-8"));
143
- if (Array.isArray(raw)) list5 = raw.filter((a) => a && typeof a.apiKey === "string");
144
- } catch {
145
- }
146
- const active2 = loginCredentialFile();
147
- if (active2.apiKey && !list5.some((a) => a.apiKey === active2.apiKey)) {
148
- list5.unshift({ apiKey: active2.apiKey, apiUrl: active2.apiUrl || DEFAULT_API_URL });
149
- }
150
- return list5;
151
- }
152
138
  var viewOverride = null;
153
- var GUEST_EMAIL_SUFFIX = "@anonymous.solongate.invalid";
154
- function isGuestAccount() {
155
- try {
156
- const active2 = loginCredentialFile().apiKey;
157
- if (!active2) return false;
158
- return listAccounts().some((a) => a.apiKey === active2 && (a.email ?? "").endsWith(GUEST_EMAIL_SUFFIX));
159
- } catch {
160
- return false;
161
- }
162
- }
163
139
  var ApiError = class extends Error {
164
140
  status;
165
141
  code;
@@ -170,12 +146,6 @@ var ApiError = class extends Error {
170
146
  this.code = code;
171
147
  }
172
148
  };
173
- var GuestReadOnlyError = class extends Error {
174
- constructor() {
175
- super("Guest account: sign up at auth.solongate.com to change anything. Your project, its policy and everything recorded carry over.");
176
- this.name = "GuestReadOnlyError";
177
- }
178
- };
179
149
  var NotAuthenticatedError = class extends Error {
180
150
  constructor() {
181
151
  super("Not logged in. Run `solongate` and log in from the Accounts panel.");
@@ -240,9 +210,6 @@ function buildUrl(base, path, query) {
240
210
  return url.toString();
241
211
  }
242
212
  async function request(method, path, opts = {}) {
243
- if (method !== "GET" && isGuestAccount()) {
244
- throw new GuestReadOnlyError();
245
- }
246
213
  const creds = resolveCredentials(opts.apiUrl);
247
214
  const url = buildUrl(creds.apiUrl, path, opts.query);
248
215
  const headers = {
package/dist/index.js CHANGED
@@ -7716,26 +7716,6 @@ var init_local_log = __esm({
7716
7716
  });
7717
7717
 
7718
7718
  // src/api-client/client.ts
7719
- var client_exports = {};
7720
- __export(client_exports, {
7721
- ApiError: () => ApiError,
7722
- DEFAULT_API_URL: () => DEFAULT_API_URL2,
7723
- GUEST_EMAIL_SUFFIX: () => GUEST_EMAIL_SUFFIX,
7724
- GuestReadOnlyError: () => GuestReadOnlyError,
7725
- NotAuthenticatedError: () => NotAuthenticatedError,
7726
- clearActiveCredential: () => clearActiveCredential,
7727
- enforcingKey: () => enforcingKey,
7728
- isActiveAccount: () => isActiveAccount,
7729
- isAuthenticated: () => isAuthenticated,
7730
- isGuestAccount: () => isGuestAccount,
7731
- listAccounts: () => listAccounts,
7732
- removeAccount: () => removeAccount,
7733
- request: () => request,
7734
- resolveCredentials: () => resolveCredentials,
7735
- saveAccount: () => saveAccount,
7736
- setActiveAccount: () => setActiveAccount,
7737
- setViewCredentials: () => setViewCredentials
7738
- });
7739
7719
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync5 } from "fs";
7740
7720
  import { resolve as resolve4, join as join9 } from "path";
7741
7721
  import { homedir as homedir7 } from "os";
@@ -7787,15 +7767,6 @@ function setViewCredentials(creds) {
7787
7767
  viewOverride = creds;
7788
7768
  cached2 = null;
7789
7769
  }
7790
- function isGuestAccount() {
7791
- try {
7792
- const active2 = loginCredentialFile().apiKey;
7793
- if (!active2) return false;
7794
- return listAccounts().some((a) => a.apiKey === active2 && (a.email ?? "").endsWith(GUEST_EMAIL_SUFFIX));
7795
- } catch {
7796
- return false;
7797
- }
7798
- }
7799
7770
  function enforcingKey() {
7800
7771
  return loginCredentialFile().apiKey ?? null;
7801
7772
  }
@@ -7898,9 +7869,6 @@ function buildUrl(base, path, query) {
7898
7869
  return url.toString();
7899
7870
  }
7900
7871
  async function request(method, path, opts = {}) {
7901
- if (method !== "GET" && isGuestAccount()) {
7902
- throw new GuestReadOnlyError();
7903
- }
7904
7872
  const creds = resolveCredentials(opts.apiUrl);
7905
7873
  const url = buildUrl(creds.apiUrl, path, opts.query);
7906
7874
  const headers = {
@@ -7956,7 +7924,7 @@ async function request(method, path, opts = {}) {
7956
7924
  }
7957
7925
  return json;
7958
7926
  }
7959
- var DEFAULT_API_URL2, accountsFile, viewOverride, GUEST_EMAIL_SUFFIX, ApiError, GuestReadOnlyError, NotAuthenticatedError, cached2;
7927
+ var DEFAULT_API_URL2, accountsFile, viewOverride, ApiError, NotAuthenticatedError, cached2;
7960
7928
  var init_client = __esm({
7961
7929
  "src/api-client/client.ts"() {
7962
7930
  "use strict";
@@ -7964,7 +7932,6 @@ var init_client = __esm({
7964
7932
  DEFAULT_API_URL2 = "https://api.solongate.com";
7965
7933
  accountsFile = () => join9(homedir7(), ".solongate", "accounts.json");
7966
7934
  viewOverride = null;
7967
- GUEST_EMAIL_SUFFIX = "@anonymous.solongate.invalid";
7968
7935
  ApiError = class extends Error {
7969
7936
  status;
7970
7937
  code;
@@ -7975,12 +7942,6 @@ var init_client = __esm({
7975
7942
  this.code = code;
7976
7943
  }
7977
7944
  };
7978
- GuestReadOnlyError = class extends Error {
7979
- constructor() {
7980
- super("Guest account: sign up at auth.solongate.com to change anything. Your project, its policy and everything recorded carry over.");
7981
- this.name = "GuestReadOnlyError";
7982
- }
7983
- };
7984
7945
  NotAuthenticatedError = class extends Error {
7985
7946
  constructor() {
7986
7947
  super("Not logged in. Run `solongate` and log in from the Accounts panel.");
@@ -9321,18 +9282,18 @@ function LivePanel({ active: active2 }) {
9321
9282
  }
9322
9283
  ) : null,
9323
9284
  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,
9324
- /* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, children: [
9325
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
9285
+ /* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, overflow: "hidden", children: [
9286
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, height: 1 + chartH, marginRight: 2, overflow: "hidden", children: [
9326
9287
  /* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
9327
9288
  /* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
9328
9289
  ] }),
9329
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
9290
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, height: 1 + chartH, overflow: "hidden", children: [
9330
9291
  /* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `now ${latNow}ms \xB7 med ${latMed}ms \xB7 amber >${Math.round(latHotAt)}ms`, width: rightW }),
9331
9292
  /* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "white", hotColor: "#ffb454" })
9332
9293
  ] })
9333
9294
  ] }),
9334
- /* @__PURE__ */ jsxs2(Box2, { height: colH, children: [
9335
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
9295
+ /* @__PURE__ */ jsxs2(Box2, { height: colH, overflow: "hidden", children: [
9296
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, height: colH, marginRight: 2, overflow: "hidden", children: [
9336
9297
  /* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", extra: "l = inspect", width: colW }),
9337
9298
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
9338
9299
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "RATELIMIT ".padEnd(10) }),
@@ -9345,7 +9306,11 @@ function LivePanel({ active: active2 }) {
9345
9306
  /* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: (dl?.mode ?? "?").padEnd(7) }),
9346
9307
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${(dl?.patterns ?? []).length} builtin \xB7 ${(dl?.custom ?? []).length} custom` })
9347
9308
  ] }),
9348
- dlpBars.length ? dlpBars.map((d2) => /* @__PURE__ */ jsx2(HBar, { label: truncate2(d2.pattern, 10), value: d2.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d2.pattern)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " no dlp hits in last 7 days" }),
9309
+ dlpBars.length ? dlpBars.map((d2) => /* @__PURE__ */ jsx2(HBar, { label: truncate2(d2.pattern, 10), value: d2.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d2.pattern)) : (
9310
+ // 36 chars against a colW that is ~31 at cols=100: without truncate
9311
+ // this wrapped onto a second line and pushed the column over colH.
9312
+ /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " no dlp hits in last 7 days" })
9313
+ ),
9349
9314
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
9350
9315
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
9351
9316
  ring ? /* @__PURE__ */ jsxs2(Text2, { color: theme.ok, children: [
@@ -9354,7 +9319,7 @@ function LivePanel({ active: active2 }) {
9354
9319
  ] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no local ring in cwd" })
9355
9320
  ] })
9356
9321
  ] }),
9357
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
9322
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, height: colH, marginRight: 2, overflow: "hidden", children: [
9358
9323
  /* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
9359
9324
  pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
9360
9325
  pickable.map((r, i) => {
@@ -9380,7 +9345,7 @@ function LivePanel({ active: active2 }) {
9380
9345
  ] }, r.id);
9381
9346
  })
9382
9347
  ] }),
9383
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, overflow: "hidden", children: [
9348
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, height: colH, overflow: "hidden", children: [
9384
9349
  /* @__PURE__ */ jsx2(PaneTitle, { label: "EVENT LOG", extra: "system heartbeat", width: colW }),
9385
9350
  log3.slice(-(colH - 1)).map((l, i) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
9386
9351
  /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
@@ -11587,26 +11552,7 @@ var init_doctor = __esm({
11587
11552
  import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
11588
11553
  import TextInput6 from "ink-text-input";
11589
11554
  import { useEffect as useEffect7, useRef as useRef3, useState as useState8 } from "react";
11590
- import { readFileSync as readFileSync11, readdirSync as readdirSync2 } from "fs";
11591
- import { homedir as homedir11 } from "os";
11592
- import { join as join13 } from "path";
11593
11555
  import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
11594
- function guestAllowance() {
11595
- try {
11596
- const dir = join13(homedir11(), ".solongate");
11597
- for (const f of readdirSync2(dir)) {
11598
- if (!f.startsWith(".policy-cache-") || !f.endsWith(".json")) continue;
11599
- try {
11600
- const c2 = JSON.parse(readFileSync11(join13(dir, f), "utf-8"));
11601
- const q = c2?.security?.guestQuota;
11602
- if (q && typeof q.limit === "number") return { used: q.used ?? 0, limit: q.limit };
11603
- } catch {
11604
- }
11605
- }
11606
- } catch {
11607
- }
11608
- return null;
11609
- }
11610
11556
  function SettingsPanel({
11611
11557
  active: active2,
11612
11558
  focused,
@@ -11655,7 +11601,6 @@ function SettingsPanel({
11655
11601
  const step = setInterval(() => setDiagStep((s) => Math.min(s + 1, total - 1)), 220);
11656
11602
  return () => clearInterval(step);
11657
11603
  }, [diagBusy]);
11658
- void guestAllowance;
11659
11604
  const ver = currentVersion();
11660
11605
  useEffect7(() => {
11661
11606
  let live2 = true;
@@ -12220,15 +12165,6 @@ function SettingsPanel({
12220
12165
  cursor(r),
12221
12166
  /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
12222
12167
  ] });
12223
- case "allowance": {
12224
- const left = Math.max(0, r.limit - r.used);
12225
- return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
12226
- cursor(r),
12227
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "allowance".padEnd(11) }),
12228
- /* @__PURE__ */ jsx8(Text8, { color: left === 0 ? theme.bad : left <= 20 ? theme.warn : theme.ok, bold: true, children: `${r.used}/${r.limit} tool calls` }),
12229
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: left === 0 ? " used up \u2014 every call is denied \xB7 create an account to carry on" : ` ${left} left on this guest account \xB7 create an account for no limit` })
12230
- ] });
12231
- }
12232
12168
  case "guard":
12233
12169
  return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
12234
12170
  cursor(r),
@@ -12334,7 +12270,7 @@ function SettingsPanel({
12334
12270
  ] });
12335
12271
  }
12336
12272
  };
12337
- const sectionOf = (r) => r.kind === "acct" || r.kind === "acct-add" || r.kind === "allowance" ? "ACCOUNTS" : r.kind === "guard" || r.kind === "self" || r.kind === "doctor" || r.kind === "repair" ? "PROTECTION" : r.kind === "cli-update" || r.kind === "auto-update" ? "UPDATES" : r.kind === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
12273
+ const sectionOf = (r) => r.kind === "acct" || r.kind === "acct-add" ? "ACCOUNTS" : r.kind === "guard" || r.kind === "self" || r.kind === "doctor" || r.kind === "repair" ? "PROTECTION" : r.kind === "cli-update" || r.kind === "auto-update" ? "UPDATES" : r.kind === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
12338
12274
  const SECTION_DESC = {
12339
12275
  ACCOUNTS: `on this device (${accounts.length}) \xB7 \u25CF viewing \xB7 ACTIVE = guard key \xB7 x removes`,
12340
12276
  PROTECTION: "guard hook: enter install/update \xB7 d remove \xB7 self-protection \xB7 doctor + repair",
@@ -12528,23 +12464,7 @@ function App() {
12528
12464
  const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
12529
12465
  const cur = accounts[acctIdx];
12530
12466
  const rawEmail = cur?.email ?? "";
12531
- const isGuestAccount2 = rawEmail.endsWith("@anonymous.solongate.invalid");
12532
- const acctLabel2 = !cur ? "not logged in" : isGuestAccount2 ? "Anonymous" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
12533
- const [guestSpent, setGuestSpent] = useState9(false);
12534
- useEffect8(() => {
12535
- if (!isGuestAccount2) {
12536
- setGuestSpent(false);
12537
- return;
12538
- }
12539
- let alive = true;
12540
- api.auth.me().then((r) => {
12541
- if (alive) setGuestSpent(r.guest?.exhausted === true);
12542
- }).catch(() => {
12543
- });
12544
- return () => {
12545
- alive = false;
12546
- };
12547
- }, [isGuestAccount2, cur?.apiKey]);
12467
+ const acctLabel2 = !cur ? "not logged in" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
12548
12468
  useEffect8(() => {
12549
12469
  if (!cur || cur.email) return;
12550
12470
  let alive = true;
@@ -12630,12 +12550,7 @@ function App() {
12630
12550
  /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
12631
12551
  /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
12632
12552
  /* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
12633
- locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : guestSpent ? (
12634
- // Said on this line rather than on a new one: every panel budgets its
12635
- // height against a fixed shell, so one more line up here silently clips
12636
- // a row off whichever panel is open.
12637
- /* @__PURE__ */ jsx9(Text9, { color: theme.bad, bold: true, children: " \xB7 allowance used up \xB7 every call denied \xB7 create an account to carry on" })
12638
- ) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Settings to add another" }),
12553
+ locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Settings to add another" }),
12639
12554
  update2.kind === "updating" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 updating to v${update2.version}\u2026` }) : update2.kind === "updated" ? /* @__PURE__ */ jsx9(Text9, { color: theme.ok, bold: true, children: ` \u2191 v${update2.version} installed \xB7 restart (q, then solongate) to apply` }) : update2.kind === "available" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 v${update2.version} out \xB7 Settings \u2192 UPDATES` }) : update2.kind === "needs-admin" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 v${update2.version} needs admin rights \xB7 Settings \u2192 UPDATES` }) : null
12640
12555
  ] }),
12641
12556
  /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
@@ -12713,8 +12628,8 @@ __export(tui_exports, {
12713
12628
  launchTui: () => launchTui
12714
12629
  });
12715
12630
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync9 } from "fs";
12716
- import { homedir as homedir12 } from "os";
12717
- import { join as join14 } from "path";
12631
+ import { homedir as homedir11 } from "os";
12632
+ import { join as join13 } from "path";
12718
12633
  import { render } from "ink";
12719
12634
  import { jsx as jsx10 } from "react/jsx-runtime";
12720
12635
  async function launchTui() {
@@ -12725,11 +12640,11 @@ async function launchTui() {
12725
12640
  return;
12726
12641
  }
12727
12642
  process.stdout.write("\x1B[?1049h\x1B[H");
12728
- const debugLog = join14(homedir12(), ".solongate", "dataroom-debug.log");
12643
+ const debugLog = join13(homedir11(), ".solongate", "dataroom-debug.log");
12729
12644
  const saved = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug };
12730
12645
  const toFile = (level) => (...args) => {
12731
12646
  try {
12732
- mkdirSync9(join14(homedir12(), ".solongate"), { recursive: true });
12647
+ mkdirSync9(join13(homedir11(), ".solongate"), { recursive: true });
12733
12648
  appendFileSync2(debugLog, `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
12734
12649
  `);
12735
12650
  } catch {
@@ -12756,7 +12671,7 @@ var init_tui = __esm({
12756
12671
  });
12757
12672
 
12758
12673
  // src/commands/policy.ts
12759
- import { readFileSync as readFileSync12 } from "fs";
12674
+ import { readFileSync as readFileSync11 } from "fs";
12760
12675
  async function run2(argv) {
12761
12676
  const { positionals, flags } = parse(argv);
12762
12677
  const sub = positionals[0];
@@ -12913,7 +12828,7 @@ function printRules(rules) {
12913
12828
  }
12914
12829
  async function resolveRules(target) {
12915
12830
  if (target.endsWith(".json")) {
12916
- const parsed = JSON.parse(readFileSync12(target, "utf-8"));
12831
+ const parsed = JSON.parse(readFileSync11(target, "utf-8"));
12917
12832
  return parsed.rules ?? [];
12918
12833
  }
12919
12834
  const p = await api.policies.get(target);
@@ -13627,10 +13542,10 @@ __export(logs_server_exports, {
13627
13542
  runLogsServer: () => runLogsServer
13628
13543
  });
13629
13544
  import { createServer } from "http";
13630
- import { readFileSync as readFileSync13, statSync as statSync4 } from "fs";
13631
- import { resolve as resolve5, join as join15, isAbsolute as isAbsolute2 } from "path";
13632
- import { homedir as homedir13 } from "os";
13633
- import { readdirSync as readdirSync3 } from "fs";
13545
+ import { readFileSync as readFileSync12, statSync as statSync4 } from "fs";
13546
+ import { resolve as resolve5, join as join14, isAbsolute as isAbsolute2 } from "path";
13547
+ import { homedir as homedir12 } from "os";
13548
+ import { readdirSync as readdirSync2 } from "fs";
13634
13549
  function allowedOrigins() {
13635
13550
  const base = [
13636
13551
  "https://dashboard.solongate.com",
@@ -13646,15 +13561,15 @@ function resolveLocalLogDir(rawPath) {
13646
13561
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
13647
13562
  if (!dir) return null;
13648
13563
  if (isAbsolute2(dir)) return dir;
13649
- return resolve5(homedir13(), ".solongate", "local-logs");
13564
+ return resolve5(homedir12(), ".solongate", "local-logs");
13650
13565
  }
13651
13566
  async function findLogDir() {
13652
- const base = resolve5(homedir13(), ".solongate");
13567
+ const base = resolve5(homedir12(), ".solongate");
13653
13568
  try {
13654
- const files = readdirSync3(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
13569
+ const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
13655
13570
  for (const f of files) {
13656
13571
  try {
13657
- const c2 = JSON.parse(readFileSync13(join15(base, f), "utf-8"));
13572
+ const c2 = JSON.parse(readFileSync12(join14(base, f), "utf-8"));
13658
13573
  const p = c2?.security?.localLogs?.path;
13659
13574
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
13660
13575
  } catch {
@@ -13663,7 +13578,7 @@ async function findLogDir() {
13663
13578
  } catch {
13664
13579
  }
13665
13580
  try {
13666
- const cfgRaw = readFileSync13(join15(base, "cloud-guard.json"), "utf-8");
13581
+ const cfgRaw = readFileSync12(join14(base, "cloud-guard.json"), "utf-8");
13667
13582
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
13668
13583
  if (apiKey) {
13669
13584
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -13692,7 +13607,7 @@ function setCors(req, res) {
13692
13607
  }
13693
13608
  function fileInfo(dir) {
13694
13609
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
13695
- const file = join15(dir, LOG_FILENAME);
13610
+ const file = join14(dir, LOG_FILENAME);
13696
13611
  try {
13697
13612
  const st = statSync4(file);
13698
13613
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -13775,7 +13690,7 @@ async function runLogsServer() {
13775
13690
  return;
13776
13691
  }
13777
13692
  try {
13778
- const text = readFileSync13(info.file, "utf-8");
13693
+ const text = readFileSync12(info.file, "utf-8");
13779
13694
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Last-Modified": lastMod, "X-Solongate-Exists": "1" });
13780
13695
  res.end(text);
13781
13696
  } catch {
@@ -13828,9 +13743,9 @@ var init_logs_server = __esm({
13828
13743
  });
13829
13744
 
13830
13745
  // src/index.ts
13831
- import { readFileSync as readFileSync14 } from "fs";
13746
+ import { readFileSync as readFileSync13 } from "fs";
13832
13747
  import { fileURLToPath as fileURLToPath4 } from "url";
13833
- import { dirname as dirname4, join as join16 } from "path";
13748
+ import { dirname as dirname4, join as join15 } from "path";
13834
13749
 
13835
13750
  // src/config.ts
13836
13751
  import { readFileSync, existsSync } from "fs";
@@ -17225,8 +17140,8 @@ if (!IS_HUMAN_CLI) {
17225
17140
  }
17226
17141
  var PKG_VERSION = (() => {
17227
17142
  try {
17228
- const p = join16(dirname4(fileURLToPath4(import.meta.url)), "..", "package.json");
17229
- return JSON.parse(readFileSync14(p, "utf-8")).version || "unknown";
17143
+ const p = join15(dirname4(fileURLToPath4(import.meta.url)), "..", "package.json");
17144
+ return JSON.parse(readFileSync13(p, "utf-8")).version || "unknown";
17230
17145
  } catch {
17231
17146
  return "unknown";
17232
17147
  }
@@ -17397,13 +17312,6 @@ async function main() {
17397
17312
  }
17398
17313
  const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "sessions", "session", "doctor", "watch", "alerts", "webhooks"]);
17399
17314
  if (MGMT_COMMANDS.has(subcommand ?? "")) {
17400
- const { isGuestAccount: isGuestAccount2 } = await Promise.resolve().then(() => (init_client(), client_exports));
17401
- if (subcommand !== "doctor" && isGuestAccount2()) {
17402
- process.stderr.write(
17403
- "\n This is a guest account, which can look but not change.\n Create an account to use the management commands:\n https://auth.solongate.com\n\n Your project, its policy and everything already recorded carry over,\n and this device keeps working without being paired again.\n\n"
17404
- );
17405
- process.exit(1);
17406
- }
17407
17315
  const { runCommand: runCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
17408
17316
  const code = await runCommand2(subcommand, process.argv.slice(3));
17409
17317
  process.exit(code);
package/dist/tui/index.js CHANGED
@@ -530,8 +530,8 @@ var init_global_install = __esm({
530
530
 
531
531
  // src/tui/index.tsx
532
532
  import { appendFileSync, mkdirSync as mkdirSync7 } from "fs";
533
- import { homedir as homedir11 } from "os";
534
- import { join as join11 } from "path";
533
+ import { homedir as homedir10 } from "os";
534
+ import { join as join10 } from "path";
535
535
  import { render } from "ink";
536
536
 
537
537
  // src/tui/App.tsx
@@ -935,16 +935,6 @@ function setViewCredentials(creds) {
935
935
  viewOverride = creds;
936
936
  cached2 = null;
937
937
  }
938
- var GUEST_EMAIL_SUFFIX = "@anonymous.solongate.invalid";
939
- function isGuestAccount() {
940
- try {
941
- const active2 = loginCredentialFile().apiKey;
942
- if (!active2) return false;
943
- return listAccounts().some((a) => a.apiKey === active2 && (a.email ?? "").endsWith(GUEST_EMAIL_SUFFIX));
944
- } catch {
945
- return false;
946
- }
947
- }
948
938
  function enforcingKey() {
949
939
  return loginCredentialFile().apiKey ?? null;
950
940
  }
@@ -1000,12 +990,6 @@ var ApiError = class extends Error {
1000
990
  this.code = code;
1001
991
  }
1002
992
  };
1003
- var GuestReadOnlyError = class extends Error {
1004
- constructor() {
1005
- super("Guest account: sign up at auth.solongate.com to change anything. Your project, its policy and everything recorded carry over.");
1006
- this.name = "GuestReadOnlyError";
1007
- }
1008
- };
1009
993
  var NotAuthenticatedError = class extends Error {
1010
994
  constructor() {
1011
995
  super("Not logged in. Run `solongate` and log in from the Accounts panel.");
@@ -1070,9 +1054,6 @@ function buildUrl(base, path, query) {
1070
1054
  return url.toString();
1071
1055
  }
1072
1056
  async function request(method, path, opts = {}) {
1073
- if (method !== "GET" && isGuestAccount()) {
1074
- throw new GuestReadOnlyError();
1075
- }
1076
1057
  const creds = resolveCredentials(opts.apiUrl);
1077
1058
  const url = buildUrl(creds.apiUrl, path, opts.query);
1078
1059
  const headers = {
@@ -2422,18 +2403,18 @@ function LivePanel({ active: active2 }) {
2422
2403
  }
2423
2404
  ) : null,
2424
2405
  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,
2425
- /* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, children: [
2426
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
2406
+ /* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, overflow: "hidden", children: [
2407
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, height: 1 + chartH, marginRight: 2, overflow: "hidden", children: [
2427
2408
  /* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
2428
2409
  /* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
2429
2410
  ] }),
2430
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
2411
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, height: 1 + chartH, overflow: "hidden", children: [
2431
2412
  /* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `now ${latNow}ms \xB7 med ${latMed}ms \xB7 amber >${Math.round(latHotAt)}ms`, width: rightW }),
2432
2413
  /* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "white", hotColor: "#ffb454" })
2433
2414
  ] })
2434
2415
  ] }),
2435
- /* @__PURE__ */ jsxs2(Box2, { height: colH, children: [
2436
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
2416
+ /* @__PURE__ */ jsxs2(Box2, { height: colH, overflow: "hidden", children: [
2417
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, height: colH, marginRight: 2, overflow: "hidden", children: [
2437
2418
  /* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", extra: "l = inspect", width: colW }),
2438
2419
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
2439
2420
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "RATELIMIT ".padEnd(10) }),
@@ -2446,7 +2427,11 @@ function LivePanel({ active: active2 }) {
2446
2427
  /* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: (dl?.mode ?? "?").padEnd(7) }),
2447
2428
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${(dl?.patterns ?? []).length} builtin \xB7 ${(dl?.custom ?? []).length} custom` })
2448
2429
  ] }),
2449
- dlpBars.length ? dlpBars.map((d2) => /* @__PURE__ */ jsx2(HBar, { label: truncate(d2.pattern, 10), value: d2.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d2.pattern)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " no dlp hits in last 7 days" }),
2430
+ dlpBars.length ? dlpBars.map((d2) => /* @__PURE__ */ jsx2(HBar, { label: truncate(d2.pattern, 10), value: d2.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d2.pattern)) : (
2431
+ // 36 chars against a colW that is ~31 at cols=100: without truncate
2432
+ // this wrapped onto a second line and pushed the column over colH.
2433
+ /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " no dlp hits in last 7 days" })
2434
+ ),
2450
2435
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
2451
2436
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
2452
2437
  ring ? /* @__PURE__ */ jsxs2(Text2, { color: theme.ok, children: [
@@ -2455,7 +2440,7 @@ function LivePanel({ active: active2 }) {
2455
2440
  ] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no local ring in cwd" })
2456
2441
  ] })
2457
2442
  ] }),
2458
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
2443
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, height: colH, marginRight: 2, overflow: "hidden", children: [
2459
2444
  /* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
2460
2445
  pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
2461
2446
  pickable.map((r, i) => {
@@ -2481,7 +2466,7 @@ function LivePanel({ active: active2 }) {
2481
2466
  ] }, r.id);
2482
2467
  })
2483
2468
  ] }),
2484
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, overflow: "hidden", children: [
2469
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, height: colH, overflow: "hidden", children: [
2485
2470
  /* @__PURE__ */ jsx2(PaneTitle, { label: "EVENT LOG", extra: "system heartbeat", width: colW }),
2486
2471
  log.slice(-(colH - 1)).map((l, i) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
2487
2472
  /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
@@ -4677,26 +4662,7 @@ async function tuiUpdateFlow(onStatus) {
4677
4662
  }
4678
4663
 
4679
4664
  // src/tui/panels/Settings.tsx
4680
- import { readFileSync as readFileSync8, readdirSync as readdirSync2 } from "fs";
4681
- import { homedir as homedir10 } from "os";
4682
- import { join as join10 } from "path";
4683
4665
  import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
4684
- function guestAllowance() {
4685
- try {
4686
- const dir = join10(homedir10(), ".solongate");
4687
- for (const f of readdirSync2(dir)) {
4688
- if (!f.startsWith(".policy-cache-") || !f.endsWith(".json")) continue;
4689
- try {
4690
- const c2 = JSON.parse(readFileSync8(join10(dir, f), "utf-8"));
4691
- const q = c2?.security?.guestQuota;
4692
- if (q && typeof q.limit === "number") return { used: q.used ?? 0, limit: q.limit };
4693
- } catch {
4694
- }
4695
- }
4696
- } catch {
4697
- }
4698
- return null;
4699
- }
4700
4666
  var EVENTS = ["denials", "allowed", "all"];
4701
4667
  var SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
4702
4668
  var SIGNALS2 = ["any", "deny", "dlp", "ratelimit"];
@@ -4761,7 +4727,6 @@ function SettingsPanel({
4761
4727
  const step = setInterval(() => setDiagStep((s) => Math.min(s + 1, total - 1)), 220);
4762
4728
  return () => clearInterval(step);
4763
4729
  }, [diagBusy]);
4764
- void guestAllowance;
4765
4730
  const ver = currentVersion();
4766
4731
  useEffect7(() => {
4767
4732
  let live2 = true;
@@ -5326,15 +5291,6 @@ function SettingsPanel({
5326
5291
  cursor(r),
5327
5292
  /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
5328
5293
  ] });
5329
- case "allowance": {
5330
- const left = Math.max(0, r.limit - r.used);
5331
- return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
5332
- cursor(r),
5333
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "allowance".padEnd(11) }),
5334
- /* @__PURE__ */ jsx8(Text8, { color: left === 0 ? theme.bad : left <= 20 ? theme.warn : theme.ok, bold: true, children: `${r.used}/${r.limit} tool calls` }),
5335
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: left === 0 ? " used up \u2014 every call is denied \xB7 create an account to carry on" : ` ${left} left on this guest account \xB7 create an account for no limit` })
5336
- ] });
5337
- }
5338
5294
  case "guard":
5339
5295
  return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
5340
5296
  cursor(r),
@@ -5440,7 +5396,7 @@ function SettingsPanel({
5440
5396
  ] });
5441
5397
  }
5442
5398
  };
5443
- const sectionOf = (r) => r.kind === "acct" || r.kind === "acct-add" || r.kind === "allowance" ? "ACCOUNTS" : r.kind === "guard" || r.kind === "self" || r.kind === "doctor" || r.kind === "repair" ? "PROTECTION" : r.kind === "cli-update" || r.kind === "auto-update" ? "UPDATES" : r.kind === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
5399
+ const sectionOf = (r) => r.kind === "acct" || r.kind === "acct-add" ? "ACCOUNTS" : r.kind === "guard" || r.kind === "self" || r.kind === "doctor" || r.kind === "repair" ? "PROTECTION" : r.kind === "cli-update" || r.kind === "auto-update" ? "UPDATES" : r.kind === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
5444
5400
  const SECTION_DESC = {
5445
5401
  ACCOUNTS: `on this device (${accounts.length}) \xB7 \u25CF viewing \xB7 ACTIVE = guard key \xB7 x removes`,
5446
5402
  PROTECTION: "guard hook: enter install/update \xB7 d remove \xB7 self-protection \xB7 doctor + repair",
@@ -5610,23 +5566,7 @@ function App() {
5610
5566
  const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
5611
5567
  const cur = accounts[acctIdx];
5612
5568
  const rawEmail = cur?.email ?? "";
5613
- const isGuestAccount2 = rawEmail.endsWith("@anonymous.solongate.invalid");
5614
- const acctLabel2 = !cur ? "not logged in" : isGuestAccount2 ? "Anonymous" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
5615
- const [guestSpent, setGuestSpent] = useState9(false);
5616
- useEffect8(() => {
5617
- if (!isGuestAccount2) {
5618
- setGuestSpent(false);
5619
- return;
5620
- }
5621
- let alive = true;
5622
- api.auth.me().then((r) => {
5623
- if (alive) setGuestSpent(r.guest?.exhausted === true);
5624
- }).catch(() => {
5625
- });
5626
- return () => {
5627
- alive = false;
5628
- };
5629
- }, [isGuestAccount2, cur?.apiKey]);
5569
+ const acctLabel2 = !cur ? "not logged in" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
5630
5570
  useEffect8(() => {
5631
5571
  if (!cur || cur.email) return;
5632
5572
  let alive = true;
@@ -5712,12 +5652,7 @@ function App() {
5712
5652
  /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
5713
5653
  /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
5714
5654
  /* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
5715
- locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : guestSpent ? (
5716
- // Said on this line rather than on a new one: every panel budgets its
5717
- // height against a fixed shell, so one more line up here silently clips
5718
- // a row off whichever panel is open.
5719
- /* @__PURE__ */ jsx9(Text9, { color: theme.bad, bold: true, children: " \xB7 allowance used up \xB7 every call denied \xB7 create an account to carry on" })
5720
- ) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Settings to add another" }),
5655
+ locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Settings to add another" }),
5721
5656
  update2.kind === "updating" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 updating to v${update2.version}\u2026` }) : update2.kind === "updated" ? /* @__PURE__ */ jsx9(Text9, { color: theme.ok, bold: true, children: ` \u2191 v${update2.version} installed \xB7 restart (q, then solongate) to apply` }) : update2.kind === "available" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 v${update2.version} out \xB7 Settings \u2192 UPDATES` }) : update2.kind === "needs-admin" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 v${update2.version} needs admin rights \xB7 Settings \u2192 UPDATES` }) : null
5722
5657
  ] }),
5723
5658
  /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
@@ -5766,11 +5701,11 @@ async function launchTui() {
5766
5701
  return;
5767
5702
  }
5768
5703
  process.stdout.write("\x1B[?1049h\x1B[H");
5769
- const debugLog = join11(homedir11(), ".solongate", "dataroom-debug.log");
5704
+ const debugLog = join10(homedir10(), ".solongate", "dataroom-debug.log");
5770
5705
  const saved = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug };
5771
5706
  const toFile = (level) => (...args) => {
5772
5707
  try {
5773
- mkdirSync7(join11(homedir11(), ".solongate"), { recursive: true });
5708
+ mkdirSync7(join10(homedir10(), ".solongate"), { recursive: true });
5774
5709
  appendFileSync(debugLog, `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
5775
5710
  `);
5776
5711
  } catch {
@@ -6536,7 +6536,7 @@ import { resolve, join, dirname, isAbsolute } from "node:path";
6536
6536
  import { homedir } from "node:os";
6537
6537
  import { gunzipSync } from "node:zlib";
6538
6538
  import { createHash } from "node:crypto";
6539
- var HOOK_VERSION = 70;
6539
+ var HOOK_VERSION = 71;
6540
6540
  function localLogsOnly(security) {
6541
6541
  if (security && typeof security === "object") {
6542
6542
  const l = security.localLogs;
@@ -7951,53 +7951,10 @@ function rateLimitCheck(agentKey, limits) {
7951
7951
  return null;
7952
7952
  }
7953
7953
  }
7954
- function quotaTally(used) {
7955
- const dir = resolve(homedir(), ".solongate");
7956
- const markFile = join(dir, ".quota-mark");
7957
- const tallyFile = join(dir, ".quota-tally");
7958
- let n = 0;
7959
- try {
7960
- let mark = null;
7961
- if (existsSync(markFile)) {
7962
- try {
7963
- mark = readFileSync(markFile, "utf-8").trim();
7964
- } catch {
7965
- }
7966
- }
7967
- if (mark !== String(used)) {
7968
- writeFileSync(markFile, String(used));
7969
- writeFileSync(tallyFile, "");
7970
- } else if (existsSync(tallyFile)) {
7971
- try {
7972
- n = statSync(tallyFile).size;
7973
- } catch {
7974
- }
7975
- }
7976
- } catch {
7977
- }
7978
- return {
7979
- n,
7980
- add() {
7981
- try {
7982
- appendFileSync(tallyFile, ".");
7983
- } catch {
7984
- }
7985
- }
7986
- };
7987
- }
7988
7954
  function securityLayerCheck(toolName, args, cfg, agentKey) {
7989
7955
  if (!cfg)
7990
7956
  return null;
7991
7957
  try {
7992
- const q = cfg.guestQuota;
7993
- if (q) {
7994
- const tally = quotaTally(q.used ?? 0);
7995
- const spent = (q.used ?? 0) + tally.n;
7996
- if (q.exhausted || spent >= q.limit) {
7997
- return `Guest allowance used up (${q.limit} tool calls). Create an account at https://auth.solongate.com to carry on. Your project, its policy and everything recorded carry over, and this device keeps working. To stop guarding this machine instead: solongate, Settings, guard, then press d.`;
7998
- }
7999
- tally.add();
8000
- }
8001
7958
  if (cfg.dlpBlock) {
8002
7959
  const hit = dlpScan(args, cfg.dlpBlock);
8003
7960
  if (hit)
package/hooks/guard.mjs CHANGED
@@ -32,7 +32,7 @@ import { createHash } from 'node:crypto';
32
32
  // the installed hook self-updates when the cloud version is higher (see
33
33
  // maybeSelfUpdate). This is what makes guard fixes propagate without a manual
34
34
  // reinstall — the same trust model as the OPA WASM this hook already runs.
35
- const HOOK_VERSION = 70;
35
+ const HOOK_VERSION = 71;
36
36
 
37
37
  // True when local log storage is ON. In that mode logs are kept LOCAL ONLY and
38
38
  // nothing is sent to the cloud audit log.
@@ -1761,62 +1761,10 @@ function rateLimitCheck(agentKey, limits) {
1761
1761
  }
1762
1762
  }
1763
1763
 
1764
- // The allowance figure the API sends is built from audit rows, and those are
1765
- // written AFTER a call runs. Under a burst of parallel tool calls every hook
1766
- // therefore reads the same pre-burst figure, and the allowance overshoots by
1767
- // roughly a batch before the count catches up.
1768
- //
1769
- // The guard is the thing making the calls, so it can close that gap without
1770
- // another round trip: it tallies what it has already let through since the
1771
- // server's figure last moved, and adds it in. One byte is appended per call and
1772
- // the file's LENGTH is the count, because many hooks run at once and a
1773
- // read-modify-write would lose increments; an O_APPEND write of a single byte
1774
- // does not interleave.
1775
- function quotaTally(used) {
1776
- const dir = resolve(homedir(), '.solongate');
1777
- const markFile = join(dir, '.quota-mark');
1778
- const tallyFile = join(dir, '.quota-tally');
1779
- let n = 0;
1780
- try {
1781
- let mark = null;
1782
- if (existsSync(markFile)) { try { mark = readFileSync(markFile, 'utf-8').trim(); } catch {} }
1783
- // A figure that has moved means the audit rows caught up and absorbed the
1784
- // local tally, so it starts again from there.
1785
- if (mark !== String(used)) {
1786
- writeFileSync(markFile, String(used));
1787
- writeFileSync(tallyFile, '');
1788
- } else if (existsSync(tallyFile)) {
1789
- try { n = statSync(tallyFile).size; } catch {}
1790
- }
1791
- } catch { /* a missing tally only costs accuracy near the edge, never the gate */ }
1792
- return {
1793
- n,
1794
- add() { try { appendFileSync(tallyFile, '.'); } catch {} },
1795
- };
1796
- }
1797
-
1798
1764
  // Runs all enabled enforcement layers; returns a deny reason or null (allow).
1799
1765
  function securityLayerCheck(toolName, args, cfg, agentKey) {
1800
1766
  if (!cfg) return null;
1801
1767
  try {
1802
- // Guest allowance. A guest account is a trial, so past its allowance every
1803
- // call is denied without exception rather than quietly running unguarded:
1804
- // an agent that believes it is protected and is not is worse off than one
1805
- // that is stopped and told why. Checked first, so no other layer can let a
1806
- // call through after the allowance is gone.
1807
- const q = cfg.guestQuota;
1808
- if (q) {
1809
- // The server's figure plus what this device has run since, so a burst
1810
- // cannot spend past the allowance while the audit rows are still in flight.
1811
- const tally = quotaTally(q.used ?? 0);
1812
- const spent = (q.used ?? 0) + tally.n;
1813
- if (q.exhausted || spent >= q.limit) {
1814
- return `Guest allowance used up (${q.limit} tool calls). Create an account at https://auth.solongate.com to carry on. Your project, its policy and everything recorded carry over, and this device keeps working. To stop guarding this machine instead: solongate, Settings, guard, then press d.`;
1815
- }
1816
- // Counted here rather than after the verdict, because a call the policy
1817
- // goes on to deny is still recorded and still spends the allowance.
1818
- tally.add();
1819
- }
1820
1768
  if (cfg.dlpBlock) {
1821
1769
  const hit = dlpScan(args, cfg.dlpBlock);
1822
1770
  if (hit) return 'Security layer (DLP): blocked - arguments contain a ' + hit +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.83.4",
3
+ "version": "0.83.5",
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": {