@solongate/proxy 0.81.37 → 0.81.39
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/api-client/client.d.ts +8 -0
- package/dist/api-client/device-login.d.ts +16 -0
- package/dist/api-client/index.d.ts +1 -0
- package/dist/commands/index.js +3 -0
- package/dist/index.js +487 -275
- package/dist/tui/index.js +285 -67
- package/dist/tui/panels/Accounts.d.ts +7 -0
- package/package.json +1 -1
package/dist/tui/index.js
CHANGED
|
@@ -8,8 +8,8 @@ var __export = (target, all) => {
|
|
|
8
8
|
import { render } from "ink";
|
|
9
9
|
|
|
10
10
|
// src/tui/App.tsx
|
|
11
|
-
import { Box as
|
|
12
|
-
import { useState as
|
|
11
|
+
import { Box as Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
|
|
12
|
+
import { useState as useState9 } from "react";
|
|
13
13
|
|
|
14
14
|
// src/cli-utils.ts
|
|
15
15
|
var c = {
|
|
@@ -263,11 +263,48 @@ function listAccounts() {
|
|
|
263
263
|
}
|
|
264
264
|
return list5;
|
|
265
265
|
}
|
|
266
|
+
function saveAccount(acc) {
|
|
267
|
+
try {
|
|
268
|
+
const list5 = (() => {
|
|
269
|
+
try {
|
|
270
|
+
const raw = JSON.parse(readFileSync3(accountsFile(), "utf-8"));
|
|
271
|
+
return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
|
|
272
|
+
} catch {
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
})();
|
|
276
|
+
const next = list5.filter((a) => a.apiKey !== acc.apiKey);
|
|
277
|
+
next.unshift({ ...acc, addedAt: acc.addedAt ?? Date.now() });
|
|
278
|
+
mkdirSync(join3(homedir3(), ".solongate"), { recursive: true });
|
|
279
|
+
writeFileSync2(accountsFile(), JSON.stringify(next, null, 2));
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
}
|
|
266
283
|
var viewOverride = null;
|
|
267
284
|
function setViewCredentials(creds) {
|
|
268
285
|
viewOverride = creds;
|
|
269
286
|
cached2 = null;
|
|
270
287
|
}
|
|
288
|
+
function isActiveAccount(apiKey) {
|
|
289
|
+
return loginCredentialFile().apiKey === apiKey;
|
|
290
|
+
}
|
|
291
|
+
function setActiveAccount(creds) {
|
|
292
|
+
try {
|
|
293
|
+
const dir = join3(homedir3(), ".solongate");
|
|
294
|
+
mkdirSync(dir, { recursive: true });
|
|
295
|
+
const p = join3(dir, ["cloud", "guard.json"].join("-"));
|
|
296
|
+
let existing = {};
|
|
297
|
+
try {
|
|
298
|
+
existing = JSON.parse(readFileSync3(p, "utf-8"));
|
|
299
|
+
} catch {
|
|
300
|
+
}
|
|
301
|
+
writeFileSync2(p, JSON.stringify({ ...existing, apiKey: creds.apiKey, apiUrl: creds.apiUrl }, null, 2));
|
|
302
|
+
cached2 = null;
|
|
303
|
+
return true;
|
|
304
|
+
} catch {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
271
308
|
var ApiError = class extends Error {
|
|
272
309
|
status;
|
|
273
310
|
code;
|
|
@@ -312,14 +349,6 @@ function dotenvApiKey() {
|
|
|
312
349
|
return void 0;
|
|
313
350
|
}
|
|
314
351
|
var cached2 = null;
|
|
315
|
-
function isAuthenticated() {
|
|
316
|
-
try {
|
|
317
|
-
resolveCredentials();
|
|
318
|
-
return true;
|
|
319
|
-
} catch {
|
|
320
|
-
return false;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
352
|
function resolveCredentials(apiUrlOverride) {
|
|
324
353
|
if (viewOverride && !apiUrlOverride) return viewOverride;
|
|
325
354
|
if (cached2 && !apiUrlOverride) return cached2;
|
|
@@ -397,6 +426,48 @@ async function request(method, path, opts = {}) {
|
|
|
397
426
|
return json;
|
|
398
427
|
}
|
|
399
428
|
|
|
429
|
+
// src/api-client/device-login.ts
|
|
430
|
+
import { spawn } from "child_process";
|
|
431
|
+
function openBrowser(url) {
|
|
432
|
+
try {
|
|
433
|
+
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
434
|
+
const args = process.platform === "win32" ? ["/c", "start", '""', url] : [url];
|
|
435
|
+
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
436
|
+
child.on("error", () => {
|
|
437
|
+
});
|
|
438
|
+
child.unref();
|
|
439
|
+
} catch {
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
async function startDeviceLogin(apiUrl = DEFAULT_API_URL) {
|
|
443
|
+
const res = await fetch(`${apiUrl}/api/v1/auth/device/start`, { method: "POST" });
|
|
444
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
445
|
+
const j = await res.json();
|
|
446
|
+
return {
|
|
447
|
+
deviceCode: j.device_code,
|
|
448
|
+
verifyUrl: j.verification_uri_complete || j.verification_uri || "",
|
|
449
|
+
intervalMs: Math.max(2, Number(j.interval) || 3) * 1e3,
|
|
450
|
+
expiresAt: Date.now() + (Number(j.expires_in) || 600) * 1e3
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
async function pollDeviceLogin(apiUrl, deviceCode) {
|
|
454
|
+
try {
|
|
455
|
+
const res = await fetch(`${apiUrl}/api/v1/auth/device/poll`, {
|
|
456
|
+
method: "POST",
|
|
457
|
+
headers: { "Content-Type": "application/json" },
|
|
458
|
+
body: JSON.stringify({ device_code: deviceCode })
|
|
459
|
+
});
|
|
460
|
+
const j = await res.json().catch(() => ({}));
|
|
461
|
+
if (j.status === "approved" && j.api_key) {
|
|
462
|
+
return { status: "approved", apiKey: j.api_key, project: j.project?.name, user: j.user?.name || j.user?.email };
|
|
463
|
+
}
|
|
464
|
+
if (j.status === "expired" || j.status === "not_found") return { status: j.status };
|
|
465
|
+
return { status: "pending" };
|
|
466
|
+
} catch {
|
|
467
|
+
return { status: "pending" };
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
400
471
|
// src/api-client/policies.ts
|
|
401
472
|
var policies_exports = {};
|
|
402
473
|
__export(policies_exports, {
|
|
@@ -622,24 +693,24 @@ function list4() {
|
|
|
622
693
|
var api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports, keys: keys_exports, mcp: mcp_exports };
|
|
623
694
|
|
|
624
695
|
// src/tui/notify.ts
|
|
625
|
-
import { spawn } from "child_process";
|
|
696
|
+
import { spawn as spawn2 } from "child_process";
|
|
626
697
|
function desktopNotify(title, msg) {
|
|
627
698
|
try {
|
|
628
699
|
if (process.platform === "linux") {
|
|
629
|
-
const p =
|
|
700
|
+
const p = spawn2("notify-send", ["-a", "SolonGate", title, msg], { stdio: "ignore", detached: true });
|
|
630
701
|
p.on("error", () => {
|
|
631
702
|
});
|
|
632
703
|
p.unref();
|
|
633
704
|
} else if (process.platform === "win32") {
|
|
634
705
|
const q = (s) => s.replace(/'/g, "''");
|
|
635
706
|
const ps = `Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing;$n=New-Object System.Windows.Forms.NotifyIcon;$n.Icon=[System.Drawing.SystemIcons]::Information;$n.Visible=$true;$n.ShowBalloonTip(6000,'${q(title)}','${q(msg)}',[System.Windows.Forms.ToolTipIcon]::Warning);Start-Sleep -Milliseconds 6500;$n.Dispose()`;
|
|
636
|
-
const p =
|
|
707
|
+
const p = spawn2("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { stdio: "ignore", detached: true, windowsHide: true });
|
|
637
708
|
p.on("error", () => {
|
|
638
709
|
});
|
|
639
710
|
p.unref();
|
|
640
711
|
} else if (process.platform === "darwin") {
|
|
641
712
|
const esc = (s) => s.replace(/"/g, '\\"');
|
|
642
|
-
const p =
|
|
713
|
+
const p = spawn2("osascript", ["-e", `display notification "${esc(msg)}" with title "${esc(title)}"`], { stdio: "ignore", detached: true });
|
|
643
714
|
p.on("error", () => {
|
|
644
715
|
});
|
|
645
716
|
p.unref();
|
|
@@ -3083,16 +3154,147 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
3083
3154
|
] }) });
|
|
3084
3155
|
}
|
|
3085
3156
|
|
|
3086
|
-
// src/tui/
|
|
3157
|
+
// src/tui/panels/Accounts.tsx
|
|
3158
|
+
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
3159
|
+
import { useEffect as useEffect6, useRef as useRef3, useState as useState8 } from "react";
|
|
3087
3160
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3088
|
-
|
|
3161
|
+
var SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3162
|
+
function AccountsPanel({
|
|
3163
|
+
focused,
|
|
3164
|
+
viewApiKey,
|
|
3165
|
+
onView
|
|
3166
|
+
}) {
|
|
3167
|
+
const [accounts, setAccounts] = useState8(() => listAccounts());
|
|
3168
|
+
const [sel, setSel] = useState8(0);
|
|
3169
|
+
const [msg, setMsg] = useState8(null);
|
|
3170
|
+
const [login, setLogin] = useState8(null);
|
|
3171
|
+
const [tick, setTick] = useState8(0);
|
|
3172
|
+
const abort = useRef3(false);
|
|
3173
|
+
const refresh = () => setAccounts(listAccounts());
|
|
3174
|
+
useEffect6(() => {
|
|
3175
|
+
if (!login || login.phase === "done" || login.phase === "error") return;
|
|
3176
|
+
const t = setInterval(() => setTick((n) => n + 1), 120);
|
|
3177
|
+
return () => clearInterval(t);
|
|
3178
|
+
}, [login]);
|
|
3179
|
+
const beginLogin = () => {
|
|
3180
|
+
if (login && (login.phase === "starting" || login.phase === "waiting")) return;
|
|
3181
|
+
abort.current = false;
|
|
3182
|
+
setLogin({ phase: "starting" });
|
|
3183
|
+
void (async () => {
|
|
3184
|
+
let start;
|
|
3185
|
+
try {
|
|
3186
|
+
start = await startDeviceLogin(DEFAULT_API_URL);
|
|
3187
|
+
} catch (e) {
|
|
3188
|
+
setLogin({ phase: "error", msg: "could not reach SolonGate: " + (e instanceof Error ? e.message : String(e)) });
|
|
3189
|
+
return;
|
|
3190
|
+
}
|
|
3191
|
+
setLogin({ phase: "waiting", url: start.verifyUrl });
|
|
3192
|
+
openBrowser(start.verifyUrl);
|
|
3193
|
+
while (Date.now() < start.expiresAt && !abort.current) {
|
|
3194
|
+
await new Promise((r) => setTimeout(r, start.intervalMs));
|
|
3195
|
+
if (abort.current) return;
|
|
3196
|
+
const res = await pollDeviceLogin(DEFAULT_API_URL, start.deviceCode);
|
|
3197
|
+
if (res.status === "approved" && res.apiKey) {
|
|
3198
|
+
saveAccount({ apiKey: res.apiKey, apiUrl: DEFAULT_API_URL, project: res.project, user: res.user });
|
|
3199
|
+
setLogin({ phase: "done", msg: `\u2713 added ${res.project || res.user || "account"}` });
|
|
3200
|
+
refresh();
|
|
3201
|
+
setViewCredentials({ apiKey: res.apiKey, apiUrl: DEFAULT_API_URL });
|
|
3202
|
+
onView?.({ apiKey: res.apiKey, apiUrl: DEFAULT_API_URL, project: res.project, user: res.user });
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
if (res.status === "expired" || res.status === "not_found") {
|
|
3206
|
+
setLogin({ phase: "error", msg: "code expired \u2014 press n to retry" });
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
if (!abort.current) setLogin({ phase: "error", msg: "timed out \u2014 press n to retry" });
|
|
3211
|
+
})();
|
|
3212
|
+
};
|
|
3213
|
+
useInput7(
|
|
3214
|
+
(input, key) => {
|
|
3215
|
+
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
3216
|
+
if (key.escape) {
|
|
3217
|
+
abort.current = true;
|
|
3218
|
+
setLogin(null);
|
|
3219
|
+
}
|
|
3220
|
+
return;
|
|
3221
|
+
}
|
|
3222
|
+
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
3223
|
+
else if (key.downArrow) setSel((n) => Math.min(accounts.length - 1, n + 1));
|
|
3224
|
+
else if (key.return) {
|
|
3225
|
+
const a = accounts[sel];
|
|
3226
|
+
if (a) {
|
|
3227
|
+
setViewCredentials({ apiKey: a.apiKey, apiUrl: a.apiUrl });
|
|
3228
|
+
onView?.(a);
|
|
3229
|
+
setMsg({ text: `viewing ${a.project || a.user || a.apiKey.slice(0, 8)}`, level: "ok" });
|
|
3230
|
+
}
|
|
3231
|
+
} else if (input === "m") {
|
|
3232
|
+
const a = accounts[sel];
|
|
3233
|
+
if (a) {
|
|
3234
|
+
const ok = setActiveAccount({ apiKey: a.apiKey, apiUrl: a.apiUrl });
|
|
3235
|
+
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" });
|
|
3236
|
+
refresh();
|
|
3237
|
+
}
|
|
3238
|
+
} else if (input === "n") beginLogin();
|
|
3239
|
+
else if (input === "r") refresh();
|
|
3240
|
+
},
|
|
3241
|
+
{ isActive: focused }
|
|
3242
|
+
);
|
|
3243
|
+
const spin = SPIN2[tick % SPIN2.length];
|
|
3244
|
+
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
3245
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
3246
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "Add an account" }),
|
|
3247
|
+
login.phase === "starting" ? /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
|
|
3248
|
+
spin,
|
|
3249
|
+
" starting device pairing\u2026"
|
|
3250
|
+
] }) : /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
3251
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Opening your browser to authorize. If it didn't open, visit:" }),
|
|
3252
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, wrap: "truncate", children: login.url }),
|
|
3253
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
3254
|
+
/* @__PURE__ */ jsxs8(Text8, { color: theme.warn, children: [
|
|
3255
|
+
spin,
|
|
3256
|
+
" waiting for authorization\u2026 "
|
|
3257
|
+
] }),
|
|
3258
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "esc to cancel" })
|
|
3259
|
+
] })
|
|
3260
|
+
] })
|
|
3261
|
+
] });
|
|
3262
|
+
}
|
|
3089
3263
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
3090
|
-
/* @__PURE__ */ jsx8(Text8, { color: theme.
|
|
3091
|
-
/* @__PURE__ */ jsx8(Text8, { color: theme.
|
|
3092
|
-
/* @__PURE__ */ jsx8(
|
|
3093
|
-
|
|
3264
|
+
/* @__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" }),
|
|
3265
|
+
msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, 70) }) : login?.msg ? /* @__PURE__ */ jsx8(Text8, { color: login.phase === "error" ? theme.bad : theme.ok, children: login.msg }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
|
|
3266
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: accounts.length === 0 ? /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
3267
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "No accounts on this device yet." }),
|
|
3268
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
3269
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "n" }),
|
|
3270
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " to log in \u2014 pairs this device and starts protecting your Claude Code sessions." })
|
|
3271
|
+
] })
|
|
3272
|
+
] }) : accounts.map((a, i) => {
|
|
3273
|
+
const isSel = i === sel && focused;
|
|
3274
|
+
const isView = viewApiKey ? a.apiKey === viewApiKey : i === 0;
|
|
3275
|
+
const isActive = isActiveAccount(a.apiKey);
|
|
3276
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: isSel ? "#333333" : void 0, bold: isSel, children: [
|
|
3277
|
+
/* @__PURE__ */ jsx8(Text8, { color: isSel ? theme.accentBright : theme.dim, children: isSel ? "\u25B8 " : " " }),
|
|
3278
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(a.project || a.user || a.apiKey.slice(0, 12), 30).padEnd(31) }),
|
|
3279
|
+
isView ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
|
|
3280
|
+
isActive ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
|
|
3281
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: a.user && a.project ? truncate(a.user, 24) : "" })
|
|
3282
|
+
] }, a.apiKey);
|
|
3283
|
+
}) }),
|
|
3284
|
+
/* @__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" }) })
|
|
3285
|
+
] });
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
// src/tui/App.tsx
|
|
3289
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3290
|
+
function LiveHint() {
|
|
3291
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
3292
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
3293
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
3294
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
3295
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
3094
3296
|
"press ",
|
|
3095
|
-
/* @__PURE__ */
|
|
3297
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
|
|
3096
3298
|
" to go live"
|
|
3097
3299
|
] }) })
|
|
3098
3300
|
] });
|
|
@@ -3103,39 +3305,48 @@ var SECTIONS = [
|
|
|
3103
3305
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
3104
3306
|
{ label: "DLP", Panel: DlpPanel },
|
|
3105
3307
|
{ label: "Audit", Panel: AuditPanel },
|
|
3106
|
-
{ label: "Settings", Panel: SettingsPanel }
|
|
3308
|
+
{ label: "Settings", Panel: SettingsPanel },
|
|
3309
|
+
{ label: "Accounts", Panel: AccountsPanel }
|
|
3107
3310
|
];
|
|
3108
3311
|
var BANNER_HEX = ["white", "white", "white", "white", "white", "white"];
|
|
3109
3312
|
function Banner({ cols }) {
|
|
3110
3313
|
const wide = cols >= 82;
|
|
3111
3314
|
if (!wide) {
|
|
3112
|
-
return /* @__PURE__ */
|
|
3113
|
-
/* @__PURE__ */
|
|
3114
|
-
/* @__PURE__ */
|
|
3315
|
+
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
3316
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
3317
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
|
|
3115
3318
|
] });
|
|
3116
3319
|
}
|
|
3117
|
-
return /* @__PURE__ */
|
|
3118
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
3119
|
-
/* @__PURE__ */
|
|
3320
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
3321
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
3322
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
3120
3323
|
] });
|
|
3121
3324
|
}
|
|
3325
|
+
var ACCOUNTS_SECTION = SECTIONS.findIndex((s) => s.label === "Accounts");
|
|
3122
3326
|
function App() {
|
|
3123
3327
|
const { exit } = useApp();
|
|
3124
|
-
const [
|
|
3125
|
-
const [
|
|
3126
|
-
const [
|
|
3127
|
-
const [
|
|
3128
|
-
const [
|
|
3328
|
+
const [accounts, setAccounts] = useState9(() => listAccounts());
|
|
3329
|
+
const [viewKey, setViewKey] = useState9(() => accounts[0]?.apiKey);
|
|
3330
|
+
const [viewNonce, setViewNonce] = useState9(0);
|
|
3331
|
+
const [section, setSection] = useState9(accounts.length === 0 ? ACCOUNTS_SECTION : 0);
|
|
3332
|
+
const [focus, setFocus] = useState9("nav");
|
|
3333
|
+
const [help, setHelp] = useState9(false);
|
|
3334
|
+
const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
|
|
3129
3335
|
const cur = accounts[acctIdx];
|
|
3130
|
-
const acctLabel = cur ? cur.project || cur.user || cur.apiKey.slice(0, 8) : "
|
|
3336
|
+
const acctLabel = cur ? cur.project || cur.user || cur.apiKey.slice(0, 8) : "not logged in";
|
|
3337
|
+
const viewAccount = (a) => {
|
|
3338
|
+
setViewCredentials({ apiKey: a.apiKey, apiUrl: a.apiUrl });
|
|
3339
|
+
setAccounts(listAccounts());
|
|
3340
|
+
setViewKey(a.apiKey);
|
|
3341
|
+
setViewNonce((n) => n + 1);
|
|
3342
|
+
};
|
|
3131
3343
|
const switchAccount = () => {
|
|
3132
3344
|
if (accounts.length < 2) return;
|
|
3133
|
-
|
|
3134
|
-
const a = accounts[next];
|
|
3135
|
-
setViewCredentials({ apiKey: a.apiKey, apiUrl: a.apiUrl });
|
|
3136
|
-
setAcctIdx(next);
|
|
3345
|
+
viewAccount(accounts[(acctIdx + 1) % accounts.length]);
|
|
3137
3346
|
};
|
|
3138
|
-
|
|
3347
|
+
const locked = accounts.length === 0;
|
|
3348
|
+
const effectiveSection = locked ? ACCOUNTS_SECTION : section;
|
|
3349
|
+
useInput8((input, key) => {
|
|
3139
3350
|
if (help) {
|
|
3140
3351
|
setHelp(false);
|
|
3141
3352
|
return;
|
|
@@ -3144,6 +3355,12 @@ function App() {
|
|
|
3144
3355
|
setHelp(true);
|
|
3145
3356
|
return;
|
|
3146
3357
|
}
|
|
3358
|
+
if (locked) {
|
|
3359
|
+
if (key.rightArrow || key.return || key.tab) setFocus("panel");
|
|
3360
|
+
else if (key.escape) setFocus("nav");
|
|
3361
|
+
else if (input === "q" && focus === "nav") exit();
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3147
3364
|
if (focus === "nav") {
|
|
3148
3365
|
if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
|
|
3149
3366
|
else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
|
|
@@ -3156,24 +3373,29 @@ function App() {
|
|
|
3156
3373
|
}
|
|
3157
3374
|
});
|
|
3158
3375
|
const { cols, rows } = useTermSize();
|
|
3159
|
-
const current = SECTIONS[
|
|
3160
|
-
if (help) return /* @__PURE__ */
|
|
3376
|
+
const current = SECTIONS[effectiveSection];
|
|
3377
|
+
if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
|
|
3161
3378
|
if (current.label === "Live" && focus === "panel") {
|
|
3162
|
-
return /* @__PURE__ */
|
|
3379
|
+
return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }, viewNonce) });
|
|
3163
3380
|
}
|
|
3164
3381
|
const Panel = current.Panel;
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
/* @__PURE__ */
|
|
3168
|
-
|
|
3169
|
-
/* @__PURE__ */
|
|
3170
|
-
|
|
3382
|
+
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" });
|
|
3383
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
3384
|
+
/* @__PURE__ */ jsx9(Banner, { cols }),
|
|
3385
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
3386
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
|
|
3387
|
+
/* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel }),
|
|
3388
|
+
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" })
|
|
3171
3389
|
] }),
|
|
3172
|
-
/* @__PURE__ */
|
|
3173
|
-
/* @__PURE__ */
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3390
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
|
|
3391
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
|
|
3392
|
+
const isCur = i === effectiveSection;
|
|
3393
|
+
const disabled = locked && s.label !== "Accounts";
|
|
3394
|
+
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);
|
|
3395
|
+
}) }),
|
|
3396
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: panelBody })
|
|
3397
|
+
] }, viewNonce),
|
|
3398
|
+
/* @__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"]] }) })
|
|
3177
3399
|
] });
|
|
3178
3400
|
}
|
|
3179
3401
|
var HELP = [
|
|
@@ -3186,21 +3408,21 @@ var HELP = [
|
|
|
3186
3408
|
["Accounts", [["a", "switch account (view another account logged in on this device)"], ["", "the header shows which account you are viewing; guard/logging keep the active key"]]]
|
|
3187
3409
|
];
|
|
3188
3410
|
function HelpOverlay({ cols, rows }) {
|
|
3189
|
-
return /* @__PURE__ */
|
|
3190
|
-
/* @__PURE__ */
|
|
3191
|
-
/* @__PURE__ */
|
|
3192
|
-
/* @__PURE__ */
|
|
3193
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
3194
|
-
/* @__PURE__ */
|
|
3195
|
-
/* @__PURE__ */
|
|
3411
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
3412
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
3413
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
3414
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
|
|
3415
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
3416
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
3417
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
|
|
3196
3418
|
] }, k))
|
|
3197
3419
|
] }, group)) }),
|
|
3198
|
-
/* @__PURE__ */
|
|
3420
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
|
|
3199
3421
|
] });
|
|
3200
3422
|
}
|
|
3201
3423
|
|
|
3202
3424
|
// src/tui/index.tsx
|
|
3203
|
-
import { jsx as
|
|
3425
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
3204
3426
|
async function launchTui() {
|
|
3205
3427
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
3206
3428
|
process.stderr.write(
|
|
@@ -3208,13 +3430,9 @@ async function launchTui() {
|
|
|
3208
3430
|
);
|
|
3209
3431
|
return;
|
|
3210
3432
|
}
|
|
3211
|
-
if (!isAuthenticated()) {
|
|
3212
|
-
process.stderr.write("Not logged in. Run `solongate login` first.\n");
|
|
3213
|
-
return;
|
|
3214
|
-
}
|
|
3215
3433
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
3216
3434
|
try {
|
|
3217
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
3435
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
|
|
3218
3436
|
await waitUntilExit();
|
|
3219
3437
|
} finally {
|
|
3220
3438
|
process.stdout.write("\x1B[?1049l");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.39",
|
|
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": {
|