@solongate/proxy 0.81.70 → 0.81.71
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/index.js +52 -6
- package/dist/tui/index.js +52 -6
- package/dist/tui/panels/Settings.d.ts +4 -1
- package/package.json +1 -1
|
@@ -26,6 +26,14 @@ export declare function isActiveAccount(apiKey: string): boolean;
|
|
|
26
26
|
* account. Best-effort; returns false on failure.
|
|
27
27
|
*/
|
|
28
28
|
export declare function setActiveAccount(creds: Credentials): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Clear the ACTIVE login credential — a local sign-out. Removes the key/url from
|
|
31
|
+
* the active-key file so `listAccounts` no longer re-seeds it (the source of the
|
|
32
|
+
* "ghost account …xxxx" that lingers after removing the account you're logged in
|
|
33
|
+
* as). The guard hooks then have NO key until the next `solongate login`, so this
|
|
34
|
+
* only runs when the user removes their LAST account. Best-effort; false on error.
|
|
35
|
+
*/
|
|
36
|
+
export declare function clearActiveCredential(): boolean;
|
|
29
37
|
/**
|
|
30
38
|
* Thrown for any non-2xx response. `code`/`message` come from the API's
|
|
31
39
|
* `{ error: { code, message } }` envelope when present; `status` is the HTTP
|
package/dist/index.js
CHANGED
|
@@ -7168,6 +7168,28 @@ function setActiveAccount(creds) {
|
|
|
7168
7168
|
return false;
|
|
7169
7169
|
}
|
|
7170
7170
|
}
|
|
7171
|
+
function clearActiveCredential() {
|
|
7172
|
+
try {
|
|
7173
|
+
const p = join8(homedir6(), ".solongate", ["cloud", "guard.json"].join("-"));
|
|
7174
|
+
if (!existsSync3(p)) {
|
|
7175
|
+
cached2 = null;
|
|
7176
|
+
return true;
|
|
7177
|
+
}
|
|
7178
|
+
let existing = {};
|
|
7179
|
+
try {
|
|
7180
|
+
existing = JSON.parse(readFileSync8(p, "utf-8"));
|
|
7181
|
+
} catch {
|
|
7182
|
+
}
|
|
7183
|
+
delete existing.apiKey;
|
|
7184
|
+
delete existing.apiUrl;
|
|
7185
|
+
writeFileSync6(p, JSON.stringify(existing, null, 2));
|
|
7186
|
+
cached2 = null;
|
|
7187
|
+
viewOverride = null;
|
|
7188
|
+
return true;
|
|
7189
|
+
} catch {
|
|
7190
|
+
return false;
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7171
7193
|
function loginCredentialFile() {
|
|
7172
7194
|
try {
|
|
7173
7195
|
const p = join8(homedir6(), ".solongate", "cloud-guard.json");
|
|
@@ -10315,7 +10337,8 @@ function SettingsPanel({
|
|
|
10315
10337
|
active: active2,
|
|
10316
10338
|
focused,
|
|
10317
10339
|
viewApiKey,
|
|
10318
|
-
onView
|
|
10340
|
+
onView,
|
|
10341
|
+
onAccountsChanged
|
|
10319
10342
|
}) {
|
|
10320
10343
|
void active2;
|
|
10321
10344
|
const { cols, rows } = usePanelSize();
|
|
@@ -10589,16 +10612,31 @@ function SettingsPanel({
|
|
|
10589
10612
|
else activate(cur);
|
|
10590
10613
|
}
|
|
10591
10614
|
} else if (inp === "x" && cur.kind === "acct") {
|
|
10592
|
-
const
|
|
10615
|
+
const target = cur.acc;
|
|
10616
|
+
const k = "acct:" + target.apiKey;
|
|
10617
|
+
const wasActive = isActiveAccount(target.apiKey);
|
|
10618
|
+
const others = accounts.filter((a) => a.apiKey !== target.apiKey);
|
|
10593
10619
|
if (confirmDel !== k) {
|
|
10594
10620
|
setConfirmDel(k);
|
|
10595
|
-
|
|
10621
|
+
const note = wasActive ? others.length ? ` \u2014 the ACTIVE guard key; ${acctLabel(others[0])} takes over` : " \u2014 your ONLY account & the active guard key; this signs the device out (guard has no key until you log in again)" : " (the cloud account is untouched)";
|
|
10622
|
+
setMsg({ text: `press x again to remove ${acctLabel(target)} from this device${note}`, level: "bad" });
|
|
10596
10623
|
return;
|
|
10597
10624
|
}
|
|
10598
10625
|
setConfirmDel(null);
|
|
10599
|
-
removeAccount(
|
|
10600
|
-
|
|
10626
|
+
removeAccount(target.apiKey);
|
|
10627
|
+
let extra = " from this device";
|
|
10628
|
+
if (wasActive) {
|
|
10629
|
+
if (others.length) {
|
|
10630
|
+
setActiveAccount({ apiKey: others[0].apiKey, apiUrl: others[0].apiUrl });
|
|
10631
|
+
extra = ` \xB7 ${acctLabel(others[0])} is now the active key`;
|
|
10632
|
+
} else {
|
|
10633
|
+
clearActiveCredential();
|
|
10634
|
+
extra = " \xB7 signed out of this device";
|
|
10635
|
+
}
|
|
10636
|
+
}
|
|
10637
|
+
setMsg({ text: `\u2713 removed ${acctLabel(target)}${extra}`, level: "ok" });
|
|
10601
10638
|
refreshAccounts();
|
|
10639
|
+
onAccountsChanged?.();
|
|
10602
10640
|
} else if (inp === "t" && cur.kind === "wh") {
|
|
10603
10641
|
run12("webhook test sent \u2014 check your endpoint", () => api.settings.sendTestWebhook(cur.wh.id).then((r) => {
|
|
10604
10642
|
if (!r.delivered) throw new Error("endpoint rejected the test (non-2xx)");
|
|
@@ -10953,6 +10991,14 @@ function App() {
|
|
|
10953
10991
|
if (accounts.length < 2) return;
|
|
10954
10992
|
viewAccount(accounts[(acctIdx + 1) % accounts.length]);
|
|
10955
10993
|
};
|
|
10994
|
+
const syncAccounts = () => {
|
|
10995
|
+
const list6 = listAccounts();
|
|
10996
|
+
setAccounts(list6);
|
|
10997
|
+
const next = list6.find((a) => a.apiKey === viewKey) ?? list6[0];
|
|
10998
|
+
setViewCredentials(next ? { apiKey: next.apiKey, apiUrl: next.apiUrl } : null);
|
|
10999
|
+
setViewKey(next?.apiKey);
|
|
11000
|
+
setViewNonce((n) => n + 1);
|
|
11001
|
+
};
|
|
10956
11002
|
const locked = accounts.length === 0;
|
|
10957
11003
|
const effectiveSection = locked ? SETTINGS_SECTION : section;
|
|
10958
11004
|
useInput7((input, key) => {
|
|
@@ -10988,7 +11034,7 @@ function App() {
|
|
|
10988
11034
|
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows - 1, overflow: "hidden", children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }, viewNonce) });
|
|
10989
11035
|
}
|
|
10990
11036
|
const Panel = current.Panel;
|
|
10991
|
-
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" });
|
|
11037
|
+
const panelBody = current.label === "Settings" ? /* @__PURE__ */ jsx8(SettingsPanel, { active: true, focused: focus === "panel", viewApiKey: viewKey, onView: viewAccount, onAccountsChanged: syncAccounts }) : /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" });
|
|
10992
11038
|
return (
|
|
10993
11039
|
// height rows-1 (not rows): once total output reaches stdout.rows ink stops
|
|
10994
11040
|
// diffing and clearTerminal-repaints every frame, which slides the banner.
|
package/dist/tui/index.js
CHANGED
|
@@ -359,6 +359,28 @@ function setActiveAccount(creds) {
|
|
|
359
359
|
return false;
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
|
+
function clearActiveCredential() {
|
|
363
|
+
try {
|
|
364
|
+
const p = join3(homedir3(), ".solongate", ["cloud", "guard.json"].join("-"));
|
|
365
|
+
if (!existsSync(p)) {
|
|
366
|
+
cached2 = null;
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
let existing = {};
|
|
370
|
+
try {
|
|
371
|
+
existing = JSON.parse(readFileSync3(p, "utf-8"));
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
delete existing.apiKey;
|
|
375
|
+
delete existing.apiUrl;
|
|
376
|
+
writeFileSync2(p, JSON.stringify(existing, null, 2));
|
|
377
|
+
cached2 = null;
|
|
378
|
+
viewOverride = null;
|
|
379
|
+
return true;
|
|
380
|
+
} catch {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
362
384
|
var ApiError = class extends Error {
|
|
363
385
|
status;
|
|
364
386
|
code;
|
|
@@ -3215,7 +3237,8 @@ function SettingsPanel({
|
|
|
3215
3237
|
active: active2,
|
|
3216
3238
|
focused,
|
|
3217
3239
|
viewApiKey,
|
|
3218
|
-
onView
|
|
3240
|
+
onView,
|
|
3241
|
+
onAccountsChanged
|
|
3219
3242
|
}) {
|
|
3220
3243
|
void active2;
|
|
3221
3244
|
const { cols, rows } = usePanelSize();
|
|
@@ -3489,16 +3512,31 @@ function SettingsPanel({
|
|
|
3489
3512
|
else activate(cur);
|
|
3490
3513
|
}
|
|
3491
3514
|
} else if (inp === "x" && cur.kind === "acct") {
|
|
3492
|
-
const
|
|
3515
|
+
const target = cur.acc;
|
|
3516
|
+
const k = "acct:" + target.apiKey;
|
|
3517
|
+
const wasActive = isActiveAccount(target.apiKey);
|
|
3518
|
+
const others = accounts.filter((a) => a.apiKey !== target.apiKey);
|
|
3493
3519
|
if (confirmDel !== k) {
|
|
3494
3520
|
setConfirmDel(k);
|
|
3495
|
-
|
|
3521
|
+
const note = wasActive ? others.length ? ` \u2014 the ACTIVE guard key; ${acctLabel(others[0])} takes over` : " \u2014 your ONLY account & the active guard key; this signs the device out (guard has no key until you log in again)" : " (the cloud account is untouched)";
|
|
3522
|
+
setMsg({ text: `press x again to remove ${acctLabel(target)} from this device${note}`, level: "bad" });
|
|
3496
3523
|
return;
|
|
3497
3524
|
}
|
|
3498
3525
|
setConfirmDel(null);
|
|
3499
|
-
removeAccount(
|
|
3500
|
-
|
|
3526
|
+
removeAccount(target.apiKey);
|
|
3527
|
+
let extra = " from this device";
|
|
3528
|
+
if (wasActive) {
|
|
3529
|
+
if (others.length) {
|
|
3530
|
+
setActiveAccount({ apiKey: others[0].apiKey, apiUrl: others[0].apiUrl });
|
|
3531
|
+
extra = ` \xB7 ${acctLabel(others[0])} is now the active key`;
|
|
3532
|
+
} else {
|
|
3533
|
+
clearActiveCredential();
|
|
3534
|
+
extra = " \xB7 signed out of this device";
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
setMsg({ text: `\u2713 removed ${acctLabel(target)}${extra}`, level: "ok" });
|
|
3501
3538
|
refreshAccounts();
|
|
3539
|
+
onAccountsChanged?.();
|
|
3502
3540
|
} else if (inp === "t" && cur.kind === "wh") {
|
|
3503
3541
|
run("webhook test sent \u2014 check your endpoint", () => api.settings.sendTestWebhook(cur.wh.id).then((r) => {
|
|
3504
3542
|
if (!r.delivered) throw new Error("endpoint rejected the test (non-2xx)");
|
|
@@ -3958,6 +3996,14 @@ function App() {
|
|
|
3958
3996
|
if (accounts.length < 2) return;
|
|
3959
3997
|
viewAccount(accounts[(acctIdx + 1) % accounts.length]);
|
|
3960
3998
|
};
|
|
3999
|
+
const syncAccounts = () => {
|
|
4000
|
+
const list5 = listAccounts();
|
|
4001
|
+
setAccounts(list5);
|
|
4002
|
+
const next = list5.find((a) => a.apiKey === viewKey) ?? list5[0];
|
|
4003
|
+
setViewCredentials(next ? { apiKey: next.apiKey, apiUrl: next.apiUrl } : null);
|
|
4004
|
+
setViewKey(next?.apiKey);
|
|
4005
|
+
setViewNonce((n) => n + 1);
|
|
4006
|
+
};
|
|
3961
4007
|
const locked = accounts.length === 0;
|
|
3962
4008
|
const effectiveSection = locked ? SETTINGS_SECTION : section;
|
|
3963
4009
|
useInput7((input, key) => {
|
|
@@ -3993,7 +4039,7 @@ function App() {
|
|
|
3993
4039
|
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows - 1, overflow: "hidden", children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }, viewNonce) });
|
|
3994
4040
|
}
|
|
3995
4041
|
const Panel = current.Panel;
|
|
3996
|
-
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" });
|
|
4042
|
+
const panelBody = current.label === "Settings" ? /* @__PURE__ */ jsx8(SettingsPanel, { active: true, focused: focus === "panel", viewApiKey: viewKey, onView: viewAccount, onAccountsChanged: syncAccounts }) : /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" });
|
|
3997
4043
|
return (
|
|
3998
4044
|
// height rows-1 (not rows): once total output reaches stdout.rows ink stops
|
|
3999
4045
|
// diffing and clearTerminal-repaints every frame, which slides the banner.
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { type SavedAccount } from '../../api-client/client.js';
|
|
2
|
-
export declare function SettingsPanel({ active, focused, viewApiKey, onView, }: {
|
|
2
|
+
export declare function SettingsPanel({ active, focused, viewApiKey, onView, onAccountsChanged, }: {
|
|
3
3
|
active: boolean;
|
|
4
4
|
focused: boolean;
|
|
5
5
|
viewApiKey?: string;
|
|
6
6
|
onView?: (acc: SavedAccount) => void;
|
|
7
|
+
/** Fired after this panel mutates the on-disk account set so the App shell can
|
|
8
|
+
* re-derive its header / view / locked state from disk. */
|
|
9
|
+
onAccountsChanged?: () => void;
|
|
7
10
|
}): JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.71",
|
|
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": {
|