@solongate/proxy 0.81.48 → 0.81.50

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
@@ -7188,7 +7188,7 @@ async function pollDeviceLogin(apiUrl, deviceCode) {
7188
7188
  });
7189
7189
  const j = await res.json().catch(() => ({}));
7190
7190
  if (j.status === "approved" && j.api_key) {
7191
- return { status: "approved", apiKey: j.api_key, project: j.project?.name, user: j.user?.name || j.user?.email };
7191
+ return { status: "approved", apiKey: j.api_key, project: j.project?.name, user: j.user?.name || j.user?.email, email: j.user?.email };
7192
7192
  }
7193
7193
  if (j.status === "expired" || j.status === "not_found") return { status: j.status };
7194
7194
  return { status: "pending" };
@@ -9956,8 +9956,8 @@ var init_Audit = __esm({
9956
9956
  // src/tui/panels/Settings.tsx
9957
9957
  import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
9958
9958
  import TextInput5 from "ink-text-input";
9959
- import { useState as useState7 } from "react";
9960
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
9959
+ import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
9960
+ import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
9961
9961
  function alertBodyFor(channel) {
9962
9962
  const c2 = channel.trim();
9963
9963
  const base = { name: "dataroom alert", threshold: 5, windowSeconds: 60, signal: "any" };
@@ -9965,7 +9965,12 @@ function alertBodyFor(channel) {
9965
9965
  if (c2.includes("@")) return { ...base, emails: [c2] };
9966
9966
  return { ...base, telegram: [c2] };
9967
9967
  }
9968
- function SettingsPanel({ active: active2, focused }) {
9968
+ function SettingsPanel({
9969
+ active: active2,
9970
+ focused,
9971
+ viewApiKey,
9972
+ onView
9973
+ }) {
9969
9974
  void active2;
9970
9975
  const { cols, rows } = usePanelSize();
9971
9976
  const [sel, setSel] = useState7(0);
@@ -9974,24 +9979,40 @@ function SettingsPanel({ active: active2, focused }) {
9974
9979
  const [confirmDel, setConfirmDel] = useState7(null);
9975
9980
  const [msg, setMsg] = useState7(null);
9976
9981
  const [busy, setBusy] = useState7(false);
9977
- const localQ = useLoader(() => api.settings.getLocalLogs());
9978
- const whQ = useLoader(() => api.settings.getWebhooks());
9979
- const alertQ = useLoader(() => api.settings.getAlerts());
9982
+ const [accounts, setAccounts] = useState7(() => listAccounts());
9983
+ const [login, setLogin] = useState7(null);
9984
+ const [tick, setTick] = useState7(0);
9985
+ const abort = useRef3(false);
9986
+ const refreshAccounts = () => setAccounts(listAccounts());
9987
+ const locked = accounts.length === 0;
9988
+ useEffect6(() => {
9989
+ if (!login || login.phase === "done" || login.phase === "error") return;
9990
+ const t = setInterval(() => setTick((n) => n + 1), 120);
9991
+ return () => clearInterval(t);
9992
+ }, [login]);
9993
+ const localQ = useLoader(() => listAccounts().length ? api.settings.getLocalLogs() : Promise.resolve(null));
9994
+ const whQ = useLoader(() => listAccounts().length ? api.settings.getWebhooks() : Promise.resolve(null));
9995
+ const alertQ = useLoader(() => listAccounts().length ? api.settings.getAlerts() : Promise.resolve(null));
9980
9996
  const local = localQ.data;
9981
9997
  const webhooks = whQ.data?.webhooks ?? [];
9982
9998
  const alerts = alertQ.data?.rules ?? [];
9983
9999
  const rowsAll = [
9984
- { kind: "ll-enabled" },
9985
- { kind: "ll-path" },
9986
- ...webhooks.map((wh) => ({ kind: "wh", wh })),
9987
- { kind: "wh-add" },
9988
- ...alerts.map((rule) => ({ kind: "alert", rule })),
9989
- { kind: "alert-add" }
10000
+ ...accounts.map((acc) => ({ kind: "acct", acc })),
10001
+ { kind: "acct-add" },
10002
+ ...locked ? [] : [
10003
+ { kind: "ll-enabled" },
10004
+ { kind: "ll-path" },
10005
+ ...webhooks.map((wh) => ({ kind: "wh", wh })),
10006
+ { kind: "wh-add" },
10007
+ ...alerts.map((rule) => ({ kind: "alert", rule })),
10008
+ { kind: "alert-add" }
10009
+ ]
9990
10010
  ];
9991
10011
  const selClamped = Math.min(sel, rowsAll.length - 1);
9992
10012
  const cur = rowsAll[selClamped];
9993
- const keyOf = (r) => r.kind === "wh" ? "wh:" + r.wh.id : r.kind === "alert" ? "alert:" + r.rule.id : r.kind;
10013
+ const keyOf = (r) => r.kind === "acct" ? "acct:" + r.acc.apiKey : r.kind === "wh" ? "wh:" + r.wh.id : r.kind === "alert" ? "alert:" + r.rule.id : r.kind;
9994
10014
  const reloadAll = () => {
10015
+ refreshAccounts();
9995
10016
  localQ.reload();
9996
10017
  whQ.reload();
9997
10018
  alertQ.reload();
@@ -10005,8 +10026,48 @@ function SettingsPanel({ active: active2, focused }) {
10005
10026
  reload();
10006
10027
  }).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" })).finally(() => setBusy(false));
10007
10028
  };
10029
+ const beginLogin = () => {
10030
+ if (login && (login.phase === "starting" || login.phase === "waiting")) return;
10031
+ abort.current = false;
10032
+ setLogin({ phase: "starting" });
10033
+ void (async () => {
10034
+ let start2;
10035
+ try {
10036
+ start2 = await startDeviceLogin(DEFAULT_API_URL2);
10037
+ } catch (e) {
10038
+ setLogin({ phase: "error", msg: "could not reach SolonGate: " + (e instanceof Error ? e.message : String(e)) });
10039
+ return;
10040
+ }
10041
+ setLogin({ phase: "waiting", url: start2.verifyUrl });
10042
+ openBrowser(start2.verifyUrl);
10043
+ while (Date.now() < start2.expiresAt && !abort.current) {
10044
+ await new Promise((r) => setTimeout(r, start2.intervalMs));
10045
+ if (abort.current) return;
10046
+ const res = await pollDeviceLogin(DEFAULT_API_URL2, start2.deviceCode);
10047
+ if (res.status === "approved" && res.apiKey) {
10048
+ saveAccount({ apiKey: res.apiKey, apiUrl: DEFAULT_API_URL2, project: res.project, user: res.user, email: res.email });
10049
+ setLogin({ phase: "done", msg: `\u2713 added ${res.email || res.user || res.project || "account"}` });
10050
+ setViewCredentials({ apiKey: res.apiKey, apiUrl: DEFAULT_API_URL2 });
10051
+ onView?.({ apiKey: res.apiKey, apiUrl: DEFAULT_API_URL2, project: res.project, user: res.user, email: res.email });
10052
+ reloadAll();
10053
+ return;
10054
+ }
10055
+ if (res.status === "expired" || res.status === "not_found") {
10056
+ setLogin({ phase: "error", msg: "code expired \u2014 press n to retry" });
10057
+ return;
10058
+ }
10059
+ }
10060
+ if (!abort.current) setLogin({ phase: "error", msg: "timed out \u2014 press n to retry" });
10061
+ })();
10062
+ };
10008
10063
  const activate = (r) => {
10009
- if (r.kind === "ll-enabled") {
10064
+ if (r.kind === "acct") {
10065
+ setViewCredentials({ apiKey: r.acc.apiKey, apiUrl: r.acc.apiUrl });
10066
+ onView?.(r.acc);
10067
+ setMsg({ text: `viewing ${acctLabel(r.acc)}`, level: "ok" });
10068
+ } else if (r.kind === "acct-add") {
10069
+ beginLogin();
10070
+ } else if (r.kind === "ll-enabled") {
10010
10071
  if (!local) return;
10011
10072
  if (!local.enabled && !local.path.trim()) {
10012
10073
  setMsg({ text: "set a path first (\u2193 then enter)", level: "bad" });
@@ -10045,6 +10106,13 @@ function SettingsPanel({ active: active2, focused }) {
10045
10106
  };
10046
10107
  useInput6(
10047
10108
  (inp, key) => {
10109
+ if (login && (login.phase === "starting" || login.phase === "waiting")) {
10110
+ if (key.escape) {
10111
+ abort.current = true;
10112
+ setLogin(null);
10113
+ }
10114
+ return;
10115
+ }
10048
10116
  setMsg(null);
10049
10117
  if (key.upArrow) {
10050
10118
  setSel((n) => Math.max(0, n - 1));
@@ -10053,6 +10121,11 @@ function SettingsPanel({ active: active2, focused }) {
10053
10121
  setSel((n) => Math.min(rowsAll.length - 1, n + 1));
10054
10122
  setConfirmDel(null);
10055
10123
  } else if (key.return || inp === " ") activate(cur);
10124
+ else if (inp === "m" && cur.kind === "acct") {
10125
+ const ok = setActiveAccount({ apiKey: cur.acc.apiKey, apiUrl: cur.acc.apiUrl });
10126
+ setMsg(ok ? { text: `\u2713 ${acctLabel(cur.acc)} is now the ACTIVE key (guard + logging)`, level: "ok" } : { text: "\u2717 could not set active", level: "bad" });
10127
+ refreshAccounts();
10128
+ } else if (inp === "n") beginLogin();
10056
10129
  else if (inp === "e" && cur.kind === "wh") {
10057
10130
  const next = EVENTS[(EVENTS.indexOf(cur.wh.events) + 1) % EVENTS.length];
10058
10131
  run12(`webhook events \u2192 ${next}`, () => api.settings.updateWebhook(cur.wh.id, { events: next }), whQ.reload);
@@ -10079,247 +10152,163 @@ function SettingsPanel({ active: active2, focused }) {
10079
10152
  },
10080
10153
  { isActive: focused && !editing }
10081
10154
  );
10082
- const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
10083
- const error = localQ.error ?? whQ.error ?? alertQ.error;
10155
+ const spin = SPIN2[tick % SPIN2.length];
10156
+ if (login && (login.phase === "starting" || login.phase === "waiting")) {
10157
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
10158
+ /* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accentBright, children: "Add an account" }),
10159
+ login.phase === "starting" ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
10160
+ spin,
10161
+ " starting device pairing\u2026"
10162
+ ] }) : /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginTop: 1, children: [
10163
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Opening your browser to authorize. If it didn't open, visit:" }),
10164
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, wrap: "truncate", children: login.url }),
10165
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
10166
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
10167
+ spin,
10168
+ " waiting for authorization\u2026 "
10169
+ ] }),
10170
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "esc to cancel" })
10171
+ ] })
10172
+ ] })
10173
+ ] });
10174
+ }
10175
+ const loading = !locked && (localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data);
10176
+ const error = locked ? null : localQ.error ?? whQ.error ?? alertQ.error;
10084
10177
  const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
10085
10178
  const onOff = (on) => on ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "off" });
10086
10179
  const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
10087
- const listBudget = Math.max(4, rows - 8);
10180
+ const listBudget = Math.max(4, rows - 10);
10088
10181
  const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
10089
10182
  const inWindow = (r) => {
10090
10183
  const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
10091
10184
  return i >= start && i < start + listBudget;
10092
10185
  };
10093
10186
  return /* @__PURE__ */ jsx7(DataView, { loading, error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
10094
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter/space toggle-edit-add \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
10187
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter view-toggle-edit \xB7 m make active \xB7 n add account \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
10095
10188
  editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
10096
10189
  /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
10097
10190
  /* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
10098
- ] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
10099
- inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
10100
- /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
10101
- "LOCAL LOGS ",
10102
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
10103
- ] }),
10104
- /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10105
- /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
10106
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(10) }),
10107
- onOff(!!local?.enabled),
10108
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
10109
- ] }),
10110
- /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10111
- /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
10112
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(10) }),
10113
- local?.path ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "not set \u2014 enter to edit" })
10114
- ] })
10115
- ] }) : null,
10191
+ ] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : login?.msg ? /* @__PURE__ */ jsx7(Text7, { color: login.phase === "error" ? theme.bad : theme.ok, children: login.msg }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
10116
10192
  /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
10117
10193
  /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
10118
- "WEBHOOKS ",
10194
+ "ACCOUNTS ",
10119
10195
  /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
10120
- "\u2014 POST every matching event to your endpoint (",
10121
- webhooks.length,
10122
- ")"
10196
+ "\u2014 logged in on this device (",
10197
+ accounts.length,
10198
+ ") \xB7 \u25CF viewing = shown here \xB7 ACTIVE = key the guard uses"
10123
10199
  ] })
10124
10200
  ] }),
10125
- webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10126
- /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
10127
- onOff(wh.enabled),
10128
- /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
10129
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
10130
- ] }, wh.id)),
10131
- inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs7(Text7, { children: [
10132
- /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
10133
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add webhook (enter)" })
10201
+ accounts.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { children: [
10202
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " no accounts yet \u2014 " }),
10203
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: "n" }),
10204
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " (or enter) logs in, pairs this device and unlocks the dataroom" })
10205
+ ] }) : null,
10206
+ accounts.filter((acc) => inWindow({ kind: "acct", acc })).map((a) => {
10207
+ const isView = viewApiKey ? a.apiKey === viewApiKey : accounts[0]?.apiKey === a.apiKey;
10208
+ const isActive = isActiveAccount(a.apiKey);
10209
+ return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10210
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "acct:" + a.apiKey && focused ? theme.accentBright : theme.dim, children: mark({ kind: "acct", acc: a }) }),
10211
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(acctLabel(a), 30).padEnd(31) }),
10212
+ isView ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " }),
10213
+ isActive ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " }),
10214
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: a.project ? truncate2(a.project, 24) : "" })
10215
+ ] }, a.apiKey);
10216
+ }),
10217
+ inWindow({ kind: "acct-add" }) ? /* @__PURE__ */ jsxs7(Text7, { children: [
10218
+ /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "acct-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "acct-add" }) }),
10219
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
10134
10220
  ] }) : null
10135
10221
  ] }),
10136
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
10137
- /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
10138
- "ALERTS ",
10139
- /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
10140
- "\u2014 notify on a signal spike (",
10141
- alerts.length,
10142
- ")"
10222
+ locked ? null : /* @__PURE__ */ jsxs7(Fragment5, { children: [
10223
+ inWindow({ kind: "ll-enabled" }) || inWindow({ kind: "ll-path" }) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
10224
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
10225
+ "LOCAL LOGS ",
10226
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
10227
+ ] }),
10228
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10229
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "ll-enabled" }) }),
10230
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(10) }),
10231
+ onOff(!!local?.enabled),
10232
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
10233
+ ] }),
10234
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10235
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "ll-path" }) }),
10236
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(10) }),
10237
+ local?.path ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "not set \u2014 enter to edit" })
10143
10238
  ] })
10239
+ ] }) : null,
10240
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
10241
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
10242
+ "WEBHOOKS ",
10243
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
10244
+ "\u2014 POST every matching event to your endpoint (",
10245
+ webhooks.length,
10246
+ ")"
10247
+ ] })
10248
+ ] }),
10249
+ webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10250
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
10251
+ onOff(wh.enabled),
10252
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
10253
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
10254
+ ] }, wh.id)),
10255
+ inWindow({ kind: "wh-add" }) ? /* @__PURE__ */ jsxs7(Text7, { children: [
10256
+ /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
10257
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add webhook (enter)" })
10258
+ ] }) : null
10144
10259
  ] }),
10145
- alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10146
- /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
10147
- onOff(rule.enabled),
10148
- /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
10149
- /* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
10150
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
10151
- ] }, rule.id)),
10152
- inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs7(Text7, { children: [
10153
- /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
10154
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
10155
- ] }) : null
10260
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
10261
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
10262
+ "ALERTS ",
10263
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
10264
+ "\u2014 notify on a signal spike (",
10265
+ alerts.length,
10266
+ ")"
10267
+ ] })
10268
+ ] }),
10269
+ alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10270
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
10271
+ onOff(rule.enabled),
10272
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
10273
+ /* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
10274
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
10275
+ ] }, rule.id)),
10276
+ inWindow({ kind: "alert-add" }) ? /* @__PURE__ */ jsxs7(Text7, { children: [
10277
+ /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
10278
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
10279
+ ] }) : null
10280
+ ] })
10156
10281
  ] })
10157
10282
  ] }) });
10158
10283
  }
10159
- var EVENTS;
10284
+ var EVENTS, SPIN2, acctLabel;
10160
10285
  var init_Settings = __esm({
10161
10286
  "src/tui/panels/Settings.tsx"() {
10162
10287
  "use strict";
10163
10288
  init_api_client();
10289
+ init_client();
10290
+ init_device_login();
10164
10291
  init_components();
10165
10292
  init_hooks();
10166
10293
  init_theme();
10167
10294
  EVENTS = ["denials", "allowed", "all"];
10168
- }
10169
- });
10170
-
10171
- // src/tui/panels/Accounts.tsx
10172
- import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
10173
- import { useEffect as useEffect6, useRef as useRef3, useState as useState8 } from "react";
10174
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
10175
- function AccountsPanel({
10176
- focused,
10177
- viewApiKey,
10178
- onView
10179
- }) {
10180
- const [accounts, setAccounts] = useState8(() => listAccounts());
10181
- const [sel, setSel] = useState8(0);
10182
- const [msg, setMsg] = useState8(null);
10183
- const [login, setLogin] = useState8(null);
10184
- const [tick, setTick] = useState8(0);
10185
- const abort = useRef3(false);
10186
- const refresh = () => setAccounts(listAccounts());
10187
- useEffect6(() => {
10188
- if (!login || login.phase === "done" || login.phase === "error") return;
10189
- const t = setInterval(() => setTick((n) => n + 1), 120);
10190
- return () => clearInterval(t);
10191
- }, [login]);
10192
- const beginLogin = () => {
10193
- if (login && (login.phase === "starting" || login.phase === "waiting")) return;
10194
- abort.current = false;
10195
- setLogin({ phase: "starting" });
10196
- void (async () => {
10197
- let start;
10198
- try {
10199
- start = await startDeviceLogin(DEFAULT_API_URL2);
10200
- } catch (e) {
10201
- setLogin({ phase: "error", msg: "could not reach SolonGate: " + (e instanceof Error ? e.message : String(e)) });
10202
- return;
10203
- }
10204
- setLogin({ phase: "waiting", url: start.verifyUrl });
10205
- openBrowser(start.verifyUrl);
10206
- while (Date.now() < start.expiresAt && !abort.current) {
10207
- await new Promise((r) => setTimeout(r, start.intervalMs));
10208
- if (abort.current) return;
10209
- const res = await pollDeviceLogin(DEFAULT_API_URL2, start.deviceCode);
10210
- if (res.status === "approved" && res.apiKey) {
10211
- saveAccount({ apiKey: res.apiKey, apiUrl: DEFAULT_API_URL2, project: res.project, user: res.user });
10212
- setLogin({ phase: "done", msg: `\u2713 added ${res.project || res.user || "account"}` });
10213
- refresh();
10214
- setViewCredentials({ apiKey: res.apiKey, apiUrl: DEFAULT_API_URL2 });
10215
- onView?.({ apiKey: res.apiKey, apiUrl: DEFAULT_API_URL2, project: res.project, user: res.user });
10216
- return;
10217
- }
10218
- if (res.status === "expired" || res.status === "not_found") {
10219
- setLogin({ phase: "error", msg: "code expired \u2014 press n to retry" });
10220
- return;
10221
- }
10222
- }
10223
- if (!abort.current) setLogin({ phase: "error", msg: "timed out \u2014 press n to retry" });
10224
- })();
10225
- };
10226
- useInput7(
10227
- (input, key) => {
10228
- if (login && (login.phase === "starting" || login.phase === "waiting")) {
10229
- if (key.escape) {
10230
- abort.current = true;
10231
- setLogin(null);
10232
- }
10233
- return;
10234
- }
10235
- if (key.upArrow) setSel((n) => Math.max(0, n - 1));
10236
- else if (key.downArrow) setSel((n) => Math.min(accounts.length - 1, n + 1));
10237
- else if (key.return) {
10238
- const a = accounts[sel];
10239
- if (a) {
10240
- setViewCredentials({ apiKey: a.apiKey, apiUrl: a.apiUrl });
10241
- onView?.(a);
10242
- setMsg({ text: `viewing ${a.project || a.user || "account \u2026" + a.apiKey.slice(-4)}`, level: "ok" });
10243
- }
10244
- } else if (input === "m") {
10245
- const a = accounts[sel];
10246
- if (a) {
10247
- const ok = setActiveAccount({ apiKey: a.apiKey, apiUrl: a.apiUrl });
10248
- setMsg(ok ? { text: `\u2713 ${a.project || a.user || "account"} is now the ACTIVE key (guard + logging)`, level: "ok" } : { text: "\u2717 could not set active", level: "bad" });
10249
- refresh();
10250
- }
10251
- } else if (input === "n") beginLogin();
10252
- else if (input === "r") refresh();
10253
- },
10254
- { isActive: focused }
10255
- );
10256
- const spin = SPIN2[tick % SPIN2.length];
10257
- if (login && (login.phase === "starting" || login.phase === "waiting")) {
10258
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
10259
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "Add an account" }),
10260
- login.phase === "starting" ? /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
10261
- spin,
10262
- " starting device pairing\u2026"
10263
- ] }) : /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
10264
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Opening your browser to authorize. If it didn't open, visit:" }),
10265
- /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, wrap: "truncate", children: login.url }),
10266
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
10267
- /* @__PURE__ */ jsxs8(Text8, { color: theme.warn, children: [
10268
- spin,
10269
- " waiting for authorization\u2026 "
10270
- ] }),
10271
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "esc to cancel" })
10272
- ] })
10273
- ] })
10274
- ] });
10275
- }
10276
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
10277
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter view \xB7 m make active \xB7 n add account \xB7 r refresh" : "press \u2192 to manage accounts" }),
10278
- msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, 70) }) : login?.msg ? /* @__PURE__ */ jsx8(Text8, { color: login.phase === "error" ? theme.bad : theme.ok, children: login.msg }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
10279
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: accounts.length === 0 ? /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
10280
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "No accounts on this device yet." }),
10281
- /* @__PURE__ */ jsxs8(Text8, { children: [
10282
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "n" }),
10283
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " to log in \u2014 pairs this device and starts protecting your Claude Code sessions." })
10284
- ] })
10285
- ] }) : accounts.map((a, i) => {
10286
- const isSel = i === sel && focused;
10287
- const isView = viewApiKey ? a.apiKey === viewApiKey : i === 0;
10288
- const isActive = isActiveAccount(a.apiKey);
10289
- return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: isSel ? "#333333" : void 0, bold: isSel, children: [
10290
- /* @__PURE__ */ jsx8(Text8, { color: isSel ? theme.accentBright : theme.dim, children: isSel ? "\u25B8 " : " " }),
10291
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(a.project || a.user || "account \u2026" + a.apiKey.slice(-4), 30).padEnd(31) }),
10292
- isView ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
10293
- isActive ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
10294
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: a.user && a.project ? truncate2(a.user, 24) : "" })
10295
- ] }, a.apiKey);
10296
- }) }),
10297
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u25CF viewing = the account the dataroom is showing \xB7 ACTIVE = the key the guard hooks + logging use" }) })
10298
- ] });
10299
- }
10300
- var SPIN2;
10301
- var init_Accounts = __esm({
10302
- "src/tui/panels/Accounts.tsx"() {
10303
- "use strict";
10304
- init_client();
10305
- init_device_login();
10306
- init_theme();
10307
10295
  SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
10296
+ acctLabel = (a) => a.email || a.user || "account \u2026" + a.apiKey.slice(-4);
10308
10297
  }
10309
10298
  });
10310
10299
 
10311
10300
  // src/tui/App.tsx
10312
- import { Box as Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
10313
- import { useEffect as useEffect7, useState as useState9 } from "react";
10314
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
10301
+ import { Box as Box8, Text as Text8, useApp, useInput as useInput7 } from "ink";
10302
+ import { useEffect as useEffect7, useState as useState8 } from "react";
10303
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
10315
10304
  function LiveHint() {
10316
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
10317
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
10318
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
10319
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
10320
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
10305
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
10306
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
10307
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
10308
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
10309
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
10321
10310
  "press ",
10322
- /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
10311
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
10323
10312
  " to go live"
10324
10313
  ] }) })
10325
10314
  ] });
@@ -10327,25 +10316,25 @@ function LiveHint() {
10327
10316
  function Banner({ cols }) {
10328
10317
  const wide = cols >= 82;
10329
10318
  if (!wide) {
10330
- return /* @__PURE__ */ jsxs9(Box9, { children: [
10331
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
10332
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
10319
+ return /* @__PURE__ */ jsxs8(Box8, { children: [
10320
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate" }),
10321
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 security control center" })
10333
10322
  ] });
10334
10323
  }
10335
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
10336
- BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
10337
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
10324
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
10325
+ BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx8(Text8, { bold: true, color: BANNER_HEX[i], children: line }, i)),
10326
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
10338
10327
  ] });
10339
10328
  }
10340
10329
  function App() {
10341
10330
  const { exit } = useApp();
10342
- const [accounts, setAccounts] = useState9(() => listAccounts());
10343
- const [viewKey, setViewKey] = useState9(() => accounts[0]?.apiKey);
10344
- const [viewNonce, setViewNonce] = useState9(0);
10345
- const [section, setSection] = useState9(accounts.length === 0 ? ACCOUNTS_SECTION : 0);
10346
- const [focus, setFocus] = useState9("nav");
10347
- const [help, setHelp] = useState9(false);
10348
- const [update2, setUpdate] = useState9({ kind: "idle" });
10331
+ const [accounts, setAccounts] = useState8(() => listAccounts());
10332
+ const [viewKey, setViewKey] = useState8(() => accounts[0]?.apiKey);
10333
+ const [viewNonce, setViewNonce] = useState8(0);
10334
+ const [section, setSection] = useState8(accounts.length === 0 ? SETTINGS_SECTION : 0);
10335
+ const [focus, setFocus] = useState8("nav");
10336
+ const [help, setHelp] = useState8(false);
10337
+ const [update2, setUpdate] = useState8({ kind: "idle" });
10349
10338
  useEffect7(() => {
10350
10339
  let alive = true;
10351
10340
  const run12 = () => void tuiUpdateFlow((s) => alive && setUpdate(s));
@@ -10358,15 +10347,16 @@ function App() {
10358
10347
  }, []);
10359
10348
  const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
10360
10349
  const cur = accounts[acctIdx];
10361
- const acctLabel = cur ? cur.project || cur.user || `account \u2026${cur.apiKey.slice(-4)}` : "not logged in";
10350
+ const acctLabel2 = cur ? cur.email || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}` : "not logged in";
10362
10351
  useEffect7(() => {
10363
- if (!cur || cur.project || cur.user) return;
10352
+ if (!cur || cur.email) return;
10364
10353
  let alive = true;
10365
10354
  api.auth.me().then((r) => {
10366
- const project = r.project?.name || void 0;
10367
- const user = r.user?.name || r.user?.email || void 0;
10368
- if (!alive || !project && !user) return;
10369
- saveAccount({ ...cur, project, user });
10355
+ const project = r.project?.name || cur.project;
10356
+ const user = r.user?.name || cur.user;
10357
+ const email = r.user?.email || void 0;
10358
+ if (!alive || !project && !user && !email) return;
10359
+ saveAccount({ ...cur, project, user, email });
10370
10360
  setAccounts(listAccounts());
10371
10361
  }).catch(() => {
10372
10362
  });
@@ -10385,8 +10375,8 @@ function App() {
10385
10375
  viewAccount(accounts[(acctIdx + 1) % accounts.length]);
10386
10376
  };
10387
10377
  const locked = accounts.length === 0;
10388
- const effectiveSection = locked ? ACCOUNTS_SECTION : section;
10389
- useInput8((input, key) => {
10378
+ const effectiveSection = locked ? SETTINGS_SECTION : section;
10379
+ useInput7((input, key) => {
10390
10380
  if (help) {
10391
10381
  setHelp(false);
10392
10382
  return;
@@ -10414,45 +10404,45 @@ function App() {
10414
10404
  });
10415
10405
  const { cols, rows } = useTermSize();
10416
10406
  const current = SECTIONS[effectiveSection];
10417
- if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
10407
+ if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
10418
10408
  if (current.label === "Live" && focus === "panel") {
10419
- return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows - 1, overflow: "hidden", children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }, viewNonce) });
10409
+ return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows - 1, overflow: "hidden", children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }, viewNonce) });
10420
10410
  }
10421
10411
  const Panel = current.Panel;
10422
- const panelBody = current.label === "Accounts" ? /* @__PURE__ */ jsx9(AccountsPanel, { active: true, focused: focus === "panel", viewApiKey: viewKey, onView: viewAccount }) : /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" });
10423
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
10424
- /* @__PURE__ */ jsx9(Banner, { cols }),
10425
- /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
10426
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
10427
- /* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel }),
10428
- locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Accounts to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Accounts to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Accounts to add another" }),
10429
- 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 \u2014 restart (q, then solongate) to apply` }) : update2.kind === "manual" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 v${update2.version} available \u2014 npm i -g @solongate/proxy@latest` }) : null
10412
+ const panelBody = current.label === "Settings" ? /* @__PURE__ */ jsx8(SettingsPanel, { active: true, focused: focus === "panel", viewApiKey: viewKey, onView: viewAccount }) : /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" });
10413
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
10414
+ /* @__PURE__ */ jsx8(Banner, { cols }),
10415
+ /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10416
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "account: " }),
10417
+ /* @__PURE__ */ jsx8(Text8, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
10418
+ locked ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \xB7 Settings to add another" }),
10419
+ update2.kind === "updating" ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: ` \u2191 updating to v${update2.version}\u2026` }) : update2.kind === "updated" ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, bold: true, children: ` \u2191 v${update2.version} installed \u2014 restart (q, then solongate) to apply` }) : update2.kind === "manual" ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: ` \u2191 v${update2.version} available \u2014 npm i -g @solongate/proxy@latest` }) : null
10430
10420
  ] }),
10431
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
10432
- /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
10421
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
10422
+ /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
10433
10423
  const isCur = i === effectiveSection;
10434
- const disabled = locked && s.label !== "Accounts";
10435
- return /* @__PURE__ */ jsx9(Text9, { color: isCur ? theme.accentBright : disabled ? theme.dim : void 0, bold: isCur, dimColor: disabled, children: (isCur ? "\u25B8 " : disabled ? "\u2298 " : " ") + s.label }, s.label);
10424
+ const disabled = locked && s.label !== "Settings";
10425
+ return /* @__PURE__ */ jsx8(Text8, { color: isCur ? theme.accentBright : disabled ? theme.dim : void 0, bold: isCur, dimColor: disabled, children: (isCur ? "\u25B8 " : disabled ? "\u2298 " : " ") + s.label }, s.label);
10436
10426
  }) }),
10437
- /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: panelBody })
10427
+ /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: panelBody })
10438
10428
  ] }, viewNonce),
10439
- /* @__PURE__ */ jsx9(Box9, { children: locked ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2192/enter", "open Accounts"], ["n", "log in"], ["q", "quit"]] }) : focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ...accounts.length > 1 ? [["a", "account"]] : [], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
10429
+ /* @__PURE__ */ jsx8(Box8, { children: locked ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2192/enter", "open Settings"], ["n", "log in"], ["q", "quit"]] }) : focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ...accounts.length > 1 ? [["a", "account"]] : [], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
10440
10430
  ] });
10441
10431
  }
10442
10432
  function HelpOverlay({ cols, rows }) {
10443
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
10444
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
10445
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
10446
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
10447
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
10448
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
10449
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
10433
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
10434
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
10435
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
10436
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: group }),
10437
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs8(Text8, { children: [
10438
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
10439
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: desc })
10450
10440
  ] }, k))
10451
10441
  ] }, group)) }),
10452
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
10442
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
10453
10443
  ] });
10454
10444
  }
10455
- var SECTIONS, BANNER_HEX, ACCOUNTS_SECTION, HELP;
10445
+ var SECTIONS, BANNER_HEX, SETTINGS_SECTION, HELP;
10456
10446
  var init_App = __esm({
10457
10447
  "src/tui/App.tsx"() {
10458
10448
  "use strict";
@@ -10465,7 +10455,6 @@ var init_App = __esm({
10465
10455
  init_Dlp();
10466
10456
  init_Audit();
10467
10457
  init_Settings();
10468
- init_Accounts();
10469
10458
  init_hooks();
10470
10459
  init_client();
10471
10460
  init_api_client();
@@ -10476,11 +10465,10 @@ var init_App = __esm({
10476
10465
  { label: "Rate Limit", Panel: RateLimitPanel },
10477
10466
  { label: "DLP", Panel: DlpPanel },
10478
10467
  { label: "Audit", Panel: AuditPanel },
10479
- { label: "Settings", Panel: SettingsPanel },
10480
- { label: "Accounts", Panel: AccountsPanel }
10468
+ { label: "Settings", Panel: SettingsPanel }
10481
10469
  ];
10482
10470
  BANNER_HEX = ["white", "white", "white", "white", "white", "white"];
10483
- ACCOUNTS_SECTION = SECTIONS.findIndex((s) => s.label === "Accounts");
10471
+ SETTINGS_SECTION = SECTIONS.findIndex((s) => s.label === "Settings");
10484
10472
  HELP = [
10485
10473
  ["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
10486
10474
  ["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
@@ -10502,7 +10490,7 @@ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync7 } from "fs";
10502
10490
  import { homedir as homedir8 } from "os";
10503
10491
  import { join as join10 } from "path";
10504
10492
  import { render } from "ink";
10505
- import { jsx as jsx10 } from "react/jsx-runtime";
10493
+ import { jsx as jsx9 } from "react/jsx-runtime";
10506
10494
  async function launchTui() {
10507
10495
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
10508
10496
  process.stderr.write(
@@ -10527,7 +10515,7 @@ async function launchTui() {
10527
10515
  console.info = toFile("info");
10528
10516
  console.debug = toFile("debug");
10529
10517
  try {
10530
- const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}), { patchConsole: false });
10518
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}), { patchConsole: false });
10531
10519
  await waitUntilExit();
10532
10520
  } finally {
10533
10521
  Object.assign(console, saved);
@@ -12036,7 +12024,7 @@ async function main() {
12036
12024
  if (data?.project?.name) {
12037
12025
  console.log(` ${c.dim}Project:${c.reset} ${c.cyan}${data.project.name}${c.reset}`);
12038
12026
  }
12039
- saveAccount({ apiKey, apiUrl, project: data?.project?.name, user: data?.user?.name || data?.user?.email });
12027
+ saveAccount({ apiKey, apiUrl, project: data?.project?.name, user: data?.user?.name || data?.user?.email, email: data?.user?.email });
12040
12028
  break;
12041
12029
  }
12042
12030
  if (data?.status === "expired" || data?.status === "not_found") {