privateer-agent 0.15.4 → 0.15.6
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/README.md +11 -0
- package/bin/boot-bundle.d.mts +2 -0
- package/bin/boot-bundle.mjs +44 -0
- package/bin/fresh-supervisor.d.mts +5 -1
- package/bin/fresh-supervisor.mjs +47 -10
- package/bin/privateer-launch.mjs +27 -3
- package/bin/privateer-subagent.mjs +5 -2
- package/bin/privateer-verify.mjs +12 -0
- package/bin/session-recovery.d.mts +53 -0
- package/bin/session-recovery.mjs +240 -0
- package/bin/session-seal-preload.mjs +17 -0
- package/bin/session-seal.d.mts +25 -0
- package/bin/session-seal.mjs +242 -0
- package/dist-boot/boot.mjs +3 -0
- package/dist-boot/c-27JKSMWL.mjs +1460 -0
- package/dist-boot/c-2BMRFV24.mjs +269 -0
- package/dist-boot/c-2CTF6JA3.mjs +166 -0
- package/dist-boot/c-2G3BBHRJ.mjs +84 -0
- package/dist-boot/c-2IDLKXX6.mjs +250 -0
- package/dist-boot/c-2NJD5VYM.mjs +8 -0
- package/dist-boot/c-2S5EEQPW.mjs +1602 -0
- package/dist-boot/c-2WRDCUVI.mjs +25 -0
- package/dist-boot/c-2YN3M256.mjs +14 -0
- package/dist-boot/c-37GM3GMU.mjs +187 -0
- package/dist-boot/c-3STKRSVO.mjs +2480 -0
- package/dist-boot/c-3TPV6TQQ.mjs +2320 -0
- package/dist-boot/c-3UVHLQDJ.mjs +225 -0
- package/dist-boot/c-42PUUZYC.mjs +697 -0
- package/dist-boot/c-4D5UMWVW.mjs +683 -0
- package/dist-boot/c-4ETZRMTA.mjs +46 -0
- package/dist-boot/c-4QT25BD5.mjs +1482 -0
- package/dist-boot/c-4WVR32AP.mjs +2579 -0
- package/dist-boot/c-5EMM567G.mjs +152 -0
- package/dist-boot/c-5FA4CV63.mjs +145 -0
- package/dist-boot/c-5GV3X3RB.mjs +14 -0
- package/dist-boot/c-5JCEPRIY.mjs +52 -0
- package/dist-boot/c-5NDKIIWT.mjs +16 -0
- package/dist-boot/c-5QL3AR26.mjs +4665 -0
- package/dist-boot/c-5XY6MFKN.mjs +5142 -0
- package/dist-boot/c-65UZIZC7.mjs +2897 -0
- package/dist-boot/c-6FUXKHOK.mjs +1969 -0
- package/dist-boot/c-6JHWQE4V.mjs +18242 -0
- package/dist-boot/c-6LOBR4I6.mjs +5280 -0
- package/dist-boot/c-6QHOZXKQ.mjs +14394 -0
- package/dist-boot/c-6ZO2SADQ.mjs +178 -0
- package/dist-boot/c-6ZORVVZP.mjs +1449 -0
- package/dist-boot/c-76QH4UCP.mjs +2441 -0
- package/dist-boot/c-77GO5L5Y.mjs +20982 -0
- package/dist-boot/c-7M23XFTN.mjs +5052 -0
- package/dist-boot/c-7TC367E2.mjs +32 -0
- package/dist-boot/c-7UOG7POX.mjs +1524 -0
- package/dist-boot/c-7VHP3FLN.mjs +376 -0
- package/dist-boot/c-A73JHZP2.mjs +13207 -0
- package/dist-boot/c-ABXULAFL.mjs +71 -0
- package/dist-boot/c-ANDD2AQO.mjs +74 -0
- package/dist-boot/c-AOY77AZL.mjs +1430 -0
- package/dist-boot/c-AXEPZ22I.mjs +108 -0
- package/dist-boot/c-B2Y7VNHU.mjs +427 -0
- package/dist-boot/c-B7VJUH73.mjs +77 -0
- package/dist-boot/c-BFW6CDVL.mjs +24 -0
- package/dist-boot/c-BHNIF2AR.mjs +188 -0
- package/dist-boot/c-BKF2O6BL.mjs +23 -0
- package/dist-boot/c-BOZXFTZN.mjs +8 -0
- package/dist-boot/c-BRNYJWZL.mjs +28 -0
- package/dist-boot/c-C3N4Z2HO.mjs +93 -0
- package/dist-boot/c-C4IV7ZFG.mjs +717 -0
- package/dist-boot/c-C5NK6UYV.mjs +793 -0
- package/dist-boot/c-C67644MB.mjs +1659 -0
- package/dist-boot/c-C6S2YB53.mjs +620 -0
- package/dist-boot/c-CDBEJSBF.mjs +37 -0
- package/dist-boot/c-CG47HBKU.mjs +435 -0
- package/dist-boot/c-CKCTWCT5.mjs +51 -0
- package/dist-boot/c-CN77DCGF.mjs +60 -0
- package/dist-boot/c-CRBXNKFO.mjs +1795 -0
- package/dist-boot/c-CRIHASQ6.mjs +973 -0
- package/dist-boot/c-CU4NGAUO.mjs +3786 -0
- package/dist-boot/c-CWRFLGQU.mjs +1883 -0
- package/dist-boot/c-CYQB2USO.mjs +32 -0
- package/dist-boot/c-DEE67DJO.mjs +379 -0
- package/dist-boot/c-DIFDBYOP.mjs +1258 -0
- package/dist-boot/c-DOSGVXDZ.mjs +34879 -0
- package/dist-boot/c-DR7XQI32.mjs +381 -0
- package/dist-boot/c-DTGPSB46.mjs +14666 -0
- package/dist-boot/c-DTHDNA4P.mjs +320 -0
- package/dist-boot/c-E4Z3Y5B5.mjs +3864 -0
- package/dist-boot/c-E66Z2TI7.mjs +203 -0
- package/dist-boot/c-EBU6RLRV.mjs +1527 -0
- package/dist-boot/c-EBZZNGSF.mjs +432 -0
- package/dist-boot/c-EC3LJ2KF.mjs +1032 -0
- package/dist-boot/c-EQBKXWNX.mjs +15829 -0
- package/dist-boot/c-EUEPIW4S.mjs +574 -0
- package/dist-boot/c-EX4YVWAT.mjs +378 -0
- package/dist-boot/c-EYZGSAJU.mjs +98 -0
- package/dist-boot/c-F75VRKY4.mjs +172 -0
- package/dist-boot/c-FH4NXKHV.mjs +177 -0
- package/dist-boot/c-FHNUCTEM.mjs +140 -0
- package/dist-boot/c-FJ5T42DS.mjs +5349 -0
- package/dist-boot/c-FPCNRL7G.mjs +1728 -0
- package/dist-boot/c-G4DLN7Q4.mjs +8 -0
- package/dist-boot/c-G56GYQFZ.mjs +2507 -0
- package/dist-boot/c-GLIOERDQ.mjs +1200 -0
- package/dist-boot/c-GMJ5HUBM.mjs +14 -0
- package/dist-boot/c-H3HSGMAQ.mjs +461 -0
- package/dist-boot/c-HIKTO6P7.mjs +275 -0
- package/dist-boot/c-I2MCJ34O.mjs +464 -0
- package/dist-boot/c-I3C6LBW4.mjs +765 -0
- package/dist-boot/c-I54OKQHW.mjs +387 -0
- package/dist-boot/c-IFSP3R4D.mjs +80 -0
- package/dist-boot/c-IZK77VB7.mjs +171 -0
- package/dist-boot/c-J3Y25UGE.mjs +54 -0
- package/dist-boot/c-J5IESS7W.mjs +27750 -0
- package/dist-boot/c-JJWZBY7L.mjs +254 -0
- package/dist-boot/c-JKTFXTGF.mjs +463 -0
- package/dist-boot/c-JN57AXQ4.mjs +77 -0
- package/dist-boot/c-KFIO6LUW.mjs +1096 -0
- package/dist-boot/c-KIEMEFWW.mjs +478 -0
- package/dist-boot/c-KIVXWBYD.mjs +28 -0
- package/dist-boot/c-KP6CRIP4.mjs +580 -0
- package/dist-boot/c-L26VOJX4.mjs +11558 -0
- package/dist-boot/c-L5GHWG5G.mjs +5988 -0
- package/dist-boot/c-M7NSQAKK.mjs +353 -0
- package/dist-boot/c-MCEWOPBW.mjs +56 -0
- package/dist-boot/c-MGPIEG7I.mjs +20 -0
- package/dist-boot/c-NREXJV3M.mjs +393 -0
- package/dist-boot/c-O5PP6CTX.mjs +162 -0
- package/dist-boot/c-OBQDHSXM.mjs +1774 -0
- package/dist-boot/c-OLQHHDD4.mjs +21 -0
- package/dist-boot/c-OXW4JQQH.mjs +319 -0
- package/dist-boot/c-P3D7ZUWN.mjs +1288 -0
- package/dist-boot/c-P4PQZVV5.mjs +948 -0
- package/dist-boot/c-P63T3XL2.mjs +26373 -0
- package/dist-boot/c-P6N5DQGZ.mjs +194 -0
- package/dist-boot/c-P7MLKQFC.mjs +58 -0
- package/dist-boot/c-PB3PZJL5.mjs +1276 -0
- package/dist-boot/c-PTN4S6G2.mjs +1259 -0
- package/dist-boot/c-PYCE5J7U.mjs +1083 -0
- package/dist-boot/c-Q4HYCRLB.mjs +19410 -0
- package/dist-boot/c-QUXMETGG.mjs +68 -0
- package/dist-boot/c-RDMJZCOX.mjs +356 -0
- package/dist-boot/c-ROGJK76M.mjs +17 -0
- package/dist-boot/c-RQJJ26T4.mjs +708 -0
- package/dist-boot/c-RSKCUPJF.mjs +7559 -0
- package/dist-boot/c-RVJU4O6P.mjs +37 -0
- package/dist-boot/c-S2RVHR3M.mjs +165 -0
- package/dist-boot/c-SKMK3PEE.mjs +91120 -0
- package/dist-boot/c-TI7SM4EG.mjs +20 -0
- package/dist-boot/c-TILJ6GXD.mjs +1235 -0
- package/dist-boot/c-TT6BCUJD.mjs +114 -0
- package/dist-boot/c-U4ITOXAN.mjs +2107 -0
- package/dist-boot/c-UGERXVO2.mjs +7120 -0
- package/dist-boot/c-UPBH5AXY.mjs +1363 -0
- package/dist-boot/c-US2PUNZL.mjs +597 -0
- package/dist-boot/c-UU4WZPPE.mjs +20 -0
- package/dist-boot/c-UWVIYPQS.mjs +39 -0
- package/dist-boot/c-V4RHQBPQ.mjs +17683 -0
- package/dist-boot/c-V5MRVK47.mjs +351 -0
- package/dist-boot/c-V6CL266R.mjs +1755 -0
- package/dist-boot/c-VJTMFKYE.mjs +14975 -0
- package/dist-boot/c-VJTRUKIB.mjs +773 -0
- package/dist-boot/c-W4IFSHPW.mjs +43 -0
- package/dist-boot/c-WETGB2CF.mjs +113 -0
- package/dist-boot/c-WO3QSZUU.mjs +1337 -0
- package/dist-boot/c-WQLH73TY.mjs +242 -0
- package/dist-boot/c-XL735JW5.mjs +500 -0
- package/dist-boot/c-Y7HADL5B.mjs +1107 -0
- package/dist-boot/c-YGQHSVSK.mjs +41 -0
- package/dist-boot/c-YMP2AMEB.mjs +10350 -0
- package/dist-boot/c-YRDO2WEF.mjs +12012 -0
- package/dist-boot/c-YUSTRATJ.mjs +8 -0
- package/dist-boot/c-Z2262R22.mjs +2697 -0
- package/dist-boot/c-Z4ALQMJC.mjs +709 -0
- package/dist-boot/c-Z5ZUQVUY.mjs +62 -0
- package/dist-boot/c-ZDZUKXF7.mjs +874 -0
- package/dist-boot/c-ZJYLGIC3.mjs +32 -0
- package/dist-boot/c-ZNA5CDMI.mjs +77 -0
- package/dist-boot/c-ZSO6DI3M.mjs +204 -0
- package/dist-boot/c-ZWUVHKMK.mjs +100 -0
- package/dist-boot/c-ZY4MESEW.mjs +832 -0
- package/dist-boot/c-ZZ5REI4L.mjs +329 -0
- package/dist-boot/cli.mjs +51 -0
- package/dist-boot/ext-anthropic-attribution.mjs +207 -0
- package/dist-boot/ext-pi-background-tasks.mjs +7234 -0
- package/dist-boot/ext-pi-subagents.mjs +37 -0
- package/dist-boot/ext-privateer-account.mjs +4426 -0
- package/dist-boot/ext-privateer-brand.mjs +5274 -0
- package/dist-boot/ext-privateer-codemode.mjs +37 -0
- package/dist-boot/ext-privateer-computer.mjs +626 -0
- package/dist-boot/ext-privateer-connect.mjs +1862 -0
- package/dist-boot/ext-privateer-context.mjs +323 -0
- package/dist-boot/ext-privateer-desktop.mjs +92 -0
- package/dist-boot/ext-privateer-final-audit.mjs +48 -0
- package/dist-boot/ext-privateer-fresh.mjs +81 -0
- package/dist-boot/ext-privateer-gate.mjs +5959 -0
- package/dist-boot/ext-privateer-hints.mjs +236 -0
- package/dist-boot/ext-privateer-media.mjs +2626 -0
- package/dist-boot/ext-privateer-models.mjs +3397 -0
- package/dist-boot/ext-privateer-posture.mjs +3006 -0
- package/dist-boot/ext-privateer-privacy.mjs +4578 -0
- package/dist-boot/ext-privateer-recovery.mjs +287 -0
- package/dist-boot/ext-privateer-speak.mjs +4754 -0
- package/dist-boot/ext-privateer-terminals.mjs +725 -0
- package/dist-boot/ext-privateer-tools.mjs +528 -0
- package/dist-boot/ext-privateer-truncation.mjs +74 -0
- package/dist-boot/ext-privateer-update.mjs +210 -0
- package/dist-boot/ext-privateer-vision.mjs +148 -0
- package/dist-boot/ext-privateer-web.mjs +543 -0
- package/dist-boot/ext-rpiv-ask-user-question.mjs +710 -0
- package/dist-boot/manifest.json +9 -0
- package/extensions/privateer-brand.ts +3 -1
- package/extensions/privateer-context.ts +4 -2
- package/extensions/privateer-final-audit.ts +17 -0
- package/extensions/privateer-gate.ts +55 -9
- package/extensions/privateer-recovery.ts +175 -0
- package/extensions/privateer-web.ts +4 -4
- package/package.json +3 -1
- package/patches/@earendil-works+pi-coding-agent+1.3.0.patch +199 -2
- package/patches/pi-background-tasks+2.6.10.patch +91 -7
- package/src/cli/chat.ts +7 -1
- package/src/config/moatManifest.json +2 -0
- package/src/config/whatsNew.json +4 -4
- package/src/context.ts +33 -5
- package/src/engine/finalAudit.ts +38 -0
- package/src/engine/stallWatchdog.ts +8 -1
- package/src/providers/account.ts +34 -3
- package/src/remote/attentionPush.ts +40 -0
- package/src/remote/liveTaskSession.ts +7 -1
- package/src/remote/relayClient.ts +38 -4
- package/src/remote/remoteBridge.ts +17 -0
- package/src/remote/transcriptSnapshot.ts +104 -0
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
import{createRequire as __pvCR}from"node:module";import{fileURLToPath as __pvF}from"node:url";import{dirname as __pvD}from"node:path";const require=__pvCR(import.meta.url);const __pvUrl=(r)=>new URL(r,import.meta.url).href;const __pvFile=(r)=>__pvF(new URL(r,import.meta.url));
|
|
2
|
+
import {
|
|
3
|
+
accountSessionsDir,
|
|
4
|
+
accountSessionsPath,
|
|
5
|
+
credentialsPath,
|
|
6
|
+
globalDir
|
|
7
|
+
} from "./c-YGQHSVSK.mjs";
|
|
8
|
+
import {
|
|
9
|
+
x25519
|
|
10
|
+
} from "./c-4WVR32AP.mjs";
|
|
11
|
+
import {
|
|
12
|
+
__name
|
|
13
|
+
} from "./c-CN77DCGF.mjs";
|
|
14
|
+
|
|
15
|
+
// src/auth/privateer.ts?ext=privateer-gate
|
|
16
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4, chmodSync as chmodSync4, rmSync as rmSync3 } from "node:fs";
|
|
17
|
+
import { hostname, userInfo } from "node:os";
|
|
18
|
+
|
|
19
|
+
// src/auth/accountSessions.ts?ext=privateer-gate
|
|
20
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, renameSync, writeFileSync, chmodSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
var ENTRY = /^(\d+)\.json$/;
|
|
23
|
+
var CLAIM = /^(\d+)\.claim-(\d+)$/;
|
|
24
|
+
function entryPath(pid) {
|
|
25
|
+
return join(accountSessionsDir(), `${pid}.json`);
|
|
26
|
+
}
|
|
27
|
+
__name(entryPath, "entryPath");
|
|
28
|
+
function claimPath(pid, claimant = process.pid) {
|
|
29
|
+
return join(accountSessionsDir(), `${pid}.claim-${claimant}`);
|
|
30
|
+
}
|
|
31
|
+
__name(claimPath, "claimPath");
|
|
32
|
+
function tryChmod(path, mode) {
|
|
33
|
+
try {
|
|
34
|
+
chmodSync(path, mode);
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
__name(tryChmod, "tryChmod");
|
|
39
|
+
function tryRm(path) {
|
|
40
|
+
try {
|
|
41
|
+
rmSync(path, { force: true });
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
__name(tryRm, "tryRm");
|
|
46
|
+
function readEntry(path, pid) {
|
|
47
|
+
try {
|
|
48
|
+
const raw = JSON.parse(readFileSync(path, "utf8"));
|
|
49
|
+
if (typeof raw?.refresh !== "string" || !raw.refresh) return null;
|
|
50
|
+
return { pid, refresh: raw.refresh, expires: typeof raw.expires === "number" ? raw.expires : 0 };
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
__name(readEntry, "readEntry");
|
|
56
|
+
function isAlive(pid) {
|
|
57
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
58
|
+
try {
|
|
59
|
+
process.kill(pid, 0);
|
|
60
|
+
return true;
|
|
61
|
+
} catch (e) {
|
|
62
|
+
return e.code === "EPERM";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
__name(isAlive, "isAlive");
|
|
66
|
+
function recordOwnedSession(cred) {
|
|
67
|
+
try {
|
|
68
|
+
mkdirSync(accountSessionsDir(), { recursive: true });
|
|
69
|
+
tryChmod(globalDir(), 448);
|
|
70
|
+
tryChmod(accountSessionsDir(), 448);
|
|
71
|
+
const path = entryPath(process.pid);
|
|
72
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
73
|
+
writeFileSync(tmp, JSON.stringify({ refresh: cred.refresh, expires: cred.expires }) + "\n", {
|
|
74
|
+
encoding: "utf8",
|
|
75
|
+
mode: 384
|
|
76
|
+
});
|
|
77
|
+
tryChmod(tmp, 384);
|
|
78
|
+
renameSync(tmp, path);
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
__name(recordOwnedSession, "recordOwnedSession");
|
|
83
|
+
function forgetOwnedSession() {
|
|
84
|
+
tryRm(entryPath(process.pid));
|
|
85
|
+
}
|
|
86
|
+
__name(forgetOwnedSession, "forgetOwnedSession");
|
|
87
|
+
function orphanedSessions(now = Date.now()) {
|
|
88
|
+
let names;
|
|
89
|
+
try {
|
|
90
|
+
names = readdirSync(accountSessionsDir());
|
|
91
|
+
} catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
const orphans = [];
|
|
95
|
+
for (const name of names) {
|
|
96
|
+
const path = join(accountSessionsDir(), name);
|
|
97
|
+
const claim = CLAIM.exec(name);
|
|
98
|
+
if (claim) {
|
|
99
|
+
if (!isAlive(Number(claim[2]))) tryRm(path);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const m = ENTRY.exec(name);
|
|
103
|
+
if (!m) continue;
|
|
104
|
+
const pid = Number(m[1]);
|
|
105
|
+
if (pid === process.pid || isAlive(pid)) continue;
|
|
106
|
+
const entry = readEntry(path, pid);
|
|
107
|
+
if (!entry || entry.expires > 0 && entry.expires <= now) {
|
|
108
|
+
tryRm(path);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
orphans.push(entry);
|
|
112
|
+
}
|
|
113
|
+
return orphans;
|
|
114
|
+
}
|
|
115
|
+
__name(orphanedSessions, "orphanedSessions");
|
|
116
|
+
function claimOrphan(pid) {
|
|
117
|
+
try {
|
|
118
|
+
renameSync(entryPath(pid), claimPath(pid));
|
|
119
|
+
return true;
|
|
120
|
+
} catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
__name(claimOrphan, "claimOrphan");
|
|
125
|
+
function releaseOrphan(pid) {
|
|
126
|
+
try {
|
|
127
|
+
renameSync(claimPath(pid), entryPath(pid));
|
|
128
|
+
} catch {
|
|
129
|
+
tryRm(claimPath(pid));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
__name(releaseOrphan, "releaseOrphan");
|
|
133
|
+
function dropOwnedSession(pid) {
|
|
134
|
+
tryRm(claimPath(pid));
|
|
135
|
+
if (pid !== process.pid) tryRm(entryPath(pid));
|
|
136
|
+
}
|
|
137
|
+
__name(dropOwnedSession, "dropOwnedSession");
|
|
138
|
+
function clearOwnedSessions() {
|
|
139
|
+
tryRm(accountSessionsPath());
|
|
140
|
+
try {
|
|
141
|
+
if (existsSync(accountSessionsDir())) rmSync(accountSessionsDir(), { recursive: true, force: true });
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
__name(clearOwnedSessions, "clearOwnedSessions");
|
|
146
|
+
|
|
147
|
+
// src/util/redact.ts?ext=privateer-gate
|
|
148
|
+
var PLACEHOLDER = "\xABredacted\xBB";
|
|
149
|
+
var KEY_PATTERNS = [
|
|
150
|
+
/\bsk-(ant|or|proj|live|test)?-?[A-Za-z0-9_-]{16,}\b/g,
|
|
151
|
+
/\bAIza[A-Za-z0-9_-]{20,}\b/g,
|
|
152
|
+
/\bxai-[A-Za-z0-9_-]{16,}\b/g,
|
|
153
|
+
/\bgsk_[A-Za-z0-9_-]{16,}\b/g,
|
|
154
|
+
/\bcsk-[A-Za-z0-9_-]{16,}\b/g,
|
|
155
|
+
/\bvapi_[A-Za-z0-9_-]{16,}\b/g,
|
|
156
|
+
/\bfw_[A-Za-z0-9_-]{16,}\b/g,
|
|
157
|
+
/\b[a-f0-9]{32}\.[A-Za-z0-9]{10,}\b/g,
|
|
158
|
+
/\b(authorization|x-api-key|x-goog-api-key)\b\s*[:=]\s*(bearer\s+)?["']?[A-Za-z0-9_\-.]{16,}["']?/gi
|
|
159
|
+
];
|
|
160
|
+
function collectSecrets(providers) {
|
|
161
|
+
const out = /* @__PURE__ */ new Set();
|
|
162
|
+
const add = /* @__PURE__ */ __name((v) => {
|
|
163
|
+
if (v && v.trim().length >= 8) out.add(v.trim());
|
|
164
|
+
}, "add");
|
|
165
|
+
if (providers) for (const p of Object.values(providers)) add(p?.apiKey);
|
|
166
|
+
for (const k of [
|
|
167
|
+
"OPENROUTER_API_KEY",
|
|
168
|
+
"ANTHROPIC_API_KEY",
|
|
169
|
+
"OPENAI_API_KEY",
|
|
170
|
+
"GOOGLE_GENERATIVE_AI_API_KEY",
|
|
171
|
+
"GEMINI_API_KEY",
|
|
172
|
+
"GOOGLE_API_KEY",
|
|
173
|
+
"XAI_API_KEY",
|
|
174
|
+
"GROQ_API_KEY",
|
|
175
|
+
"MISTRAL_API_KEY",
|
|
176
|
+
"ZAI_API_KEY",
|
|
177
|
+
"Z_AI_API_KEY",
|
|
178
|
+
"MOONSHOT_API_KEY",
|
|
179
|
+
"CEREBRAS_API_KEY",
|
|
180
|
+
"FIREWORKS_API_KEY",
|
|
181
|
+
"TOGETHER_API_KEY",
|
|
182
|
+
"TOGETHER_AI_API_KEY",
|
|
183
|
+
"DEEPSEEK_API_KEY",
|
|
184
|
+
"MINIMAX_API_KEY",
|
|
185
|
+
"DASHSCOPE_API_KEY",
|
|
186
|
+
"QWEN_API_KEY",
|
|
187
|
+
"NEAR_AI_API_KEY",
|
|
188
|
+
"NEARAI_API_KEY",
|
|
189
|
+
"TINFOIL_API_KEY",
|
|
190
|
+
"VENICE_API_KEY"
|
|
191
|
+
])
|
|
192
|
+
add(process.env[k]);
|
|
193
|
+
return [...out];
|
|
194
|
+
}
|
|
195
|
+
__name(collectSecrets, "collectSecrets");
|
|
196
|
+
function redactText(text, secrets = collectSecrets()) {
|
|
197
|
+
let out = text;
|
|
198
|
+
for (const s of secrets) {
|
|
199
|
+
if (s) out = out.split(s).join(PLACEHOLDER);
|
|
200
|
+
}
|
|
201
|
+
for (const re of KEY_PATTERNS) out = out.replace(re, PLACEHOLDER);
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
__name(redactText, "redactText");
|
|
205
|
+
|
|
206
|
+
// src/engine/errors.ts?ext=privateer-gate
|
|
207
|
+
var HOST_LABELS = {
|
|
208
|
+
"openrouter.ai": "OpenRouter",
|
|
209
|
+
"api.anthropic.com": "Anthropic",
|
|
210
|
+
"api.openai.com": "OpenAI",
|
|
211
|
+
"cloud-api.near.ai": "NEAR AI",
|
|
212
|
+
"localhost:11434": "Ollama",
|
|
213
|
+
"127.0.0.1:11434": "Ollama"
|
|
214
|
+
};
|
|
215
|
+
var NETWORK_ERRNO = /^(ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|EAI_AGAIN|EPIPE|ENETUNREACH|EHOSTUNREACH|UND_ERR_CONNECT_TIMEOUT|UND_ERR_SOCKET|UND_ERR_BODY_TIMEOUT|UND_ERR_HEADERS_TIMEOUT)$/;
|
|
216
|
+
function extract(err) {
|
|
217
|
+
const facts = {};
|
|
218
|
+
const readBody = /* @__PURE__ */ __name((body) => {
|
|
219
|
+
const b = body;
|
|
220
|
+
if (!b || typeof b !== "object") return;
|
|
221
|
+
const msg = b.error?.message ?? b.message;
|
|
222
|
+
if (facts.providerMessage == null && typeof msg === "string") facts.providerMessage = msg;
|
|
223
|
+
const c = b.error?.code ?? b.code;
|
|
224
|
+
if (facts.code == null && typeof c === "string") facts.code = c;
|
|
225
|
+
}, "readBody");
|
|
226
|
+
let cur = err;
|
|
227
|
+
for (let i = 0; i < 6 && cur && typeof cur === "object"; i++) {
|
|
228
|
+
const e = cur;
|
|
229
|
+
if (facts.statusCode == null && typeof e.statusCode === "number") facts.statusCode = e.statusCode;
|
|
230
|
+
readBody(e.data);
|
|
231
|
+
if (typeof e.responseBody === "string") {
|
|
232
|
+
try {
|
|
233
|
+
readBody(JSON.parse(e.responseBody));
|
|
234
|
+
} catch {
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const reqBody = e.requestBodyValues;
|
|
238
|
+
if (facts.model == null && typeof reqBody?.model === "string") facts.model = reqBody.model;
|
|
239
|
+
if (facts.url == null && typeof e.url === "string" && /^https?:/.test(e.url)) {
|
|
240
|
+
facts.url = e.url;
|
|
241
|
+
try {
|
|
242
|
+
facts.provider = HOST_LABELS[new URL(e.url).host];
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (facts.errno == null && typeof e.code === "string" && NETWORK_ERRNO.test(e.code)) {
|
|
247
|
+
facts.errno = e.code;
|
|
248
|
+
}
|
|
249
|
+
if (typeof e.message === "string" && e.message.trim()) facts.message = e.message;
|
|
250
|
+
const next = e.lastError ?? e.cause;
|
|
251
|
+
if (!next || next === cur) break;
|
|
252
|
+
cur = next;
|
|
253
|
+
}
|
|
254
|
+
return facts;
|
|
255
|
+
}
|
|
256
|
+
__name(extract, "extract");
|
|
257
|
+
var CAP_CODE = /CAP|QUOTA|LIMIT_REACHED|INSUFFICIENT|TOP_?UP/i;
|
|
258
|
+
function isAccountCapCode(code) {
|
|
259
|
+
return typeof code === "string" && CAP_CODE.test(code);
|
|
260
|
+
}
|
|
261
|
+
__name(isAccountCapCode, "isAccountCapCode");
|
|
262
|
+
function describeAccountBalanceError(text) {
|
|
263
|
+
if (!/^\s*(?:402|429)\b/.test(text) || /<!doctype html|<html[\s>]/i.test(text)) return null;
|
|
264
|
+
const bodyStart = text.indexOf("{");
|
|
265
|
+
const facts = bodyStart < 0 ? {} : extract({ responseBody: text.slice(bodyStart) });
|
|
266
|
+
const code = facts.code;
|
|
267
|
+
const message = facts.providerMessage ?? text;
|
|
268
|
+
const balanceCode = /^(?:INSUFFICIENT_(?:BALANCE|FUNDS|CREDITS?)|(?:BALANCE|CREDITS?)_EXHAUSTED)$/i;
|
|
269
|
+
const balanceText = /\b(?:insufficient (?:credit(?:s| balance)?|balance|funds)|out of credits?|(?:credit balance|balance|credits?) (?:is |are )?exhausted)\b/i;
|
|
270
|
+
if (code ? !balanceCode.test(code) : !balanceText.test(message)) return null;
|
|
271
|
+
return {
|
|
272
|
+
message: "Your Privateer account has insufficient balance.",
|
|
273
|
+
// Use our known destination, never a URL copied from an untrusted error body.
|
|
274
|
+
hint: "Top up at https://privateer.pro/top-up, then try again.",
|
|
275
|
+
retryable: false
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
__name(describeAccountBalanceError, "describeAccountBalanceError");
|
|
279
|
+
var MAX_ERROR_CHARS = 2e3;
|
|
280
|
+
var MAX_PAGE_TEXT_CHARS = 600;
|
|
281
|
+
var HTML_DOC = /<!doctype html|<html[\s>]/i;
|
|
282
|
+
var NON_PROSE = /<(script|style|svg|head|noscript)\b[\s\S]*?<\/\1\s*>/gi;
|
|
283
|
+
var ENTITIES = {
|
|
284
|
+
amp: "&",
|
|
285
|
+
lt: "<",
|
|
286
|
+
gt: ">",
|
|
287
|
+
quot: '"',
|
|
288
|
+
apos: "'",
|
|
289
|
+
nbsp: " ",
|
|
290
|
+
"#39": "'",
|
|
291
|
+
"#x27": "'"
|
|
292
|
+
};
|
|
293
|
+
function plainText(html) {
|
|
294
|
+
return html.replace(/<[^>]*>/g, " ").replace(/&(#x?[0-9a-f]+|[a-z]+);/gi, (m, code) => {
|
|
295
|
+
const key = code.toLowerCase();
|
|
296
|
+
if (ENTITIES[key] !== void 0) return ENTITIES[key];
|
|
297
|
+
if (key.startsWith("#x")) return String.fromCodePoint(parseInt(key.slice(2), 16) || 0) || m;
|
|
298
|
+
if (key.startsWith("#")) return String.fromCodePoint(parseInt(key.slice(1), 10) || 0) || m;
|
|
299
|
+
return m;
|
|
300
|
+
}).replace(/\s+/g, " ").trim();
|
|
301
|
+
}
|
|
302
|
+
__name(plainText, "plainText");
|
|
303
|
+
function compactProviderError(raw) {
|
|
304
|
+
const text = typeof raw === "string" ? raw : String(raw ?? "");
|
|
305
|
+
if (!HTML_DOC.test(text)) {
|
|
306
|
+
if (text.length <= MAX_ERROR_CHARS) return text;
|
|
307
|
+
return `${text.slice(0, MAX_ERROR_CHARS)}\u2026 [dropped ${text.length - MAX_ERROR_CHARS} chars]`;
|
|
308
|
+
}
|
|
309
|
+
const status = /^\s*(\d{3})\b/.exec(text)?.[1];
|
|
310
|
+
const title = plainText(/<title[^>]*>([\s\S]*?)<\/title>/i.exec(text)?.[1] ?? "");
|
|
311
|
+
const body = plainText(text.replace(NON_PROSE, " "));
|
|
312
|
+
const visible = [title, body].filter(Boolean).join(" \u2014 ").slice(0, MAX_PAGE_TEXT_CHARS);
|
|
313
|
+
return `${status ?? "HTTP error"} \u2014 an HTML page, not an API response (something in front of the provider answered: a WAF, a proxy, or a captive portal): ${visible || "(no readable text)"} [dropped ${text.length} chars of HTML]`;
|
|
314
|
+
}
|
|
315
|
+
__name(compactProviderError, "compactProviderError");
|
|
316
|
+
var IDLE_TIMEOUT_CODE = /^UND_ERR_(?:BODY|HEADERS)_TIMEOUT$/;
|
|
317
|
+
var IDLE_TIMEOUT_TEXT = /(?:^|\b)(?:Body|Headers) Timeout Error\b/i;
|
|
318
|
+
function isIdleTimeoutError(text) {
|
|
319
|
+
const s = typeof text === "string" ? text : "";
|
|
320
|
+
return IDLE_TIMEOUT_TEXT.test(s) || IDLE_TIMEOUT_CODE.test(s.trim());
|
|
321
|
+
}
|
|
322
|
+
__name(isIdleTimeoutError, "isIdleTimeoutError");
|
|
323
|
+
var IDLE_TIMEOUT_DESCRIPTION = {
|
|
324
|
+
message: "The provider stopped responding \u2014 the connection went idle.",
|
|
325
|
+
hint: "No data arrived for the whole idle-timeout window, so the turn was cut off. Send it again, or run /model to switch providers \u2014 a slow model can be given longer under /settings \u2192 HTTP idle timeout."
|
|
326
|
+
};
|
|
327
|
+
var MAX_RETRY_DELAY_MS = 6e4;
|
|
328
|
+
var RETRY_AFTER_PATTERNS = [
|
|
329
|
+
/retry-after(?:-ms)?["'\s:=]+(\d+(?:\.\d+)?)/i,
|
|
330
|
+
/(?:retry|try) again in (\d+(?:\.\d+)?)\s*(m?s|seconds?|minutes?)/i,
|
|
331
|
+
/retry after (\d+(?:\.\d+)?)\s*(m?s|seconds?|minutes?)/i
|
|
332
|
+
];
|
|
333
|
+
function retryAfterMs(text) {
|
|
334
|
+
const s = typeof text === "string" ? text : "";
|
|
335
|
+
for (const re of RETRY_AFTER_PATTERNS) {
|
|
336
|
+
const m = re.exec(s);
|
|
337
|
+
if (!m) continue;
|
|
338
|
+
const value = Number.parseFloat(m[1]);
|
|
339
|
+
if (!Number.isFinite(value) || value <= 0) continue;
|
|
340
|
+
const unit = (m[2] ?? "").toLowerCase();
|
|
341
|
+
const ms = unit === "ms" || /retry-after-ms/i.test(m[0]) ? value : unit.startsWith("m") && unit !== "ms" ? value * 6e4 : value * 1e3;
|
|
342
|
+
return Math.min(Math.max(Math.round(ms), 1e3), MAX_RETRY_DELAY_MS);
|
|
343
|
+
}
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
__name(retryAfterMs, "retryAfterMs");
|
|
347
|
+
var PROVIDER_LABELS = {
|
|
348
|
+
privateer: "Privateer",
|
|
349
|
+
anthropic: "Anthropic",
|
|
350
|
+
openai: "OpenAI",
|
|
351
|
+
openrouter: "OpenRouter",
|
|
352
|
+
google: "Google",
|
|
353
|
+
tinfoil: "Tinfoil",
|
|
354
|
+
ollama: "Ollama"
|
|
355
|
+
};
|
|
356
|
+
function providerLabel(provider) {
|
|
357
|
+
if (!provider) return "the model provider";
|
|
358
|
+
return PROVIDER_LABELS[provider] ?? provider;
|
|
359
|
+
}
|
|
360
|
+
__name(providerLabel, "providerLabel");
|
|
361
|
+
var CONNECTION_FAILURE = /^\s*(?:connection error\.?|fetch failed|other side closed|socket hang up|(?:read |connect )?(?:ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|EAI_AGAIN|ENETUNREACH|EHOSTUNREACH)\b)/i;
|
|
362
|
+
function isConnectionFailureText(text) {
|
|
363
|
+
return CONNECTION_FAILURE.test(typeof text === "string" ? text : "");
|
|
364
|
+
}
|
|
365
|
+
__name(isConnectionFailureText, "isConnectionFailureText");
|
|
366
|
+
function describeErrorText(text, context = {}) {
|
|
367
|
+
const s = typeof text === "string" ? text : "";
|
|
368
|
+
const status = Number(/^\s*(\d{3})\b/.exec(s)?.[1] ?? NaN);
|
|
369
|
+
const account = context.provider === "privateer";
|
|
370
|
+
const who = providerLabel(context.provider);
|
|
371
|
+
if (!Number.isFinite(status)) {
|
|
372
|
+
if (isIdleTimeoutError(s)) {
|
|
373
|
+
return {
|
|
374
|
+
message: redactText(IDLE_TIMEOUT_DESCRIPTION.message),
|
|
375
|
+
hint: IDLE_TIMEOUT_DESCRIPTION.hint
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
if (isConnectionFailureText(s)) {
|
|
379
|
+
return {
|
|
380
|
+
message: redactText(`Couldn't reach ${who} \u2014 the request got no response ("${s.trim().slice(0, 80)}").`),
|
|
381
|
+
hint: account ? "Check this machine's connection. If it started around a sign-in, the session reconnects on your next message; if every message fails while a fresh `privateer` works, start a new session with /new." : "Check your connection and the provider's base URL, then send it again \u2014 or run /model to switch providers.",
|
|
382
|
+
retryable: true
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
if (status === 429) {
|
|
388
|
+
const stated = retryAfterMs(s);
|
|
389
|
+
return {
|
|
390
|
+
message: redactText(`Rate limited (429).`),
|
|
391
|
+
hint: stated ? `The provider asked for ${Math.ceil(stated / 1e3)}s. Wait that long and send it again.` : "Wait a moment and send it again \u2014 or run /model to switch to another provider.",
|
|
392
|
+
retryable: true
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
if (status === 401 || status === 403) {
|
|
396
|
+
const bare = /\(no body\)\s*$/i.test(s) || s.trim() === String(status);
|
|
397
|
+
const refused = status === 401 ? "rejected the credential" : "refused access";
|
|
398
|
+
return {
|
|
399
|
+
message: redactText(
|
|
400
|
+
bare ? `${who} ${refused} this request was sent with (${status}).` : compactProviderError(s)
|
|
401
|
+
),
|
|
402
|
+
hint: account ? "This run's Privateer session was refused. A fresh one is opened automatically \u2014 send the message again, or re-run a one-shot (`-p`) command. Only if it keeps failing, check `privateer auth status`." : `Check the API key for ${who} \u2014 run /login, or set the provider's API key env var.`
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
if (status === 404) {
|
|
406
|
+
return {
|
|
407
|
+
message: redactText(compactProviderError(s)),
|
|
408
|
+
hint: "Check the model id \u2014 run /model to switch."
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
if (status === 413 || /413\b|payload too large|request_too_large|request entity too large/i.test(s)) {
|
|
412
|
+
return {
|
|
413
|
+
message: redactText(`Request payload too large (413).`),
|
|
414
|
+
hint: "The conversation history or attached files exceed the server limit. Start a new session (/new) or remove large attachments."
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
if (status >= 500) {
|
|
418
|
+
return {
|
|
419
|
+
message: redactText(compactProviderError(s)),
|
|
420
|
+
hint: "Usually transient \u2014 retry shortly.",
|
|
421
|
+
retryable: true
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
__name(describeErrorText, "describeErrorText");
|
|
427
|
+
|
|
428
|
+
// src/crypto/terminalKey.ts?ext=privateer-gate
|
|
429
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, chmodSync as chmodSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
430
|
+
import { join as join2 } from "node:path";
|
|
431
|
+
function keyPath() {
|
|
432
|
+
return join2(globalDir(), "terminal-key.json");
|
|
433
|
+
}
|
|
434
|
+
__name(keyPath, "keyPath");
|
|
435
|
+
var cached;
|
|
436
|
+
function b64(bytes) {
|
|
437
|
+
return Buffer.from(bytes).toString("base64");
|
|
438
|
+
}
|
|
439
|
+
__name(b64, "b64");
|
|
440
|
+
function fromB64(s, label) {
|
|
441
|
+
const buf = Buffer.from(s, "base64");
|
|
442
|
+
if (buf.length !== 32) throw new Error(`${label} must be 32 bytes`);
|
|
443
|
+
return new Uint8Array(buf);
|
|
444
|
+
}
|
|
445
|
+
__name(fromB64, "fromB64");
|
|
446
|
+
function loadOrCreate() {
|
|
447
|
+
if (cached) return cached;
|
|
448
|
+
try {
|
|
449
|
+
const parsed = JSON.parse(readFileSync2(keyPath(), "utf8"));
|
|
450
|
+
if (parsed?.v === 1 && parsed.publicKey && parsed.secretKey) {
|
|
451
|
+
cached = {
|
|
452
|
+
publicKey: fromB64(parsed.publicKey, "terminal public key"),
|
|
453
|
+
secretKey: fromB64(parsed.secretKey, "terminal secret key")
|
|
454
|
+
};
|
|
455
|
+
return cached;
|
|
456
|
+
}
|
|
457
|
+
} catch {
|
|
458
|
+
}
|
|
459
|
+
const secretKey = x25519.utils.randomPrivateKey();
|
|
460
|
+
const publicKey = x25519.getPublicKey(secretKey);
|
|
461
|
+
const file = { v: 1, publicKey: b64(publicKey), secretKey: b64(secretKey) };
|
|
462
|
+
mkdirSync2(globalDir(), { recursive: true, mode: 448 });
|
|
463
|
+
writeFileSync2(keyPath(), JSON.stringify(file), { mode: 384 });
|
|
464
|
+
try {
|
|
465
|
+
chmodSync2(keyPath(), 384);
|
|
466
|
+
} catch {
|
|
467
|
+
}
|
|
468
|
+
cached = { publicKey, secretKey };
|
|
469
|
+
return cached;
|
|
470
|
+
}
|
|
471
|
+
__name(loadOrCreate, "loadOrCreate");
|
|
472
|
+
function terminalPublicKeyBase64() {
|
|
473
|
+
return b64(loadOrCreate().publicKey);
|
|
474
|
+
}
|
|
475
|
+
__name(terminalPublicKeyBase64, "terminalPublicKeyBase64");
|
|
476
|
+
|
|
477
|
+
// src/crypto/accountTrust.ts?ext=privateer-gate
|
|
478
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, chmodSync as chmodSync3, rmSync as rmSync2 } from "node:fs";
|
|
479
|
+
import { join as join3 } from "node:path";
|
|
480
|
+
function trustPath() {
|
|
481
|
+
return join3(globalDir(), "account-trust.json");
|
|
482
|
+
}
|
|
483
|
+
__name(trustPath, "trustPath");
|
|
484
|
+
function pinAccountSignKey(pub) {
|
|
485
|
+
const value = (pub ?? "").trim();
|
|
486
|
+
if (!value) return;
|
|
487
|
+
try {
|
|
488
|
+
const file = { v: 1, accountSignPub: value };
|
|
489
|
+
writeFileSync3(trustPath(), JSON.stringify(file), { mode: 384 });
|
|
490
|
+
try {
|
|
491
|
+
chmodSync3(trustPath(), 384);
|
|
492
|
+
} catch {
|
|
493
|
+
}
|
|
494
|
+
} catch {
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
__name(pinAccountSignKey, "pinAccountSignKey");
|
|
498
|
+
function loadAccountSignKey() {
|
|
499
|
+
try {
|
|
500
|
+
const parsed = JSON.parse(readFileSync3(trustPath(), "utf8"));
|
|
501
|
+
return parsed?.v === 1 && typeof parsed.accountSignPub === "string" ? parsed.accountSignPub : void 0;
|
|
502
|
+
} catch {
|
|
503
|
+
return void 0;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
__name(loadAccountSignKey, "loadAccountSignKey");
|
|
507
|
+
function clearAccountSignKey() {
|
|
508
|
+
try {
|
|
509
|
+
rmSync2(trustPath(), { force: true });
|
|
510
|
+
} catch {
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
__name(clearAccountSignKey, "clearAccountSignKey");
|
|
514
|
+
function tsPath() {
|
|
515
|
+
return join3(globalDir(), "control-sig.json");
|
|
516
|
+
}
|
|
517
|
+
__name(tsPath, "tsPath");
|
|
518
|
+
function loadControlSig() {
|
|
519
|
+
try {
|
|
520
|
+
const parsed = JSON.parse(readFileSync3(tsPath(), "utf8"));
|
|
521
|
+
if (parsed?.v === 1 && parsed.byTerm && typeof parsed.byTerm === "object") return parsed;
|
|
522
|
+
} catch {
|
|
523
|
+
}
|
|
524
|
+
return { v: 1, byTerm: {} };
|
|
525
|
+
}
|
|
526
|
+
__name(loadControlSig, "loadControlSig");
|
|
527
|
+
function loadLastControlTs(termId) {
|
|
528
|
+
const ts = loadControlSig().byTerm[termId];
|
|
529
|
+
return typeof ts === "number" ? ts : 0;
|
|
530
|
+
}
|
|
531
|
+
__name(loadLastControlTs, "loadLastControlTs");
|
|
532
|
+
function saveLastControlTs(termId, ts) {
|
|
533
|
+
try {
|
|
534
|
+
const file = loadControlSig();
|
|
535
|
+
file.byTerm[termId] = Math.max(file.byTerm[termId] ?? 0, ts);
|
|
536
|
+
writeFileSync3(tsPath(), JSON.stringify(file), { mode: 384 });
|
|
537
|
+
} catch {
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
__name(saveLastControlTs, "saveLastControlTs");
|
|
541
|
+
|
|
542
|
+
// src/auth/privateer.ts?ext=privateer-gate
|
|
543
|
+
var DEFAULT_SERVER_URL = "https://helix-server-m1ac.onrender.com";
|
|
544
|
+
function isSafeServerUrl(raw) {
|
|
545
|
+
let u;
|
|
546
|
+
try {
|
|
547
|
+
u = new URL(raw);
|
|
548
|
+
} catch {
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
if (u.protocol === "https:") return true;
|
|
552
|
+
if (u.protocol === "http:" && (u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "::1")) {
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
__name(isSafeServerUrl, "isSafeServerUrl");
|
|
558
|
+
function serverBaseUrl() {
|
|
559
|
+
const env = process.env.PRIVATEER_SERVER_URL?.replace(/\/$/, "");
|
|
560
|
+
if (env) {
|
|
561
|
+
if (!isSafeServerUrl(env)) {
|
|
562
|
+
throw new Error(
|
|
563
|
+
`Refusing PRIVATEER_SERVER_URL=${env}: must be https:// (http allowed only for localhost). This protects your account token from being sent over an insecure or attacker-controlled connection.`
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
return env;
|
|
567
|
+
}
|
|
568
|
+
const base = (loadCredentials()?.serverBaseUrl || DEFAULT_SERVER_URL).replace(/\/$/, "");
|
|
569
|
+
if (!isSafeServerUrl(base)) {
|
|
570
|
+
throw new Error(`Stored Privateer server URL is not https (${base}). Run /logout then /login again.`);
|
|
571
|
+
}
|
|
572
|
+
return base;
|
|
573
|
+
}
|
|
574
|
+
__name(serverBaseUrl, "serverBaseUrl");
|
|
575
|
+
function defaultDeviceLabel() {
|
|
576
|
+
try {
|
|
577
|
+
return `${userInfo().username}@${hostname()}`;
|
|
578
|
+
} catch {
|
|
579
|
+
return "terminal";
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
__name(defaultDeviceLabel, "defaultDeviceLabel");
|
|
583
|
+
var SHARED = /* @__PURE__ */ Symbol.for("privateer.auth.shared");
|
|
584
|
+
function shared() {
|
|
585
|
+
const g = globalThis;
|
|
586
|
+
return g[SHARED] ??= { cache: null, signedIn: /* @__PURE__ */ new Set(), expired: /* @__PURE__ */ new Set() };
|
|
587
|
+
}
|
|
588
|
+
__name(shared, "shared");
|
|
589
|
+
var _child = null;
|
|
590
|
+
var _spawnInFlight = null;
|
|
591
|
+
var _refreshInFlight = null;
|
|
592
|
+
var _account = null;
|
|
593
|
+
function loadCredentials() {
|
|
594
|
+
const state = shared();
|
|
595
|
+
if (state.cache) return state.cache;
|
|
596
|
+
const path = credentialsPath();
|
|
597
|
+
if (!existsSync2(path)) return null;
|
|
598
|
+
try {
|
|
599
|
+
state.cache = JSON.parse(readFileSync4(path, "utf8"));
|
|
600
|
+
} catch {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
return state.cache;
|
|
604
|
+
}
|
|
605
|
+
__name(loadCredentials, "loadCredentials");
|
|
606
|
+
function saveCredentials(creds) {
|
|
607
|
+
const dir = globalDir();
|
|
608
|
+
mkdirSync3(dir, { recursive: true });
|
|
609
|
+
tryChmod2(dir, 448);
|
|
610
|
+
const path = credentialsPath();
|
|
611
|
+
writeFileSync4(path, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
612
|
+
tryChmod2(path, 384);
|
|
613
|
+
shared().cache = creds;
|
|
614
|
+
}
|
|
615
|
+
__name(saveCredentials, "saveCredentials");
|
|
616
|
+
var ARMED_SLOT = /* @__PURE__ */ Symbol.for("privateer.accountCredential");
|
|
617
|
+
function forgetArmedAccountCredential() {
|
|
618
|
+
const slot = globalThis[ARMED_SLOT];
|
|
619
|
+
if (slot) slot.cred = void 0;
|
|
620
|
+
}
|
|
621
|
+
__name(forgetArmedAccountCredential, "forgetArmedAccountCredential");
|
|
622
|
+
function clearCredentials() {
|
|
623
|
+
try {
|
|
624
|
+
rmSync3(credentialsPath(), { force: true });
|
|
625
|
+
} catch {
|
|
626
|
+
}
|
|
627
|
+
clearAccountSignKey();
|
|
628
|
+
shared().cache = null;
|
|
629
|
+
_child = null;
|
|
630
|
+
_account = null;
|
|
631
|
+
forgetArmedAccountCredential();
|
|
632
|
+
}
|
|
633
|
+
__name(clearCredentials, "clearCredentials");
|
|
634
|
+
function hasCredentials() {
|
|
635
|
+
return loadCredentials() !== null;
|
|
636
|
+
}
|
|
637
|
+
__name(hasCredentials, "hasCredentials");
|
|
638
|
+
function currentUser() {
|
|
639
|
+
return loadCredentials()?.user ?? null;
|
|
640
|
+
}
|
|
641
|
+
__name(currentUser, "currentUser");
|
|
642
|
+
function tryChmod2(path, mode) {
|
|
643
|
+
try {
|
|
644
|
+
chmodSync4(path, mode);
|
|
645
|
+
} catch {
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
__name(tryChmod2, "tryChmod");
|
|
649
|
+
function onSessionExpired(listener) {
|
|
650
|
+
const listeners = shared().expired;
|
|
651
|
+
listeners.add(listener);
|
|
652
|
+
return () => listeners.delete(listener);
|
|
653
|
+
}
|
|
654
|
+
__name(onSessionExpired, "onSessionExpired");
|
|
655
|
+
function notifySessionExpired() {
|
|
656
|
+
for (const listener of shared().expired) {
|
|
657
|
+
try {
|
|
658
|
+
listener();
|
|
659
|
+
} catch {
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
__name(notifySessionExpired, "notifySessionExpired");
|
|
664
|
+
function handleServerRevoke() {
|
|
665
|
+
if (!hasCredentials()) return;
|
|
666
|
+
clearCredentials();
|
|
667
|
+
notifySessionExpired();
|
|
668
|
+
}
|
|
669
|
+
__name(handleServerRevoke, "handleServerRevoke");
|
|
670
|
+
function onSignedIn(listener) {
|
|
671
|
+
const listeners = shared().signedIn;
|
|
672
|
+
listeners.add(listener);
|
|
673
|
+
return () => listeners.delete(listener);
|
|
674
|
+
}
|
|
675
|
+
__name(onSignedIn, "onSignedIn");
|
|
676
|
+
function notifySignedIn() {
|
|
677
|
+
for (const listener of shared().signedIn) {
|
|
678
|
+
try {
|
|
679
|
+
listener();
|
|
680
|
+
} catch {
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
__name(notifySignedIn, "notifySignedIn");
|
|
685
|
+
async function postJson(base, path, body, init = {}) {
|
|
686
|
+
return fetch(`${base}${path}`, {
|
|
687
|
+
...init,
|
|
688
|
+
method: "POST",
|
|
689
|
+
headers: { "Content-Type": "application/json", ...init.headers || {} },
|
|
690
|
+
body: JSON.stringify(body)
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
__name(postJson, "postJson");
|
|
694
|
+
async function requestDeviceCode(deviceLabel = defaultDeviceLabel()) {
|
|
695
|
+
const base = serverBaseUrl();
|
|
696
|
+
let terminalPub;
|
|
697
|
+
try {
|
|
698
|
+
terminalPub = terminalPublicKeyBase64();
|
|
699
|
+
} catch {
|
|
700
|
+
}
|
|
701
|
+
const res = await postJson(base, "/auth/device/code", { deviceLabel, terminalPub });
|
|
702
|
+
if (!res.ok) {
|
|
703
|
+
throw new Error(`Couldn't start login (${res.status}). Check your connection or PRIVATEER_SERVER_URL.`);
|
|
704
|
+
}
|
|
705
|
+
return await res.json();
|
|
706
|
+
}
|
|
707
|
+
__name(requestDeviceCode, "requestDeviceCode");
|
|
708
|
+
async function pollForToken(code, opts = {}) {
|
|
709
|
+
const base = serverBaseUrl();
|
|
710
|
+
let interval = Math.max(1, code.interval || 5) * 1e3;
|
|
711
|
+
const deadline = Date.now() + (code.expires_in || 600) * 1e3;
|
|
712
|
+
while (Date.now() < deadline) {
|
|
713
|
+
if (opts.signal?.aborted) throw new Error("Login cancelled.");
|
|
714
|
+
await sleep(interval, opts.signal);
|
|
715
|
+
const res = await postJson(base, "/auth/device/token", { device_code: code.device_code });
|
|
716
|
+
if (res.ok) {
|
|
717
|
+
const data = await res.json();
|
|
718
|
+
const creds = { accessToken: data.accessToken, refreshToken: data.refreshToken, user: data.user, serverBaseUrl: base };
|
|
719
|
+
saveCredentials(creds);
|
|
720
|
+
pinAccountSignKey(data.accountSignPub);
|
|
721
|
+
notifySignedIn();
|
|
722
|
+
return creds;
|
|
723
|
+
}
|
|
724
|
+
let err = "";
|
|
725
|
+
try {
|
|
726
|
+
err = (await res.json()).error || "";
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
if (err === "authorization_pending") {
|
|
730
|
+
opts.onPoll?.("pending");
|
|
731
|
+
} else if (err === "slow_down") {
|
|
732
|
+
interval += 2e3;
|
|
733
|
+
opts.onPoll?.("slow_down");
|
|
734
|
+
} else if (err === "access_denied") {
|
|
735
|
+
throw new Error("Login was denied in the app.");
|
|
736
|
+
} else if (err === "expired_token") {
|
|
737
|
+
throw new Error("This login code expired. Run /login again.");
|
|
738
|
+
} else {
|
|
739
|
+
throw new Error(`Login failed (${res.status}${err ? `: ${err}` : ""}).`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
throw new Error("This login code expired. Run /login again.");
|
|
743
|
+
}
|
|
744
|
+
__name(pollForToken, "pollForToken");
|
|
745
|
+
async function runDeviceLogin(opts) {
|
|
746
|
+
const code = await requestDeviceCode(opts.deviceLabel);
|
|
747
|
+
opts.onCode(code);
|
|
748
|
+
const creds = await pollForToken(code, { onPoll: opts.onPoll, signal: opts.signal });
|
|
749
|
+
return creds.user;
|
|
750
|
+
}
|
|
751
|
+
__name(runDeviceLogin, "runDeviceLogin");
|
|
752
|
+
async function spawnFailure(res) {
|
|
753
|
+
if (res.status === 401) {
|
|
754
|
+
if (hasCredentials()) {
|
|
755
|
+
clearCredentials();
|
|
756
|
+
notifySessionExpired();
|
|
757
|
+
}
|
|
758
|
+
return new Error("Your Privateer session expired. Run /login to sign in again.");
|
|
759
|
+
}
|
|
760
|
+
let message;
|
|
761
|
+
try {
|
|
762
|
+
message = (await res.json()).message;
|
|
763
|
+
} catch {
|
|
764
|
+
}
|
|
765
|
+
return new Error(message?.trim() || `Couldn't start a Privateer session (HTTP ${res.status}).`);
|
|
766
|
+
}
|
|
767
|
+
__name(spawnFailure, "spawnFailure");
|
|
768
|
+
async function spawnChildSession() {
|
|
769
|
+
const parent = loadCredentials();
|
|
770
|
+
if (!parent) throw new Error("Not logged in to Privateer. Run /login.");
|
|
771
|
+
const res = await postJson(serverBaseUrl(), "/auth/session/spawn", {
|
|
772
|
+
refreshToken: parent.refreshToken,
|
|
773
|
+
deviceLabel: defaultDeviceLabel()
|
|
774
|
+
}, {
|
|
775
|
+
headers: { Authorization: `Bearer ${parent.accessToken}` }
|
|
776
|
+
});
|
|
777
|
+
if (!res.ok) throw await spawnFailure(res);
|
|
778
|
+
const { accessToken, refreshToken } = await res.json();
|
|
779
|
+
_child = { accessToken, refreshToken };
|
|
780
|
+
return _child;
|
|
781
|
+
}
|
|
782
|
+
__name(spawnChildSession, "spawnChildSession");
|
|
783
|
+
async function warmSession() {
|
|
784
|
+
if (!hasCredentials()) return;
|
|
785
|
+
try {
|
|
786
|
+
await ensureChildSession();
|
|
787
|
+
} catch {
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
__name(warmSession, "warmSession");
|
|
791
|
+
function ensureChildSession() {
|
|
792
|
+
if (_child) return Promise.resolve(_child);
|
|
793
|
+
if (!_spawnInFlight) {
|
|
794
|
+
_spawnInFlight = spawnChildSession().finally(() => {
|
|
795
|
+
_spawnInFlight = null;
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
return _spawnInFlight;
|
|
799
|
+
}
|
|
800
|
+
__name(ensureChildSession, "ensureChildSession");
|
|
801
|
+
function refreshChildSession() {
|
|
802
|
+
if (_refreshInFlight) return _refreshInFlight;
|
|
803
|
+
_refreshInFlight = (async () => {
|
|
804
|
+
if (_child) {
|
|
805
|
+
const res = await postJson(serverBaseUrl(), "/auth/refresh", { refreshToken: _child.refreshToken });
|
|
806
|
+
if (res.ok) {
|
|
807
|
+
const { accessToken, refreshToken } = await res.json();
|
|
808
|
+
_child = { accessToken, refreshToken };
|
|
809
|
+
return _child;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
_child = null;
|
|
813
|
+
return spawnChildSession();
|
|
814
|
+
})().finally(() => {
|
|
815
|
+
_refreshInFlight = null;
|
|
816
|
+
});
|
|
817
|
+
return _refreshInFlight;
|
|
818
|
+
}
|
|
819
|
+
__name(refreshChildSession, "refreshChildSession");
|
|
820
|
+
async function authedFetch(input, init = {}) {
|
|
821
|
+
const bodyBuf = init.body;
|
|
822
|
+
const withAuth = /* @__PURE__ */ __name((token) => {
|
|
823
|
+
const headers = new Headers(init.headers);
|
|
824
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
825
|
+
return { ...init, headers };
|
|
826
|
+
}, "withAuth");
|
|
827
|
+
let child = await ensureChildSession();
|
|
828
|
+
let res = await fetch(input, withAuth(child.accessToken));
|
|
829
|
+
if (res.status === 401) {
|
|
830
|
+
child = await refreshChildSession();
|
|
831
|
+
res = await fetch(input, { ...withAuth(child.accessToken), body: bodyBuf });
|
|
832
|
+
}
|
|
833
|
+
return await defuseRetryableCap(res);
|
|
834
|
+
}
|
|
835
|
+
__name(authedFetch, "authedFetch");
|
|
836
|
+
async function defuseRetryableCap(res) {
|
|
837
|
+
if (res.status !== 429) return res;
|
|
838
|
+
const body = await res.text();
|
|
839
|
+
let code;
|
|
840
|
+
try {
|
|
841
|
+
const parsed = JSON.parse(body);
|
|
842
|
+
code = parsed.code ?? parsed.error?.code;
|
|
843
|
+
} catch {
|
|
844
|
+
}
|
|
845
|
+
const status = isAccountCapCode(typeof code === "string" ? code : void 0) ? 402 : 429;
|
|
846
|
+
return new Response(body, { status, statusText: res.statusText, headers: res.headers });
|
|
847
|
+
}
|
|
848
|
+
__name(defuseRetryableCap, "defuseRetryableCap");
|
|
849
|
+
async function apiRequest(path, init = {}) {
|
|
850
|
+
const base = serverBaseUrl();
|
|
851
|
+
return authedFetch(`${base}${path}`, init);
|
|
852
|
+
}
|
|
853
|
+
__name(apiRequest, "apiRequest");
|
|
854
|
+
async function deleteSession(accessToken, timeoutMs) {
|
|
855
|
+
try {
|
|
856
|
+
await fetch(`${serverBaseUrl()}/auth/session/current`, {
|
|
857
|
+
method: "DELETE",
|
|
858
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
859
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
860
|
+
});
|
|
861
|
+
} catch {
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
__name(deleteSession, "deleteSession");
|
|
865
|
+
async function revokeChildSession(timeoutMs = 1500) {
|
|
866
|
+
const child = _child;
|
|
867
|
+
if (!child) return;
|
|
868
|
+
_child = null;
|
|
869
|
+
await deleteSession(child.accessToken, timeoutMs);
|
|
870
|
+
}
|
|
871
|
+
__name(revokeChildSession, "revokeChildSession");
|
|
872
|
+
async function revokeAccountSession(timeoutMs = 1500) {
|
|
873
|
+
const account = _account;
|
|
874
|
+
if (!account) return;
|
|
875
|
+
_account = null;
|
|
876
|
+
forgetOwnedSession();
|
|
877
|
+
await deleteSession(account.accessToken, timeoutMs);
|
|
878
|
+
}
|
|
879
|
+
__name(revokeAccountSession, "revokeAccountSession");
|
|
880
|
+
async function revokeLocalSessions(timeoutMs = 1500) {
|
|
881
|
+
await Promise.all([revokeChildSession(timeoutMs), revokeAccountSession(timeoutMs)]);
|
|
882
|
+
}
|
|
883
|
+
__name(revokeLocalSessions, "revokeLocalSessions");
|
|
884
|
+
async function logout() {
|
|
885
|
+
const parent = loadCredentials();
|
|
886
|
+
if (parent) {
|
|
887
|
+
try {
|
|
888
|
+
const fresh = await rotateSession(parent.refreshToken);
|
|
889
|
+
await deleteSession(fresh.access, 5e3);
|
|
890
|
+
} catch {
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
_child = null;
|
|
894
|
+
_account = null;
|
|
895
|
+
clearOwnedSessions();
|
|
896
|
+
clearCredentials();
|
|
897
|
+
}
|
|
898
|
+
__name(logout, "logout");
|
|
899
|
+
function jwtExpMs(token, fallbackMs = 5 * 6e4) {
|
|
900
|
+
try {
|
|
901
|
+
const seg = token.split(".")[1];
|
|
902
|
+
const payload = JSON.parse(Buffer.from(seg, "base64").toString("utf8"));
|
|
903
|
+
if (typeof payload.exp === "number") return payload.exp * 1e3 - 3e4;
|
|
904
|
+
} catch {
|
|
905
|
+
}
|
|
906
|
+
return Date.now() + fallbackMs;
|
|
907
|
+
}
|
|
908
|
+
__name(jwtExpMs, "jwtExpMs");
|
|
909
|
+
async function spawnAccountCredentials() {
|
|
910
|
+
const parent = loadCredentials();
|
|
911
|
+
if (!parent) throw new Error("Not logged in to Privateer. Run /login.");
|
|
912
|
+
const res = await postJson(
|
|
913
|
+
serverBaseUrl(),
|
|
914
|
+
"/auth/session/spawn",
|
|
915
|
+
{ refreshToken: parent.refreshToken, deviceLabel: defaultDeviceLabel() },
|
|
916
|
+
{ headers: { Authorization: `Bearer ${parent.accessToken}` } }
|
|
917
|
+
);
|
|
918
|
+
if (!res.ok) throw await spawnFailure(res);
|
|
919
|
+
const { accessToken, refreshToken } = await res.json();
|
|
920
|
+
_account = { accessToken };
|
|
921
|
+
const cred = { access: accessToken, refresh: refreshToken, expires: jwtExpMs(accessToken) };
|
|
922
|
+
recordOwnedSession(cred);
|
|
923
|
+
return cred;
|
|
924
|
+
}
|
|
925
|
+
__name(spawnAccountCredentials, "spawnAccountCredentials");
|
|
926
|
+
function isRefreshRejection(e) {
|
|
927
|
+
return e instanceof Error && typeof e.status === "number";
|
|
928
|
+
}
|
|
929
|
+
__name(isRefreshRejection, "isRefreshRejection");
|
|
930
|
+
async function rotateSession(refresh) {
|
|
931
|
+
const res = await postJson(serverBaseUrl(), "/auth/refresh", { refreshToken: refresh });
|
|
932
|
+
if (!res.ok) {
|
|
933
|
+
const err = new Error(`account refresh failed (${res.status})`);
|
|
934
|
+
err.status = res.status;
|
|
935
|
+
throw err;
|
|
936
|
+
}
|
|
937
|
+
const { accessToken, refreshToken } = await res.json();
|
|
938
|
+
return { access: accessToken, refresh: refreshToken, expires: jwtExpMs(accessToken) };
|
|
939
|
+
}
|
|
940
|
+
__name(rotateSession, "rotateSession");
|
|
941
|
+
async function refreshAccountCredentials(refresh) {
|
|
942
|
+
const cred = await rotateSession(refresh);
|
|
943
|
+
_account = { accessToken: cred.access };
|
|
944
|
+
recordOwnedSession(cred);
|
|
945
|
+
return cred;
|
|
946
|
+
}
|
|
947
|
+
__name(refreshAccountCredentials, "refreshAccountCredentials");
|
|
948
|
+
async function acquireAccountCredential() {
|
|
949
|
+
const orphans = orphanedSessions();
|
|
950
|
+
let adopted = null;
|
|
951
|
+
let attempted = 0;
|
|
952
|
+
while (attempted < orphans.length && !adopted) {
|
|
953
|
+
const orphan = orphans[attempted++];
|
|
954
|
+
if (!claimOrphan(orphan.pid)) continue;
|
|
955
|
+
try {
|
|
956
|
+
adopted = await refreshAccountCredentials(orphan.refresh);
|
|
957
|
+
dropOwnedSession(orphan.pid);
|
|
958
|
+
} catch (e) {
|
|
959
|
+
if (isRefreshRejection(e)) dropOwnedSession(orphan.pid);
|
|
960
|
+
else releaseOrphan(orphan.pid);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
const leftovers = orphans.slice(attempted);
|
|
964
|
+
if (leftovers.length) void revokeOrphanedSessions(leftovers);
|
|
965
|
+
return adopted ?? await spawnAccountCredentials();
|
|
966
|
+
}
|
|
967
|
+
__name(acquireAccountCredential, "acquireAccountCredential");
|
|
968
|
+
async function revokeOrphanedSessions(orphans, timeoutMs = 1500) {
|
|
969
|
+
for (const orphan of orphans) {
|
|
970
|
+
if (!claimOrphan(orphan.pid)) continue;
|
|
971
|
+
try {
|
|
972
|
+
const cred = await rotateSession(orphan.refresh);
|
|
973
|
+
await deleteSession(cred.access, timeoutMs);
|
|
974
|
+
dropOwnedSession(orphan.pid);
|
|
975
|
+
} catch (e) {
|
|
976
|
+
if (isRefreshRejection(e)) dropOwnedSession(orphan.pid);
|
|
977
|
+
else releaseOrphan(orphan.pid);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
__name(revokeOrphanedSessions, "revokeOrphanedSessions");
|
|
982
|
+
function sleep(ms, signal) {
|
|
983
|
+
return new Promise((resolve, reject) => {
|
|
984
|
+
const t = setTimeout(resolve, ms);
|
|
985
|
+
signal?.addEventListener(
|
|
986
|
+
"abort",
|
|
987
|
+
() => {
|
|
988
|
+
clearTimeout(t);
|
|
989
|
+
reject(new Error("Login cancelled."));
|
|
990
|
+
},
|
|
991
|
+
{ once: true }
|
|
992
|
+
);
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
__name(sleep, "sleep");
|
|
996
|
+
|
|
997
|
+
export {
|
|
998
|
+
redactText,
|
|
999
|
+
describeAccountBalanceError,
|
|
1000
|
+
isConnectionFailureText,
|
|
1001
|
+
describeErrorText,
|
|
1002
|
+
loadAccountSignKey,
|
|
1003
|
+
loadLastControlTs,
|
|
1004
|
+
saveLastControlTs,
|
|
1005
|
+
DEFAULT_SERVER_URL,
|
|
1006
|
+
isSafeServerUrl,
|
|
1007
|
+
serverBaseUrl,
|
|
1008
|
+
defaultDeviceLabel,
|
|
1009
|
+
loadCredentials,
|
|
1010
|
+
saveCredentials,
|
|
1011
|
+
clearCredentials,
|
|
1012
|
+
hasCredentials,
|
|
1013
|
+
currentUser,
|
|
1014
|
+
onSessionExpired,
|
|
1015
|
+
handleServerRevoke,
|
|
1016
|
+
onSignedIn,
|
|
1017
|
+
notifySignedIn,
|
|
1018
|
+
requestDeviceCode,
|
|
1019
|
+
pollForToken,
|
|
1020
|
+
runDeviceLogin,
|
|
1021
|
+
warmSession,
|
|
1022
|
+
authedFetch,
|
|
1023
|
+
apiRequest,
|
|
1024
|
+
revokeChildSession,
|
|
1025
|
+
revokeAccountSession,
|
|
1026
|
+
revokeLocalSessions,
|
|
1027
|
+
logout,
|
|
1028
|
+
spawnAccountCredentials,
|
|
1029
|
+
isRefreshRejection,
|
|
1030
|
+
refreshAccountCredentials,
|
|
1031
|
+
acquireAccountCredential
|
|
1032
|
+
};
|