@solongate/proxy 0.82.87 → 0.82.89

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.
@@ -18,6 +18,16 @@ 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;
21
31
  /** True when the given key is the ACTIVE one the guard hooks use. */
22
32
  export declare function isActiveAccount(apiKey: string): boolean;
23
33
  /**
@@ -44,6 +54,10 @@ export declare class ApiError extends Error {
44
54
  readonly code: string;
45
55
  constructor(status: number, code: string, message: string);
46
56
  }
57
+ /** Raised when a guest account tries to change something. */
58
+ export declare class GuestReadOnlyError extends Error {
59
+ constructor();
60
+ }
47
61
  /** Raised when no API key can be found — tells the user to run `solongate` and pair. */
48
62
  export declare class NotAuthenticatedError extends Error {
49
63
  constructor();
@@ -125,7 +125,31 @@ function codexHooksStatus() {
125
125
 
126
126
  // src/api-client/client.ts
127
127
  var DEFAULT_API_URL = "https://api.solongate.com";
128
+ var accountsFile = () => join2(homedir2(), ".solongate", "accounts.json");
129
+ function listAccounts() {
130
+ let list5 = [];
131
+ try {
132
+ const raw = JSON.parse(readFileSync2(accountsFile(), "utf-8"));
133
+ if (Array.isArray(raw)) list5 = raw.filter((a) => a && typeof a.apiKey === "string");
134
+ } catch {
135
+ }
136
+ const active2 = loginCredentialFile();
137
+ if (active2.apiKey && !list5.some((a) => a.apiKey === active2.apiKey)) {
138
+ list5.unshift({ apiKey: active2.apiKey, apiUrl: active2.apiUrl || DEFAULT_API_URL });
139
+ }
140
+ return list5;
141
+ }
128
142
  var viewOverride = null;
143
+ var GUEST_EMAIL_SUFFIX = "@anonymous.solongate.invalid";
144
+ function isGuestAccount() {
145
+ try {
146
+ const active2 = loginCredentialFile().apiKey;
147
+ if (!active2) return false;
148
+ return listAccounts().some((a) => a.apiKey === active2 && (a.email ?? "").endsWith(GUEST_EMAIL_SUFFIX));
149
+ } catch {
150
+ return false;
151
+ }
152
+ }
129
153
  var ApiError = class extends Error {
130
154
  status;
131
155
  code;
@@ -136,6 +160,12 @@ var ApiError = class extends Error {
136
160
  this.code = code;
137
161
  }
138
162
  };
163
+ var GuestReadOnlyError = class extends Error {
164
+ constructor() {
165
+ super("Guest account: sign up at auth.solongate.com to change anything. Your project, its policy and everything recorded carry over.");
166
+ this.name = "GuestReadOnlyError";
167
+ }
168
+ };
139
169
  var NotAuthenticatedError = class extends Error {
140
170
  constructor() {
141
171
  super("Not logged in. Run `solongate` and log in from the Accounts panel.");
@@ -200,6 +230,9 @@ function buildUrl(base, path, query) {
200
230
  return url.toString();
201
231
  }
202
232
  async function request(method, path, opts = {}) {
233
+ if (method !== "GET" && isGuestAccount()) {
234
+ throw new GuestReadOnlyError();
235
+ }
203
236
  const creds = resolveCredentials(opts.apiUrl);
204
237
  const url = buildUrl(creds.apiUrl, path, opts.query);
205
238
  const headers = {
package/dist/index.js CHANGED
@@ -7518,6 +7518,25 @@ var init_local_log = __esm({
7518
7518
  });
7519
7519
 
7520
7520
  // src/api-client/client.ts
7521
+ var client_exports = {};
7522
+ __export(client_exports, {
7523
+ ApiError: () => ApiError,
7524
+ DEFAULT_API_URL: () => DEFAULT_API_URL2,
7525
+ GUEST_EMAIL_SUFFIX: () => GUEST_EMAIL_SUFFIX,
7526
+ GuestReadOnlyError: () => GuestReadOnlyError,
7527
+ NotAuthenticatedError: () => NotAuthenticatedError,
7528
+ clearActiveCredential: () => clearActiveCredential,
7529
+ isActiveAccount: () => isActiveAccount,
7530
+ isAuthenticated: () => isAuthenticated,
7531
+ isGuestAccount: () => isGuestAccount,
7532
+ listAccounts: () => listAccounts,
7533
+ removeAccount: () => removeAccount,
7534
+ request: () => request,
7535
+ resolveCredentials: () => resolveCredentials,
7536
+ saveAccount: () => saveAccount,
7537
+ setActiveAccount: () => setActiveAccount,
7538
+ setViewCredentials: () => setViewCredentials
7539
+ });
7521
7540
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync5 } from "fs";
7522
7541
  import { resolve as resolve4, join as join9 } from "path";
7523
7542
  import { homedir as homedir7 } from "os";
@@ -7569,6 +7588,15 @@ function setViewCredentials(creds) {
7569
7588
  viewOverride = creds;
7570
7589
  cached2 = null;
7571
7590
  }
7591
+ function isGuestAccount() {
7592
+ try {
7593
+ const active2 = loginCredentialFile().apiKey;
7594
+ if (!active2) return false;
7595
+ return listAccounts().some((a) => a.apiKey === active2 && (a.email ?? "").endsWith(GUEST_EMAIL_SUFFIX));
7596
+ } catch {
7597
+ return false;
7598
+ }
7599
+ }
7572
7600
  function isActiveAccount(apiKey) {
7573
7601
  return loginCredentialFile().apiKey === apiKey;
7574
7602
  }
@@ -7668,6 +7696,9 @@ function buildUrl(base, path, query) {
7668
7696
  return url.toString();
7669
7697
  }
7670
7698
  async function request(method, path, opts = {}) {
7699
+ if (method !== "GET" && isGuestAccount()) {
7700
+ throw new GuestReadOnlyError();
7701
+ }
7671
7702
  const creds = resolveCredentials(opts.apiUrl);
7672
7703
  const url = buildUrl(creds.apiUrl, path, opts.query);
7673
7704
  const headers = {
@@ -7723,7 +7754,7 @@ async function request(method, path, opts = {}) {
7723
7754
  }
7724
7755
  return json;
7725
7756
  }
7726
- var DEFAULT_API_URL2, accountsFile, viewOverride, ApiError, NotAuthenticatedError, cached2;
7757
+ var DEFAULT_API_URL2, accountsFile, viewOverride, GUEST_EMAIL_SUFFIX, ApiError, GuestReadOnlyError, NotAuthenticatedError, cached2;
7727
7758
  var init_client = __esm({
7728
7759
  "src/api-client/client.ts"() {
7729
7760
  "use strict";
@@ -7731,6 +7762,7 @@ var init_client = __esm({
7731
7762
  DEFAULT_API_URL2 = "https://api.solongate.com";
7732
7763
  accountsFile = () => join9(homedir7(), ".solongate", "accounts.json");
7733
7764
  viewOverride = null;
7765
+ GUEST_EMAIL_SUFFIX = "@anonymous.solongate.invalid";
7734
7766
  ApiError = class extends Error {
7735
7767
  status;
7736
7768
  code;
@@ -7741,6 +7773,12 @@ var init_client = __esm({
7741
7773
  this.code = code;
7742
7774
  }
7743
7775
  };
7776
+ GuestReadOnlyError = class extends Error {
7777
+ constructor() {
7778
+ super("Guest account: sign up at auth.solongate.com to change anything. Your project, its policy and everything recorded carry over.");
7779
+ this.name = "GuestReadOnlyError";
7780
+ }
7781
+ };
7744
7782
  NotAuthenticatedError = class extends Error {
7745
7783
  constructor() {
7746
7784
  super("Not logged in. Run `solongate` and log in from the Accounts panel.");
@@ -11713,6 +11751,7 @@ function SettingsPanel({
11713
11751
  else if (inp === "m") {
11714
11752
  if (cur.kind === "acct") {
11715
11753
  const ok = setActiveAccount({ apiKey: cur.acc.apiKey, apiUrl: cur.acc.apiUrl });
11754
+ if (ok) clearLocalLog();
11716
11755
  setMsg(ok ? { text: `\u2713 ${acctLabel(cur.acc)} is now the ACTIVE key (guard + logging)`, level: "ok" } : { text: "\u2717 could not set active", level: "bad" });
11717
11756
  refreshAccounts();
11718
11757
  } else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "self" || cur.kind === "ll-enabled" || cur.kind === "ll-server") {
@@ -11732,6 +11771,7 @@ function SettingsPanel({
11732
11771
  }
11733
11772
  setConfirmDel(null);
11734
11773
  removeAccount(target.apiKey);
11774
+ clearLocalLog();
11735
11775
  let extra = " from this device";
11736
11776
  if (wasActive) {
11737
11777
  if (others.length) {
@@ -12079,6 +12119,7 @@ var init_Settings = __esm({
12079
12119
  init_components();
12080
12120
  init_hooks();
12081
12121
  init_theme();
12122
+ init_local_log();
12082
12123
  EVENTS = ["denials", "allowed", "all"];
12083
12124
  SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
12084
12125
  SIGNALS2 = ["any", "deny", "dlp", "ratelimit"];
@@ -12161,9 +12202,8 @@ function App() {
12161
12202
  const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
12162
12203
  const cur = accounts[acctIdx];
12163
12204
  const rawEmail = cur?.email ?? "";
12164
- const isGuestAccount = rawEmail.endsWith("@anonymous.solongate.invalid");
12165
- const hasKey = isAuthenticated();
12166
- const acctLabel2 = !cur ? "not logged in" : !hasKey ? "signed out" : isGuestAccount ? "Anonymous" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
12205
+ const isGuestAccount2 = rawEmail.endsWith("@anonymous.solongate.invalid");
12206
+ const acctLabel2 = !cur ? "not logged in" : isGuestAccount2 ? "Anonymous" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
12167
12207
  useEffect8(() => {
12168
12208
  if (!cur || cur.email) return;
12169
12209
  let alive = true;
@@ -12198,7 +12238,7 @@ function App() {
12198
12238
  setViewKey(next?.apiKey);
12199
12239
  setViewNonce((n) => n + 1);
12200
12240
  };
12201
- const locked = accounts.length === 0 || !hasKey;
12241
+ const locked = accounts.length === 0;
12202
12242
  const effectiveSection = locked ? SETTINGS_SECTION : section;
12203
12243
  useInput8((input, key) => {
12204
12244
  if (help) {
@@ -17003,6 +17043,13 @@ async function main() {
17003
17043
  }
17004
17044
  const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "sessions", "session", "doctor", "watch", "alerts", "webhooks"]);
17005
17045
  if (MGMT_COMMANDS.has(subcommand ?? "")) {
17046
+ const { isGuestAccount: isGuestAccount2 } = await Promise.resolve().then(() => (init_client(), client_exports));
17047
+ if (subcommand !== "doctor" && isGuestAccount2()) {
17048
+ process.stderr.write(
17049
+ "\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"
17050
+ );
17051
+ process.exit(1);
17052
+ }
17006
17053
  const { runCommand: runCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
17007
17054
  const code = await runCommand2(subcommand, process.argv.slice(3));
17008
17055
  process.exit(code);
package/dist/tui/index.js CHANGED
@@ -904,6 +904,16 @@ function setViewCredentials(creds) {
904
904
  viewOverride = creds;
905
905
  cached2 = null;
906
906
  }
907
+ var GUEST_EMAIL_SUFFIX = "@anonymous.solongate.invalid";
908
+ function isGuestAccount() {
909
+ try {
910
+ const active2 = loginCredentialFile().apiKey;
911
+ if (!active2) return false;
912
+ return listAccounts().some((a) => a.apiKey === active2 && (a.email ?? "").endsWith(GUEST_EMAIL_SUFFIX));
913
+ } catch {
914
+ return false;
915
+ }
916
+ }
907
917
  function isActiveAccount(apiKey) {
908
918
  return loginCredentialFile().apiKey === apiKey;
909
919
  }
@@ -956,6 +966,12 @@ var ApiError = class extends Error {
956
966
  this.code = code;
957
967
  }
958
968
  };
969
+ var GuestReadOnlyError = class extends Error {
970
+ constructor() {
971
+ super("Guest account: sign up at auth.solongate.com to change anything. Your project, its policy and everything recorded carry over.");
972
+ this.name = "GuestReadOnlyError";
973
+ }
974
+ };
959
975
  var NotAuthenticatedError = class extends Error {
960
976
  constructor() {
961
977
  super("Not logged in. Run `solongate` and log in from the Accounts panel.");
@@ -1020,6 +1036,9 @@ function buildUrl(base, path, query) {
1020
1036
  return url.toString();
1021
1037
  }
1022
1038
  async function request(method, path, opts = {}) {
1039
+ if (method !== "GET" && isGuestAccount()) {
1040
+ throw new GuestReadOnlyError();
1041
+ }
1023
1042
  const creds = resolveCredentials(opts.apiUrl);
1024
1043
  const url = buildUrl(creds.apiUrl, path, opts.query);
1025
1044
  const headers = {
@@ -4929,6 +4948,7 @@ function SettingsPanel({
4929
4948
  else if (inp === "m") {
4930
4949
  if (cur.kind === "acct") {
4931
4950
  const ok = setActiveAccount({ apiKey: cur.acc.apiKey, apiUrl: cur.acc.apiUrl });
4951
+ if (ok) clearLocalLog();
4932
4952
  setMsg(ok ? { text: `\u2713 ${acctLabel(cur.acc)} is now the ACTIVE key (guard + logging)`, level: "ok" } : { text: "\u2717 could not set active", level: "bad" });
4933
4953
  refreshAccounts();
4934
4954
  } else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "self" || cur.kind === "ll-enabled" || cur.kind === "ll-server") {
@@ -4948,6 +4968,7 @@ function SettingsPanel({
4948
4968
  }
4949
4969
  setConfirmDel(null);
4950
4970
  removeAccount(target.apiKey);
4971
+ clearLocalLog();
4951
4972
  let extra = " from this device";
4952
4973
  if (wasActive) {
4953
4974
  if (others.length) {
@@ -5354,9 +5375,8 @@ function App() {
5354
5375
  const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
5355
5376
  const cur = accounts[acctIdx];
5356
5377
  const rawEmail = cur?.email ?? "";
5357
- const isGuestAccount = rawEmail.endsWith("@anonymous.solongate.invalid");
5358
- const hasKey = isAuthenticated();
5359
- const acctLabel2 = !cur ? "not logged in" : !hasKey ? "signed out" : isGuestAccount ? "Anonymous" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
5378
+ const isGuestAccount2 = rawEmail.endsWith("@anonymous.solongate.invalid");
5379
+ const acctLabel2 = !cur ? "not logged in" : isGuestAccount2 ? "Anonymous" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
5360
5380
  useEffect8(() => {
5361
5381
  if (!cur || cur.email) return;
5362
5382
  let alive = true;
@@ -5391,7 +5411,7 @@ function App() {
5391
5411
  setViewKey(next?.apiKey);
5392
5412
  setViewNonce((n) => n + 1);
5393
5413
  };
5394
- const locked = accounts.length === 0 || !hasKey;
5414
+ const locked = accounts.length === 0;
5395
5415
  const effectiveSection = locked ? SETTINGS_SECTION : section;
5396
5416
  useInput8((input, key) => {
5397
5417
  if (help) {
@@ -44,7 +44,13 @@ export interface LocalLogLine {
44
44
  }
45
45
  /** Delete ONE matching line (ts + tool [+ session]) from the local log file. */
46
46
  export declare function deleteLocalEntry(at: number, tool: string, session?: string | null): number;
47
- /** Empty the local log file. Returns how many lines were removed. */
47
+ /**
48
+ * Empty the local log file. Returns how many lines were removed.
49
+ *
50
+ * The file is one per machine, not one per account, and nothing in an entry
51
+ * says which account produced it — so when the account changes, the honest move
52
+ * is to start it over rather than keep showing another account's calls in Live.
53
+ */
48
54
  export declare function clearLocalLog(): number;
49
55
  /** DLP pattern name + rate-limit-burst flag derived from a decision's reason. */
50
56
  export declare function reasonSignals(reason: string | null | undefined): {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.82.87",
3
+ "version": "0.82.89",
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": {