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,1969 @@
|
|
|
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
|
+
value_exports
|
|
4
|
+
} from "./c-6LOBR4I6.mjs";
|
|
5
|
+
import {
|
|
6
|
+
typebox_exports
|
|
7
|
+
} from "./c-RQJJ26T4.mjs";
|
|
8
|
+
import {
|
|
9
|
+
__name
|
|
10
|
+
} from "./c-CN77DCGF.mjs";
|
|
11
|
+
|
|
12
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/brave.ts?ext=privateer-web
|
|
13
|
+
var BRAVE_SEARCH_API_URL = "https://api.search.brave.com/res/v1/web/search";
|
|
14
|
+
var BRAVE_API_KEY_ENV_VAR = "BRAVE_SEARCH_API_KEY";
|
|
15
|
+
var BRAVE_PROVIDER_META = {
|
|
16
|
+
name: "brave",
|
|
17
|
+
label: "Brave",
|
|
18
|
+
envVar: BRAVE_API_KEY_ENV_VAR,
|
|
19
|
+
roles: ["search"]
|
|
20
|
+
};
|
|
21
|
+
function normalizeBraveResults(raw) {
|
|
22
|
+
return (raw.web?.results ?? []).map((r) => ({
|
|
23
|
+
title: r.title ?? "",
|
|
24
|
+
url: r.url ?? "",
|
|
25
|
+
snippet: r.description ?? ""
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
__name(normalizeBraveResults, "normalizeBraveResults");
|
|
29
|
+
var BraveProvider = class {
|
|
30
|
+
constructor(apiKey) {
|
|
31
|
+
this.apiKey = apiKey;
|
|
32
|
+
}
|
|
33
|
+
apiKey;
|
|
34
|
+
static {
|
|
35
|
+
__name(this, "BraveProvider");
|
|
36
|
+
}
|
|
37
|
+
name = BRAVE_PROVIDER_META.name;
|
|
38
|
+
label = BRAVE_PROVIDER_META.label;
|
|
39
|
+
envVar = BRAVE_PROVIDER_META.envVar;
|
|
40
|
+
async search(query, maxResults, signal) {
|
|
41
|
+
if (!this.apiKey) {
|
|
42
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
43
|
+
}
|
|
44
|
+
const url = new URL(BRAVE_SEARCH_API_URL);
|
|
45
|
+
url.searchParams.set("q", query);
|
|
46
|
+
url.searchParams.set("count", String(maxResults));
|
|
47
|
+
const res = await fetch(url.toString(), {
|
|
48
|
+
method: "GET",
|
|
49
|
+
headers: {
|
|
50
|
+
Accept: "application/json",
|
|
51
|
+
"Accept-Encoding": "gzip",
|
|
52
|
+
"X-Subscription-Token": this.apiKey
|
|
53
|
+
},
|
|
54
|
+
signal
|
|
55
|
+
});
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
const text = await res.text();
|
|
58
|
+
throw new Error(`${this.label} Search API error (${res.status}): ${text}`);
|
|
59
|
+
}
|
|
60
|
+
const raw = await res.json();
|
|
61
|
+
return { query, results: normalizeBraveResults(raw) };
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/exa.ts?ext=privateer-web
|
|
66
|
+
var EXA_API_URL = "https://api.exa.ai/search";
|
|
67
|
+
var EXA_CONTENTS_API_URL = "https://api.exa.ai/contents";
|
|
68
|
+
var EXA_API_KEY_ENV_VAR = "EXA_API_KEY";
|
|
69
|
+
var EXA_PROVIDER_META = {
|
|
70
|
+
name: "exa",
|
|
71
|
+
label: "Exa",
|
|
72
|
+
envVar: EXA_API_KEY_ENV_VAR,
|
|
73
|
+
roles: ["search", "fetch"]
|
|
74
|
+
};
|
|
75
|
+
var EXA_MAX_SNIPPET_CHARACTERS = 300;
|
|
76
|
+
var EXA_MAX_FETCH_CHARACTERS = 1e6;
|
|
77
|
+
function normalizeExaResults(results) {
|
|
78
|
+
return results.map((r) => ({
|
|
79
|
+
title: r.title ?? "",
|
|
80
|
+
url: r.url ?? "",
|
|
81
|
+
snippet: r.text ?? ""
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
__name(normalizeExaResults, "normalizeExaResults");
|
|
85
|
+
var ExaProvider = class {
|
|
86
|
+
constructor(apiKey) {
|
|
87
|
+
this.apiKey = apiKey;
|
|
88
|
+
}
|
|
89
|
+
apiKey;
|
|
90
|
+
static {
|
|
91
|
+
__name(this, "ExaProvider");
|
|
92
|
+
}
|
|
93
|
+
name = EXA_PROVIDER_META.name;
|
|
94
|
+
label = EXA_PROVIDER_META.label;
|
|
95
|
+
envVar = EXA_PROVIDER_META.envVar;
|
|
96
|
+
async search(query, maxResults, signal) {
|
|
97
|
+
if (!this.apiKey) {
|
|
98
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
99
|
+
}
|
|
100
|
+
const res = await fetch(EXA_API_URL, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: {
|
|
103
|
+
"Content-Type": "application/json",
|
|
104
|
+
"x-api-key": this.apiKey
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
query,
|
|
108
|
+
numResults: maxResults,
|
|
109
|
+
contents: {
|
|
110
|
+
text: { maxCharacters: EXA_MAX_SNIPPET_CHARACTERS }
|
|
111
|
+
}
|
|
112
|
+
}),
|
|
113
|
+
signal
|
|
114
|
+
});
|
|
115
|
+
if (!res.ok) {
|
|
116
|
+
const text = await res.text();
|
|
117
|
+
throw new Error(`${this.label} Search API error (${res.status}): ${text}`);
|
|
118
|
+
}
|
|
119
|
+
const raw = await res.json();
|
|
120
|
+
return { query, results: normalizeExaResults(raw.results ?? []) };
|
|
121
|
+
}
|
|
122
|
+
async fetch(url, _raw, signal) {
|
|
123
|
+
if (!this.apiKey) {
|
|
124
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
125
|
+
}
|
|
126
|
+
const res = await fetch(EXA_CONTENTS_API_URL, {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: {
|
|
129
|
+
"Content-Type": "application/json",
|
|
130
|
+
"x-api-key": this.apiKey
|
|
131
|
+
},
|
|
132
|
+
body: JSON.stringify({
|
|
133
|
+
ids: [url],
|
|
134
|
+
text: { maxCharacters: EXA_MAX_FETCH_CHARACTERS }
|
|
135
|
+
}),
|
|
136
|
+
signal
|
|
137
|
+
});
|
|
138
|
+
if (!res.ok) {
|
|
139
|
+
const text = await res.text();
|
|
140
|
+
throw new Error(`${this.label} Fetch API error (${res.status}): ${text}`);
|
|
141
|
+
}
|
|
142
|
+
const data = await res.json();
|
|
143
|
+
const result = data.results?.[0];
|
|
144
|
+
if (!result?.text) {
|
|
145
|
+
throw new Error(`${this.label} Fetch API error: no content returned for ${url}`);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
text: result.text,
|
|
149
|
+
title: result.title || void 0,
|
|
150
|
+
contentType: "text/plain"
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/firecrawl.ts?ext=privateer-web
|
|
156
|
+
var FIRECRAWL_API_URL = "https://api.firecrawl.dev/v1";
|
|
157
|
+
var FIRECRAWL_API_KEY_ENV_VAR = "FIRECRAWL_API_KEY";
|
|
158
|
+
var FIRECRAWL_PROVIDER_META = {
|
|
159
|
+
name: "firecrawl",
|
|
160
|
+
label: "Firecrawl",
|
|
161
|
+
envVar: FIRECRAWL_API_KEY_ENV_VAR,
|
|
162
|
+
roles: ["search", "fetch"]
|
|
163
|
+
};
|
|
164
|
+
function normalizeFirecrawlResults(results) {
|
|
165
|
+
return results.map((r) => ({
|
|
166
|
+
title: r.title ?? "",
|
|
167
|
+
url: r.url ?? "",
|
|
168
|
+
snippet: r.description ?? ""
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
__name(normalizeFirecrawlResults, "normalizeFirecrawlResults");
|
|
172
|
+
var FirecrawlProvider = class {
|
|
173
|
+
constructor(apiKey) {
|
|
174
|
+
this.apiKey = apiKey;
|
|
175
|
+
}
|
|
176
|
+
apiKey;
|
|
177
|
+
static {
|
|
178
|
+
__name(this, "FirecrawlProvider");
|
|
179
|
+
}
|
|
180
|
+
name = FIRECRAWL_PROVIDER_META.name;
|
|
181
|
+
label = FIRECRAWL_PROVIDER_META.label;
|
|
182
|
+
envVar = FIRECRAWL_PROVIDER_META.envVar;
|
|
183
|
+
async search(query, maxResults, signal) {
|
|
184
|
+
if (!this.apiKey) {
|
|
185
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
186
|
+
}
|
|
187
|
+
const res = await fetch(`${FIRECRAWL_API_URL}/search`, {
|
|
188
|
+
method: "POST",
|
|
189
|
+
headers: {
|
|
190
|
+
"Content-Type": "application/json",
|
|
191
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
192
|
+
},
|
|
193
|
+
body: JSON.stringify({
|
|
194
|
+
query,
|
|
195
|
+
limit: maxResults
|
|
196
|
+
}),
|
|
197
|
+
signal
|
|
198
|
+
});
|
|
199
|
+
if (!res.ok) {
|
|
200
|
+
const text = await res.text();
|
|
201
|
+
throw new Error(`${this.label} Search API error (${res.status}): ${text}`);
|
|
202
|
+
}
|
|
203
|
+
const raw = await res.json();
|
|
204
|
+
return { query, results: normalizeFirecrawlResults(raw.data ?? []) };
|
|
205
|
+
}
|
|
206
|
+
async fetch(url, _raw, signal) {
|
|
207
|
+
if (!this.apiKey) {
|
|
208
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
209
|
+
}
|
|
210
|
+
const res = await fetch(`${FIRECRAWL_API_URL}/scrape`, {
|
|
211
|
+
method: "POST",
|
|
212
|
+
headers: {
|
|
213
|
+
"Content-Type": "application/json",
|
|
214
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
215
|
+
},
|
|
216
|
+
body: JSON.stringify({
|
|
217
|
+
url,
|
|
218
|
+
formats: ["markdown"]
|
|
219
|
+
}),
|
|
220
|
+
signal
|
|
221
|
+
});
|
|
222
|
+
if (!res.ok) {
|
|
223
|
+
const text = await res.text();
|
|
224
|
+
throw new Error(`${this.label} Fetch API error (${res.status}): ${text}`);
|
|
225
|
+
}
|
|
226
|
+
const raw = await res.json();
|
|
227
|
+
if (!raw.success) {
|
|
228
|
+
throw new Error(`${this.label} Fetch API error: ${raw.error ?? "scrape failed"}`);
|
|
229
|
+
}
|
|
230
|
+
if (!raw.data?.markdown) {
|
|
231
|
+
throw new Error(`${this.label} Fetch API error: no content returned for ${url}`);
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
text: raw.data.markdown,
|
|
235
|
+
title: raw.data.metadata?.title || void 0,
|
|
236
|
+
contentType: "text/markdown"
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/jina.ts?ext=privateer-web
|
|
242
|
+
var JINA_SEARCH_API_URL = "https://s.jina.ai/";
|
|
243
|
+
var JINA_READER_API_URL = "https://r.jina.ai/";
|
|
244
|
+
var JINA_API_KEY_ENV_VAR = "JINA_API_KEY";
|
|
245
|
+
var JINA_PROVIDER_META = {
|
|
246
|
+
name: "jina",
|
|
247
|
+
label: "Jina",
|
|
248
|
+
envVar: JINA_API_KEY_ENV_VAR,
|
|
249
|
+
roles: ["search", "fetch"]
|
|
250
|
+
};
|
|
251
|
+
function normalizeJinaResults(results) {
|
|
252
|
+
return results.map((r) => ({
|
|
253
|
+
title: r.title ?? "",
|
|
254
|
+
url: r.url ?? "",
|
|
255
|
+
snippet: r.description ?? ""
|
|
256
|
+
}));
|
|
257
|
+
}
|
|
258
|
+
__name(normalizeJinaResults, "normalizeJinaResults");
|
|
259
|
+
var JinaProvider = class {
|
|
260
|
+
constructor(apiKey) {
|
|
261
|
+
this.apiKey = apiKey;
|
|
262
|
+
}
|
|
263
|
+
apiKey;
|
|
264
|
+
static {
|
|
265
|
+
__name(this, "JinaProvider");
|
|
266
|
+
}
|
|
267
|
+
name = JINA_PROVIDER_META.name;
|
|
268
|
+
label = JINA_PROVIDER_META.label;
|
|
269
|
+
envVar = JINA_PROVIDER_META.envVar;
|
|
270
|
+
async search(query, maxResults, signal) {
|
|
271
|
+
if (!this.apiKey) {
|
|
272
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
273
|
+
}
|
|
274
|
+
const url = new URL(`${JINA_SEARCH_API_URL}${encodeURIComponent(query)}`);
|
|
275
|
+
url.searchParams.set("num", String(maxResults));
|
|
276
|
+
const res = await fetch(url.toString(), {
|
|
277
|
+
method: "GET",
|
|
278
|
+
headers: {
|
|
279
|
+
Accept: "application/json",
|
|
280
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
281
|
+
},
|
|
282
|
+
signal
|
|
283
|
+
});
|
|
284
|
+
if (!res.ok) {
|
|
285
|
+
const text = await res.text();
|
|
286
|
+
throw new Error(`${this.label} Search API error (${res.status}): ${text}`);
|
|
287
|
+
}
|
|
288
|
+
const raw = await res.json();
|
|
289
|
+
const results = normalizeJinaResults(Array.isArray(raw.data) ? raw.data : raw.data?.results ?? []).slice(
|
|
290
|
+
0,
|
|
291
|
+
maxResults
|
|
292
|
+
);
|
|
293
|
+
return { query, results };
|
|
294
|
+
}
|
|
295
|
+
async fetch(url, _raw, signal) {
|
|
296
|
+
if (!this.apiKey) {
|
|
297
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
298
|
+
}
|
|
299
|
+
const res = await fetch(`${JINA_READER_API_URL}${url}`, {
|
|
300
|
+
method: "GET",
|
|
301
|
+
headers: {
|
|
302
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
303
|
+
},
|
|
304
|
+
signal
|
|
305
|
+
});
|
|
306
|
+
if (!res.ok) {
|
|
307
|
+
const text2 = await res.text();
|
|
308
|
+
throw new Error(`${this.label} Fetch API error (${res.status}): ${text2}`);
|
|
309
|
+
}
|
|
310
|
+
const text = await res.text();
|
|
311
|
+
if (!text.trim()) {
|
|
312
|
+
throw new Error(`${this.label} Fetch API error: no content returned for ${url}`);
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
text,
|
|
316
|
+
contentType: "text/markdown"
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/types.ts?ext=privateer-web
|
|
322
|
+
function isCancellation(input) {
|
|
323
|
+
return input == null;
|
|
324
|
+
}
|
|
325
|
+
__name(isCancellation, "isCancellation");
|
|
326
|
+
|
|
327
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/ollama.ts?ext=privateer-web
|
|
328
|
+
var OLLAMA_API_KEY_ENV_VAR = "OLLAMA_API_KEY";
|
|
329
|
+
var OLLAMA_HOST_ENV_VAR = "OLLAMA_HOST";
|
|
330
|
+
var OLLAMA_DEFAULT_URL = "http://localhost:11434";
|
|
331
|
+
var CLOUD_SEARCH_PATH = "/api/web_search";
|
|
332
|
+
var CLOUD_FETCH_PATH = "/api/web_fetch";
|
|
333
|
+
var LOCAL_SEARCH_PATH = "/api/experimental/web_search";
|
|
334
|
+
var LOCAL_FETCH_PATH = "/api/experimental/web_fetch";
|
|
335
|
+
function isLocalHost(baseUrl) {
|
|
336
|
+
try {
|
|
337
|
+
const hostname = new URL(baseUrl).hostname;
|
|
338
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "[::1]";
|
|
339
|
+
} catch {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
__name(isLocalHost, "isLocalHost");
|
|
344
|
+
var MASK_VISIBLE_CHARS = 4;
|
|
345
|
+
var OLLAMA_PROVIDER_META = {
|
|
346
|
+
name: "ollama",
|
|
347
|
+
label: "Ollama",
|
|
348
|
+
envVar: OLLAMA_API_KEY_ENV_VAR,
|
|
349
|
+
baseUrlEnvVar: OLLAMA_HOST_ENV_VAR,
|
|
350
|
+
defaultBaseUrl: OLLAMA_DEFAULT_URL,
|
|
351
|
+
roles: ["search", "fetch"],
|
|
352
|
+
configure: /* @__PURE__ */ __name((ui, current) => configureOllama(ui, current), "configure")
|
|
353
|
+
};
|
|
354
|
+
function normalizeOllamaResults(raw) {
|
|
355
|
+
return (raw.results ?? []).map((r) => ({
|
|
356
|
+
title: r.title ?? "",
|
|
357
|
+
url: r.url ?? "",
|
|
358
|
+
snippet: r.content ?? ""
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
__name(normalizeOllamaResults, "normalizeOllamaResults");
|
|
362
|
+
function stripTrailingSlashes(url) {
|
|
363
|
+
return url.replace(/\/+$/, "");
|
|
364
|
+
}
|
|
365
|
+
__name(stripTrailingSlashes, "stripTrailingSlashes");
|
|
366
|
+
function assertHttpUrl(url) {
|
|
367
|
+
let parsed;
|
|
368
|
+
try {
|
|
369
|
+
parsed = new URL(url);
|
|
370
|
+
} catch {
|
|
371
|
+
throw new Error(`${OLLAMA_HOST_ENV_VAR} is not a valid URL (got: ${url})`);
|
|
372
|
+
}
|
|
373
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`${OLLAMA_HOST_ENV_VAR} must use http:// or https:// (got: ${parsed.protocol.replace(":", "")}://)`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
__name(assertHttpUrl, "assertHttpUrl");
|
|
380
|
+
function isConnectionRefused(error) {
|
|
381
|
+
if (error instanceof TypeError) {
|
|
382
|
+
const cause = error.cause;
|
|
383
|
+
return cause?.code === "ECONNREFUSED";
|
|
384
|
+
}
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
__name(isConnectionRefused, "isConnectionRefused");
|
|
388
|
+
function connectionRefusedError(host) {
|
|
389
|
+
return new Error(`Could not connect to Ollama at ${host}. Make sure Ollama is running (ollama serve).`);
|
|
390
|
+
}
|
|
391
|
+
__name(connectionRefusedError, "connectionRefusedError");
|
|
392
|
+
var OllamaProvider = class {
|
|
393
|
+
static {
|
|
394
|
+
__name(this, "OllamaProvider");
|
|
395
|
+
}
|
|
396
|
+
name = "ollama";
|
|
397
|
+
label = "Ollama";
|
|
398
|
+
envVar = OLLAMA_API_KEY_ENV_VAR;
|
|
399
|
+
apiKey;
|
|
400
|
+
baseUrl;
|
|
401
|
+
local;
|
|
402
|
+
constructor(options) {
|
|
403
|
+
this.apiKey = options.apiKey?.trim() || void 0;
|
|
404
|
+
const trimmed = stripTrailingSlashes(options.baseUrl?.trim() ?? "");
|
|
405
|
+
if (trimmed) assertHttpUrl(trimmed);
|
|
406
|
+
this.baseUrl = trimmed;
|
|
407
|
+
this.local = isLocalHost(trimmed);
|
|
408
|
+
}
|
|
409
|
+
async search(query, maxResults, signal) {
|
|
410
|
+
this.requireBaseUrl();
|
|
411
|
+
const path = this.local ? LOCAL_SEARCH_PATH : CLOUD_SEARCH_PATH;
|
|
412
|
+
try {
|
|
413
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
414
|
+
method: "POST",
|
|
415
|
+
headers: this.buildHeaders(),
|
|
416
|
+
body: JSON.stringify({ query, max_results: maxResults }),
|
|
417
|
+
signal
|
|
418
|
+
});
|
|
419
|
+
if (!res.ok) throw await this.formatError("Search", res);
|
|
420
|
+
const raw = await res.json();
|
|
421
|
+
return { query, results: normalizeOllamaResults(raw) };
|
|
422
|
+
} catch (error) {
|
|
423
|
+
if (isConnectionRefused(error)) throw connectionRefusedError(this.baseUrl);
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
async fetch(url, _raw, signal) {
|
|
428
|
+
this.requireBaseUrl();
|
|
429
|
+
const path = this.local ? LOCAL_FETCH_PATH : CLOUD_FETCH_PATH;
|
|
430
|
+
try {
|
|
431
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
432
|
+
method: "POST",
|
|
433
|
+
headers: this.buildHeaders(),
|
|
434
|
+
body: JSON.stringify({ url }),
|
|
435
|
+
signal
|
|
436
|
+
});
|
|
437
|
+
if (!res.ok) throw await this.formatError("Fetch", res);
|
|
438
|
+
const data = await res.json();
|
|
439
|
+
if (!data.content) {
|
|
440
|
+
throw new Error(`${this.label} Fetch API error: no content returned for ${url}`);
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
text: data.content,
|
|
444
|
+
title: data.title || void 0,
|
|
445
|
+
contentType: "text/plain"
|
|
446
|
+
};
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (isConnectionRefused(error)) throw connectionRefusedError(this.baseUrl);
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
requireBaseUrl() {
|
|
453
|
+
if (!this.baseUrl) {
|
|
454
|
+
throw new Error(`${OLLAMA_HOST_ENV_VAR} is not set. Run /web-tools to configure, or export the env var.`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
buildHeaders() {
|
|
458
|
+
const headers = { "Content-Type": "application/json" };
|
|
459
|
+
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
460
|
+
return headers;
|
|
461
|
+
}
|
|
462
|
+
async formatError(label, res) {
|
|
463
|
+
const body = await res.text();
|
|
464
|
+
const hint = hintForStatus(res.status);
|
|
465
|
+
return new Error(`${this.label} ${label} API error (${res.status})${hint}: ${body}`);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
function hintForStatus(status) {
|
|
469
|
+
if (status === 401) {
|
|
470
|
+
return " (run `ollama signin` to authenticate)";
|
|
471
|
+
}
|
|
472
|
+
if (status === 404) {
|
|
473
|
+
return " (the Ollama instance may not support web search; ensure you are running a recent version)";
|
|
474
|
+
}
|
|
475
|
+
return "";
|
|
476
|
+
}
|
|
477
|
+
__name(hintForStatus, "hintForStatus");
|
|
478
|
+
function maskKey(key) {
|
|
479
|
+
const head = key.slice(0, MASK_VISIBLE_CHARS);
|
|
480
|
+
const tail = key.slice(-MASK_VISIBLE_CHARS);
|
|
481
|
+
return `${head}...${tail}`;
|
|
482
|
+
}
|
|
483
|
+
__name(maskKey, "maskKey");
|
|
484
|
+
async function promptForBaseUrl(ui, current) {
|
|
485
|
+
const existing = current?.trim();
|
|
486
|
+
const input = await ui.input(
|
|
487
|
+
"Ollama base URL",
|
|
488
|
+
existing ? `Press Enter to keep current (${existing}), or type new URL` : `Press Enter for default (${OLLAMA_DEFAULT_URL}), or type instance URL`
|
|
489
|
+
);
|
|
490
|
+
if (isCancellation(input)) return void 0;
|
|
491
|
+
return input.trim() || existing || OLLAMA_DEFAULT_URL;
|
|
492
|
+
}
|
|
493
|
+
__name(promptForBaseUrl, "promptForBaseUrl");
|
|
494
|
+
async function promptForOptionalKey(ui, current) {
|
|
495
|
+
const existing = current?.trim() || void 0;
|
|
496
|
+
const input = await ui.input(
|
|
497
|
+
"Ollama API key (optional \u2014 for direct cloud access; local Ollama authenticates via `ollama signin`)",
|
|
498
|
+
existing ? `Press Enter to keep current (${maskKey(existing)}), or type new key` : "Press Enter to leave unset, or type a key"
|
|
499
|
+
);
|
|
500
|
+
if (isCancellation(input)) return void 0;
|
|
501
|
+
return input.trim() || existing || null;
|
|
502
|
+
}
|
|
503
|
+
__name(promptForOptionalKey, "promptForOptionalKey");
|
|
504
|
+
async function configureOllama(ui, current) {
|
|
505
|
+
const baseUrl = await promptForBaseUrl(ui, current.baseUrl);
|
|
506
|
+
if (baseUrl === void 0) return null;
|
|
507
|
+
const apiKey = await promptForOptionalKey(ui, current.apiKey);
|
|
508
|
+
if (apiKey === void 0) return null;
|
|
509
|
+
return { baseUrl, apiKey };
|
|
510
|
+
}
|
|
511
|
+
__name(configureOllama, "configureOllama");
|
|
512
|
+
|
|
513
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/perplexity.ts?ext=privateer-web
|
|
514
|
+
var PERPLEXITY_API_URL = "https://api.perplexity.ai/search";
|
|
515
|
+
var PERPLEXITY_API_KEY_ENV_VAR = "PERPLEXITY_API_KEY";
|
|
516
|
+
var PERPLEXITY_PROVIDER_META = {
|
|
517
|
+
name: "perplexity",
|
|
518
|
+
label: "Perplexity",
|
|
519
|
+
envVar: PERPLEXITY_API_KEY_ENV_VAR,
|
|
520
|
+
roles: ["search"]
|
|
521
|
+
};
|
|
522
|
+
function normalizePerplexityResults(results) {
|
|
523
|
+
return results.map((r) => ({
|
|
524
|
+
title: r.title ?? "",
|
|
525
|
+
url: r.url ?? "",
|
|
526
|
+
snippet: r.snippet ?? ""
|
|
527
|
+
}));
|
|
528
|
+
}
|
|
529
|
+
__name(normalizePerplexityResults, "normalizePerplexityResults");
|
|
530
|
+
var PerplexityProvider = class {
|
|
531
|
+
constructor(apiKey) {
|
|
532
|
+
this.apiKey = apiKey;
|
|
533
|
+
}
|
|
534
|
+
apiKey;
|
|
535
|
+
static {
|
|
536
|
+
__name(this, "PerplexityProvider");
|
|
537
|
+
}
|
|
538
|
+
name = PERPLEXITY_PROVIDER_META.name;
|
|
539
|
+
label = PERPLEXITY_PROVIDER_META.label;
|
|
540
|
+
envVar = PERPLEXITY_PROVIDER_META.envVar;
|
|
541
|
+
async search(query, maxResults, signal) {
|
|
542
|
+
if (!this.apiKey) {
|
|
543
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
544
|
+
}
|
|
545
|
+
const res = await fetch(PERPLEXITY_API_URL, {
|
|
546
|
+
method: "POST",
|
|
547
|
+
headers: {
|
|
548
|
+
"Content-Type": "application/json",
|
|
549
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
550
|
+
},
|
|
551
|
+
body: JSON.stringify({
|
|
552
|
+
query,
|
|
553
|
+
max_results: maxResults
|
|
554
|
+
}),
|
|
555
|
+
signal
|
|
556
|
+
});
|
|
557
|
+
if (!res.ok) {
|
|
558
|
+
const text = await res.text();
|
|
559
|
+
throw new Error(`${this.label} Search API error (${res.status}): ${text}`);
|
|
560
|
+
}
|
|
561
|
+
const raw = await res.json();
|
|
562
|
+
return { query, results: normalizePerplexityResults(raw.results ?? []) };
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/searxng.ts?ext=privateer-web
|
|
567
|
+
var SEARXNG_API_KEY_ENV_VAR = "SEARXNG_API_KEY";
|
|
568
|
+
var SEARXNG_URL_ENV_VAR = "SEARXNG_URL";
|
|
569
|
+
var SEARXNG_DEFAULT_URL = "http://localhost:8080";
|
|
570
|
+
var SEARXNG_SEARCH_PATH = "/search";
|
|
571
|
+
var SEARXNG_FORMAT_JSON = "json";
|
|
572
|
+
var SEARXNG_SAFESEARCH_OFF = "0";
|
|
573
|
+
var MASK_VISIBLE_CHARS2 = 4;
|
|
574
|
+
var SEARXNG_PROVIDER_META = {
|
|
575
|
+
name: "searxng",
|
|
576
|
+
label: "SearXNG",
|
|
577
|
+
envVar: SEARXNG_API_KEY_ENV_VAR,
|
|
578
|
+
baseUrlEnvVar: SEARXNG_URL_ENV_VAR,
|
|
579
|
+
defaultBaseUrl: SEARXNG_DEFAULT_URL,
|
|
580
|
+
roles: ["search"],
|
|
581
|
+
configure: /* @__PURE__ */ __name((ui, current) => configureSearxng(ui, current), "configure")
|
|
582
|
+
};
|
|
583
|
+
function normalizeSearxngResults(raw, maxResults) {
|
|
584
|
+
return (raw.results ?? []).slice(0, maxResults).map((r) => ({
|
|
585
|
+
title: r.title ?? "",
|
|
586
|
+
url: r.url ?? "",
|
|
587
|
+
snippet: r.content ?? ""
|
|
588
|
+
}));
|
|
589
|
+
}
|
|
590
|
+
__name(normalizeSearxngResults, "normalizeSearxngResults");
|
|
591
|
+
function stripTrailingSlashes2(url) {
|
|
592
|
+
return url.replace(/\/+$/, "");
|
|
593
|
+
}
|
|
594
|
+
__name(stripTrailingSlashes2, "stripTrailingSlashes");
|
|
595
|
+
function assertHttpUrl2(url) {
|
|
596
|
+
let parsed;
|
|
597
|
+
try {
|
|
598
|
+
parsed = new URL(url);
|
|
599
|
+
} catch {
|
|
600
|
+
throw new Error(`${SEARXNG_URL_ENV_VAR} is not a valid URL (got: ${url})`);
|
|
601
|
+
}
|
|
602
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
603
|
+
throw new Error(
|
|
604
|
+
`${SEARXNG_URL_ENV_VAR} must use http:// or https:// (got: ${parsed.protocol.replace(":", "")}://)`
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
__name(assertHttpUrl2, "assertHttpUrl");
|
|
609
|
+
function hintForSearchStatus(status) {
|
|
610
|
+
if (status === 401) {
|
|
611
|
+
return ` (the SearXNG instance's reverse-proxy rejected the Bearer token; check ${SEARXNG_API_KEY_ENV_VAR} or apiKeys.searxng)`;
|
|
612
|
+
}
|
|
613
|
+
if (status === 403) {
|
|
614
|
+
return " (the SearXNG instance may have JSON output disabled; enable 'json' under 'search.formats' in its settings.yml)";
|
|
615
|
+
}
|
|
616
|
+
return "";
|
|
617
|
+
}
|
|
618
|
+
__name(hintForSearchStatus, "hintForSearchStatus");
|
|
619
|
+
var SearxngProvider = class {
|
|
620
|
+
static {
|
|
621
|
+
__name(this, "SearxngProvider");
|
|
622
|
+
}
|
|
623
|
+
name = "searxng";
|
|
624
|
+
label = "SearXNG";
|
|
625
|
+
envVar = SEARXNG_API_KEY_ENV_VAR;
|
|
626
|
+
apiKey;
|
|
627
|
+
baseUrl;
|
|
628
|
+
constructor(options) {
|
|
629
|
+
this.apiKey = options.apiKey?.trim() || void 0;
|
|
630
|
+
const trimmed = stripTrailingSlashes2(options.baseUrl?.trim() ?? "");
|
|
631
|
+
if (trimmed) assertHttpUrl2(trimmed);
|
|
632
|
+
this.baseUrl = trimmed;
|
|
633
|
+
}
|
|
634
|
+
async search(query, maxResults, signal) {
|
|
635
|
+
this.requireBaseUrl();
|
|
636
|
+
const res = await fetch(this.buildSearchUrl(query), {
|
|
637
|
+
method: "GET",
|
|
638
|
+
headers: this.buildAuthHeaders(),
|
|
639
|
+
signal
|
|
640
|
+
});
|
|
641
|
+
if (!res.ok) throw await this.searchApiError(res);
|
|
642
|
+
const raw = await res.json();
|
|
643
|
+
return { query, results: normalizeSearxngResults(raw, maxResults) };
|
|
644
|
+
}
|
|
645
|
+
requireBaseUrl() {
|
|
646
|
+
if (!this.baseUrl) {
|
|
647
|
+
throw new Error(`${SEARXNG_URL_ENV_VAR} is not set. Run /web-tools to configure, or export the env var.`);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
// The SearXNG API exposes only `pageno` for pagination, not `count`/`limit`
|
|
651
|
+
// (https://docs.searxng.org/dev/search_api.html), so we ask for a single
|
|
652
|
+
// page and slice to maxResults client-side.
|
|
653
|
+
buildSearchUrl(query) {
|
|
654
|
+
const url = new URL(`${this.baseUrl}${SEARXNG_SEARCH_PATH}`);
|
|
655
|
+
url.searchParams.set("q", query);
|
|
656
|
+
url.searchParams.set("format", SEARXNG_FORMAT_JSON);
|
|
657
|
+
url.searchParams.set("safesearch", SEARXNG_SAFESEARCH_OFF);
|
|
658
|
+
return url.toString();
|
|
659
|
+
}
|
|
660
|
+
// SearXNG itself has no native auth; the optional Bearer key is for
|
|
661
|
+
// instances fronted by a reverse-proxy that gates on Authorization.
|
|
662
|
+
buildAuthHeaders() {
|
|
663
|
+
const headers = { Accept: "application/json" };
|
|
664
|
+
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
665
|
+
return headers;
|
|
666
|
+
}
|
|
667
|
+
async searchApiError(res) {
|
|
668
|
+
const body = await res.text();
|
|
669
|
+
return new Error(`${this.label} Search API error (${res.status})${hintForSearchStatus(res.status)}: ${body}`);
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
function maskKey2(key) {
|
|
673
|
+
const head = key.slice(0, MASK_VISIBLE_CHARS2);
|
|
674
|
+
const tail = key.slice(-MASK_VISIBLE_CHARS2);
|
|
675
|
+
return `${head}...${tail}`;
|
|
676
|
+
}
|
|
677
|
+
__name(maskKey2, "maskKey");
|
|
678
|
+
async function promptForBaseUrl2(ui, current) {
|
|
679
|
+
const existing = current?.trim();
|
|
680
|
+
const input = await ui.input(
|
|
681
|
+
"SearXNG base URL",
|
|
682
|
+
existing ? `Press Enter to keep current (${existing}), or type new URL` : `Press Enter for default (${SEARXNG_DEFAULT_URL}), or type instance URL`
|
|
683
|
+
);
|
|
684
|
+
if (isCancellation(input)) return void 0;
|
|
685
|
+
return input.trim() || existing || SEARXNG_DEFAULT_URL;
|
|
686
|
+
}
|
|
687
|
+
__name(promptForBaseUrl2, "promptForBaseUrl");
|
|
688
|
+
async function promptForOptionalKey2(ui, current) {
|
|
689
|
+
const existing = current?.trim() || void 0;
|
|
690
|
+
const input = await ui.input(
|
|
691
|
+
"SearXNG API key (optional \u2014 for instances behind a Bearer-auth proxy)",
|
|
692
|
+
existing ? `Press Enter to keep current (${maskKey2(existing)}), or type new key` : "Press Enter to leave unset, or type a key"
|
|
693
|
+
);
|
|
694
|
+
if (isCancellation(input)) return void 0;
|
|
695
|
+
return input.trim() || existing || null;
|
|
696
|
+
}
|
|
697
|
+
__name(promptForOptionalKey2, "promptForOptionalKey");
|
|
698
|
+
async function configureSearxng(ui, current) {
|
|
699
|
+
const baseUrl = await promptForBaseUrl2(ui, current.baseUrl);
|
|
700
|
+
if (baseUrl === void 0) return null;
|
|
701
|
+
const apiKey = await promptForOptionalKey2(ui, current.apiKey);
|
|
702
|
+
if (apiKey === void 0) return null;
|
|
703
|
+
return { baseUrl, apiKey };
|
|
704
|
+
}
|
|
705
|
+
__name(configureSearxng, "configureSearxng");
|
|
706
|
+
|
|
707
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/serper.ts?ext=privateer-web
|
|
708
|
+
var SERPER_API_URL = "https://google.serper.dev/search";
|
|
709
|
+
var SERPER_API_KEY_ENV_VAR = "SERPER_API_KEY";
|
|
710
|
+
var SERPER_PROVIDER_META = {
|
|
711
|
+
name: "serper",
|
|
712
|
+
label: "Serper",
|
|
713
|
+
envVar: SERPER_API_KEY_ENV_VAR,
|
|
714
|
+
roles: ["search"]
|
|
715
|
+
};
|
|
716
|
+
function normalizeSerperResults(results) {
|
|
717
|
+
return results.map((r) => ({
|
|
718
|
+
title: r.title ?? "",
|
|
719
|
+
url: r.link ?? "",
|
|
720
|
+
snippet: r.snippet ?? ""
|
|
721
|
+
}));
|
|
722
|
+
}
|
|
723
|
+
__name(normalizeSerperResults, "normalizeSerperResults");
|
|
724
|
+
var SerperProvider = class {
|
|
725
|
+
constructor(apiKey) {
|
|
726
|
+
this.apiKey = apiKey;
|
|
727
|
+
}
|
|
728
|
+
apiKey;
|
|
729
|
+
static {
|
|
730
|
+
__name(this, "SerperProvider");
|
|
731
|
+
}
|
|
732
|
+
name = SERPER_PROVIDER_META.name;
|
|
733
|
+
label = SERPER_PROVIDER_META.label;
|
|
734
|
+
envVar = SERPER_PROVIDER_META.envVar;
|
|
735
|
+
async search(query, maxResults, signal) {
|
|
736
|
+
if (!this.apiKey) {
|
|
737
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
738
|
+
}
|
|
739
|
+
const res = await fetch(SERPER_API_URL, {
|
|
740
|
+
method: "POST",
|
|
741
|
+
headers: {
|
|
742
|
+
"Content-Type": "application/json",
|
|
743
|
+
"X-API-KEY": this.apiKey
|
|
744
|
+
},
|
|
745
|
+
body: JSON.stringify({
|
|
746
|
+
q: query,
|
|
747
|
+
num: maxResults
|
|
748
|
+
}),
|
|
749
|
+
signal
|
|
750
|
+
});
|
|
751
|
+
if (!res.ok) {
|
|
752
|
+
const text = await res.text();
|
|
753
|
+
throw new Error(`${this.label} Search API error (${res.status}): ${text}`);
|
|
754
|
+
}
|
|
755
|
+
const raw = await res.json();
|
|
756
|
+
return { query, results: normalizeSerperResults(raw.organic ?? []) };
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/tavily.ts?ext=privateer-web
|
|
761
|
+
var TAVILY_API_URL = "https://api.tavily.com/search";
|
|
762
|
+
var TAVILY_EXTRACT_API_URL = "https://api.tavily.com/extract";
|
|
763
|
+
var TAVILY_API_KEY_ENV_VAR = "TAVILY_API_KEY";
|
|
764
|
+
var TAVILY_PROVIDER_META = {
|
|
765
|
+
name: "tavily",
|
|
766
|
+
label: "Tavily",
|
|
767
|
+
envVar: TAVILY_API_KEY_ENV_VAR,
|
|
768
|
+
roles: ["search", "fetch"]
|
|
769
|
+
};
|
|
770
|
+
function normalizeTavilyResults(results) {
|
|
771
|
+
return results.map((r) => ({
|
|
772
|
+
title: r.title ?? "",
|
|
773
|
+
url: r.url ?? "",
|
|
774
|
+
snippet: r.content ?? ""
|
|
775
|
+
}));
|
|
776
|
+
}
|
|
777
|
+
__name(normalizeTavilyResults, "normalizeTavilyResults");
|
|
778
|
+
var TavilyProvider = class {
|
|
779
|
+
constructor(apiKey) {
|
|
780
|
+
this.apiKey = apiKey;
|
|
781
|
+
}
|
|
782
|
+
apiKey;
|
|
783
|
+
static {
|
|
784
|
+
__name(this, "TavilyProvider");
|
|
785
|
+
}
|
|
786
|
+
name = TAVILY_PROVIDER_META.name;
|
|
787
|
+
label = TAVILY_PROVIDER_META.label;
|
|
788
|
+
envVar = TAVILY_PROVIDER_META.envVar;
|
|
789
|
+
async search(query, maxResults, signal) {
|
|
790
|
+
if (!this.apiKey) {
|
|
791
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
792
|
+
}
|
|
793
|
+
const res = await fetch(TAVILY_API_URL, {
|
|
794
|
+
method: "POST",
|
|
795
|
+
headers: { "Content-Type": "application/json" },
|
|
796
|
+
body: JSON.stringify({
|
|
797
|
+
api_key: this.apiKey,
|
|
798
|
+
query,
|
|
799
|
+
max_results: maxResults
|
|
800
|
+
}),
|
|
801
|
+
signal
|
|
802
|
+
});
|
|
803
|
+
if (!res.ok) {
|
|
804
|
+
const text = await res.text();
|
|
805
|
+
throw new Error(`${this.label} Search API error (${res.status}): ${text}`);
|
|
806
|
+
}
|
|
807
|
+
const raw = await res.json();
|
|
808
|
+
return { query, results: normalizeTavilyResults(raw.results ?? []) };
|
|
809
|
+
}
|
|
810
|
+
async fetch(url, _raw, signal) {
|
|
811
|
+
if (!this.apiKey) {
|
|
812
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
813
|
+
}
|
|
814
|
+
const res = await fetch(TAVILY_EXTRACT_API_URL, {
|
|
815
|
+
method: "POST",
|
|
816
|
+
headers: {
|
|
817
|
+
"Content-Type": "application/json",
|
|
818
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
819
|
+
},
|
|
820
|
+
body: JSON.stringify({
|
|
821
|
+
urls: [url]
|
|
822
|
+
}),
|
|
823
|
+
signal
|
|
824
|
+
});
|
|
825
|
+
if (!res.ok) {
|
|
826
|
+
const text = await res.text();
|
|
827
|
+
throw new Error(`${this.label} Fetch API error (${res.status}): ${text}`);
|
|
828
|
+
}
|
|
829
|
+
const data = await res.json();
|
|
830
|
+
if (data.failed_results && data.failed_results.length > 0) {
|
|
831
|
+
const failed = data.failed_results[0];
|
|
832
|
+
throw new Error(
|
|
833
|
+
`${this.label} Fetch API error: extraction failed for ${failed.url ?? url}: ${failed.error ?? "unknown error"}`
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
const result = data.results?.[0];
|
|
837
|
+
if (!result?.raw_content) {
|
|
838
|
+
throw new Error(`${this.label} Fetch API error: no content returned for ${url}`);
|
|
839
|
+
}
|
|
840
|
+
return {
|
|
841
|
+
text: result.raw_content,
|
|
842
|
+
contentType: "text/plain"
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/youcom.ts?ext=privateer-web
|
|
848
|
+
var YOUCOM_SEARCH_URL = "https://ydc-index.io/v1/search";
|
|
849
|
+
var YOUCOM_CONTENTS_URL = "https://ydc-index.io/v1/contents";
|
|
850
|
+
var YOUCOM_API_KEY_ENV_VAR = "YOUCOM_API_KEY";
|
|
851
|
+
var YOUCOM_PROVIDER_META = {
|
|
852
|
+
name: "youcom",
|
|
853
|
+
label: "You.com",
|
|
854
|
+
envVar: YOUCOM_API_KEY_ENV_VAR,
|
|
855
|
+
roles: ["search", "fetch"]
|
|
856
|
+
};
|
|
857
|
+
function normalizeYouComResults(results) {
|
|
858
|
+
return results.map((r) => ({
|
|
859
|
+
title: r.title ?? "",
|
|
860
|
+
url: r.url ?? "",
|
|
861
|
+
snippet: r.snippets?.[0] ?? r.description ?? ""
|
|
862
|
+
}));
|
|
863
|
+
}
|
|
864
|
+
__name(normalizeYouComResults, "normalizeYouComResults");
|
|
865
|
+
var YouComProvider = class {
|
|
866
|
+
constructor(apiKey) {
|
|
867
|
+
this.apiKey = apiKey;
|
|
868
|
+
}
|
|
869
|
+
apiKey;
|
|
870
|
+
static {
|
|
871
|
+
__name(this, "YouComProvider");
|
|
872
|
+
}
|
|
873
|
+
name = YOUCOM_PROVIDER_META.name;
|
|
874
|
+
label = YOUCOM_PROVIDER_META.label;
|
|
875
|
+
envVar = YOUCOM_API_KEY_ENV_VAR;
|
|
876
|
+
async search(query, maxResults, signal) {
|
|
877
|
+
if (!this.apiKey) {
|
|
878
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
879
|
+
}
|
|
880
|
+
const res = await fetch(YOUCOM_SEARCH_URL, {
|
|
881
|
+
method: "POST",
|
|
882
|
+
headers: {
|
|
883
|
+
"Content-Type": "application/json",
|
|
884
|
+
"X-API-Key": this.apiKey
|
|
885
|
+
},
|
|
886
|
+
body: JSON.stringify({
|
|
887
|
+
query,
|
|
888
|
+
count: maxResults
|
|
889
|
+
}),
|
|
890
|
+
signal
|
|
891
|
+
});
|
|
892
|
+
if (!res.ok) {
|
|
893
|
+
const text = await res.text();
|
|
894
|
+
throw new Error(`${this.label} Search API error (${res.status}): ${text}`);
|
|
895
|
+
}
|
|
896
|
+
const raw = await res.json();
|
|
897
|
+
return { query, results: normalizeYouComResults(raw.results?.web ?? []) };
|
|
898
|
+
}
|
|
899
|
+
async fetch(url, _raw, signal) {
|
|
900
|
+
if (!this.apiKey) {
|
|
901
|
+
throw new Error(`${this.envVar} is not set. Run /web-tools to configure, or export the env var.`);
|
|
902
|
+
}
|
|
903
|
+
const res = await fetch(YOUCOM_CONTENTS_URL, {
|
|
904
|
+
method: "POST",
|
|
905
|
+
headers: {
|
|
906
|
+
"Content-Type": "application/json",
|
|
907
|
+
"X-API-Key": this.apiKey
|
|
908
|
+
},
|
|
909
|
+
body: JSON.stringify({
|
|
910
|
+
urls: [url],
|
|
911
|
+
formats: ["markdown"]
|
|
912
|
+
}),
|
|
913
|
+
signal
|
|
914
|
+
});
|
|
915
|
+
if (!res.ok) {
|
|
916
|
+
const text = await res.text();
|
|
917
|
+
throw new Error(`${this.label} Fetch API error (${res.status}): ${text}`);
|
|
918
|
+
}
|
|
919
|
+
const raw = await res.json();
|
|
920
|
+
const item = raw[0];
|
|
921
|
+
if (!item?.markdown) {
|
|
922
|
+
throw new Error(`${this.label} Fetch API error: no content returned for ${url}`);
|
|
923
|
+
}
|
|
924
|
+
return {
|
|
925
|
+
text: item.markdown,
|
|
926
|
+
title: item.title || void 0,
|
|
927
|
+
contentType: "text/markdown"
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/factory.ts?ext=privateer-web
|
|
933
|
+
function createSearchProvider(name, creds) {
|
|
934
|
+
const apiKey = creds.apiKey ?? "";
|
|
935
|
+
switch (name) {
|
|
936
|
+
case "brave":
|
|
937
|
+
return new BraveProvider(apiKey);
|
|
938
|
+
case "tavily":
|
|
939
|
+
return new TavilyProvider(apiKey);
|
|
940
|
+
case "serper":
|
|
941
|
+
return new SerperProvider(apiKey);
|
|
942
|
+
case "exa":
|
|
943
|
+
return new ExaProvider(apiKey);
|
|
944
|
+
case "youcom":
|
|
945
|
+
return new YouComProvider(apiKey);
|
|
946
|
+
case "jina":
|
|
947
|
+
return new JinaProvider(apiKey);
|
|
948
|
+
case "firecrawl":
|
|
949
|
+
return new FirecrawlProvider(apiKey);
|
|
950
|
+
case "perplexity":
|
|
951
|
+
return new PerplexityProvider(apiKey);
|
|
952
|
+
case "searxng":
|
|
953
|
+
return new SearxngProvider({ apiKey: creds.apiKey, baseUrl: creds.baseUrl ?? "" });
|
|
954
|
+
case "ollama":
|
|
955
|
+
return new OllamaProvider({ apiKey: creds.apiKey, baseUrl: creds.baseUrl ?? "" });
|
|
956
|
+
default:
|
|
957
|
+
throw new Error(`Unknown search provider: "${name}"`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
__name(createSearchProvider, "createSearchProvider");
|
|
961
|
+
|
|
962
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/interceptors/github.ts?ext=privateer-web
|
|
963
|
+
import { execFile } from "node:child_process";
|
|
964
|
+
import {
|
|
965
|
+
closeSync,
|
|
966
|
+
existsSync as existsSync2,
|
|
967
|
+
openSync,
|
|
968
|
+
readdirSync,
|
|
969
|
+
readFileSync as readFileSync2,
|
|
970
|
+
readSync,
|
|
971
|
+
realpathSync,
|
|
972
|
+
rmSync,
|
|
973
|
+
statSync
|
|
974
|
+
} from "node:fs";
|
|
975
|
+
import { tmpdir } from "node:os";
|
|
976
|
+
import { extname, join as join2, sep as pathSep, resolve as resolvePath } from "node:path";
|
|
977
|
+
|
|
978
|
+
// node_modules/@juicesharp/rpiv-config/config.ts?ext=privateer-web
|
|
979
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
980
|
+
import { homedir } from "node:os";
|
|
981
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
982
|
+
function expandTilde(p) {
|
|
983
|
+
if (p === "~") return homedir();
|
|
984
|
+
if (p.startsWith("~/")) return join(homedir(), p.slice(2));
|
|
985
|
+
return p;
|
|
986
|
+
}
|
|
987
|
+
__name(expandTilde, "expandTilde");
|
|
988
|
+
function defaultConfigDir() {
|
|
989
|
+
return join(homedir(), ".config");
|
|
990
|
+
}
|
|
991
|
+
__name(defaultConfigDir, "defaultConfigDir");
|
|
992
|
+
function resolveConfigDir() {
|
|
993
|
+
const xdg = readEnvVar("XDG_CONFIG_HOME");
|
|
994
|
+
if (!xdg) return defaultConfigDir();
|
|
995
|
+
const expanded = expandTilde(xdg);
|
|
996
|
+
return isAbsolute(expanded) ? expanded : defaultConfigDir();
|
|
997
|
+
}
|
|
998
|
+
__name(resolveConfigDir, "resolveConfigDir");
|
|
999
|
+
function legacyConfigPath(name, file = "config.json") {
|
|
1000
|
+
return join(defaultConfigDir(), name, file);
|
|
1001
|
+
}
|
|
1002
|
+
__name(legacyConfigPath, "legacyConfigPath");
|
|
1003
|
+
function configPath(name, file = "config.json") {
|
|
1004
|
+
return join(resolveConfigDir(), name, file);
|
|
1005
|
+
}
|
|
1006
|
+
__name(configPath, "configPath");
|
|
1007
|
+
function loadJsonConfig(path) {
|
|
1008
|
+
if (!existsSync(path)) return {};
|
|
1009
|
+
try {
|
|
1010
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
1011
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
1012
|
+
return parsed;
|
|
1013
|
+
} catch (err) {
|
|
1014
|
+
console.warn(`rpiv-config: invalid JSON at ${path}, using default ({}) \u2014 ${err.message}`);
|
|
1015
|
+
return {};
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
__name(loadJsonConfig, "loadJsonConfig");
|
|
1019
|
+
function loadJsonConfigWithLegacyFallback(name, file = "config.json") {
|
|
1020
|
+
const xdgPath = configPath(name, file);
|
|
1021
|
+
if (existsSync(xdgPath)) {
|
|
1022
|
+
return loadJsonConfig(xdgPath);
|
|
1023
|
+
}
|
|
1024
|
+
return loadJsonConfig(legacyConfigPath(name, file));
|
|
1025
|
+
}
|
|
1026
|
+
__name(loadJsonConfigWithLegacyFallback, "loadJsonConfigWithLegacyFallback");
|
|
1027
|
+
var CONFIG_FILE_MODE = 384;
|
|
1028
|
+
function saveJsonConfig(path, data) {
|
|
1029
|
+
try {
|
|
1030
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
1031
|
+
writeFileSync(path, `${JSON.stringify(data, null, 2)}
|
|
1032
|
+
`, "utf-8");
|
|
1033
|
+
} catch {
|
|
1034
|
+
return false;
|
|
1035
|
+
}
|
|
1036
|
+
try {
|
|
1037
|
+
chmodSync(path, CONFIG_FILE_MODE);
|
|
1038
|
+
} catch {
|
|
1039
|
+
}
|
|
1040
|
+
return true;
|
|
1041
|
+
}
|
|
1042
|
+
__name(saveJsonConfig, "saveJsonConfig");
|
|
1043
|
+
var GuidanceFieldsSchema = typebox_exports.Object(
|
|
1044
|
+
{
|
|
1045
|
+
promptSnippet: typebox_exports.Optional(typebox_exports.String()),
|
|
1046
|
+
promptGuidelines: typebox_exports.Optional(typebox_exports.Array(typebox_exports.String())),
|
|
1047
|
+
description: typebox_exports.Optional(typebox_exports.String())
|
|
1048
|
+
},
|
|
1049
|
+
{ additionalProperties: true }
|
|
1050
|
+
);
|
|
1051
|
+
function validateGuidanceFields(fields) {
|
|
1052
|
+
if (!fields || typeof fields !== "object") return {};
|
|
1053
|
+
const g = fields;
|
|
1054
|
+
const result = {};
|
|
1055
|
+
if (typeof g.promptSnippet === "string" && g.promptSnippet.length > 0) {
|
|
1056
|
+
result.promptSnippet = g.promptSnippet;
|
|
1057
|
+
}
|
|
1058
|
+
if (Array.isArray(g.promptGuidelines) && g.promptGuidelines.length > 0 && g.promptGuidelines.every((s) => typeof s === "string" && s.length > 0)) {
|
|
1059
|
+
result.promptGuidelines = g.promptGuidelines;
|
|
1060
|
+
}
|
|
1061
|
+
if (typeof g.description === "string" && g.description.length > 0) {
|
|
1062
|
+
result.description = g.description;
|
|
1063
|
+
}
|
|
1064
|
+
return result;
|
|
1065
|
+
}
|
|
1066
|
+
__name(validateGuidanceFields, "validateGuidanceFields");
|
|
1067
|
+
function readEnvVar(key, fallback) {
|
|
1068
|
+
return process.env[key]?.trim() || fallback;
|
|
1069
|
+
}
|
|
1070
|
+
__name(readEnvVar, "readEnvVar");
|
|
1071
|
+
|
|
1072
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/config.ts?ext=privateer-web
|
|
1073
|
+
var WebToolsGuidanceSchema = typebox_exports.Object(
|
|
1074
|
+
{
|
|
1075
|
+
web_search: typebox_exports.Optional(GuidanceFieldsSchema),
|
|
1076
|
+
web_fetch: typebox_exports.Optional(GuidanceFieldsSchema)
|
|
1077
|
+
},
|
|
1078
|
+
{ additionalProperties: true }
|
|
1079
|
+
);
|
|
1080
|
+
var GitHubInterceptorOptionsSchema = typebox_exports.Object(
|
|
1081
|
+
{
|
|
1082
|
+
enabled: typebox_exports.Optional(typebox_exports.Boolean()),
|
|
1083
|
+
maxRepoSizeMB: typebox_exports.Optional(typebox_exports.Number()),
|
|
1084
|
+
cloneTimeoutSeconds: typebox_exports.Optional(typebox_exports.Number()),
|
|
1085
|
+
clonePath: typebox_exports.Optional(typebox_exports.String())
|
|
1086
|
+
},
|
|
1087
|
+
{ additionalProperties: true }
|
|
1088
|
+
);
|
|
1089
|
+
var InterceptorsConfigSchema = typebox_exports.Object(
|
|
1090
|
+
{
|
|
1091
|
+
// Boolean shorthand or per-field overrides. `enabled: false` inside the
|
|
1092
|
+
// object form is allowed but redundant — use the top-level `false`.
|
|
1093
|
+
github: typebox_exports.Optional(typebox_exports.Union([typebox_exports.Boolean(), GitHubInterceptorOptionsSchema]))
|
|
1094
|
+
},
|
|
1095
|
+
{ additionalProperties: true }
|
|
1096
|
+
);
|
|
1097
|
+
var WebToolsConfigSchema = typebox_exports.Object(
|
|
1098
|
+
{
|
|
1099
|
+
provider: typebox_exports.Optional(typebox_exports.String()),
|
|
1100
|
+
apiKeys: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.String())),
|
|
1101
|
+
baseUrls: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.String())),
|
|
1102
|
+
// Legacy top-level Brave key. Auto-migrated to `apiKeys.brave` by the
|
|
1103
|
+
// /web-tools save path — kept here for the load+rewrite round-trip.
|
|
1104
|
+
apiKey: typebox_exports.Optional(typebox_exports.String()),
|
|
1105
|
+
guidance: typebox_exports.Optional(WebToolsGuidanceSchema),
|
|
1106
|
+
interceptors: typebox_exports.Optional(InterceptorsConfigSchema)
|
|
1107
|
+
},
|
|
1108
|
+
{ additionalProperties: true }
|
|
1109
|
+
);
|
|
1110
|
+
var CONFIG_PATH = configPath("rpiv-web-tools");
|
|
1111
|
+
function getConfigPath() {
|
|
1112
|
+
return CONFIG_PATH;
|
|
1113
|
+
}
|
|
1114
|
+
__name(getConfigPath, "getConfigPath");
|
|
1115
|
+
var pointerSegments = /* @__PURE__ */ __name((instancePath) => instancePath === "" ? [] : instancePath.split("/").slice(1).map((s) => s.replaceAll("~1", "/").replaceAll("~0", "~")), "pointerSegments");
|
|
1116
|
+
var deleteAtPointer = /* @__PURE__ */ __name((root, segs) => {
|
|
1117
|
+
let parent = root;
|
|
1118
|
+
for (let i = 0; i < segs.length - 1; i++) {
|
|
1119
|
+
const next = parent[segs[i]];
|
|
1120
|
+
if (next === null || typeof next !== "object") return false;
|
|
1121
|
+
if (Array.isArray(next)) {
|
|
1122
|
+
delete parent[segs[i]];
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
parent = next;
|
|
1126
|
+
}
|
|
1127
|
+
const leaf = segs[segs.length - 1];
|
|
1128
|
+
if (!(leaf in parent)) return false;
|
|
1129
|
+
delete parent[leaf];
|
|
1130
|
+
return true;
|
|
1131
|
+
}, "deleteAtPointer");
|
|
1132
|
+
var salvageConfig = /* @__PURE__ */ __name((raw) => {
|
|
1133
|
+
const cfg = structuredClone(raw);
|
|
1134
|
+
for (let pass = 0; pass < 5; pass++) {
|
|
1135
|
+
const errors = [...value_exports.Errors(WebToolsConfigSchema, cfg)];
|
|
1136
|
+
if (errors.length === 0) return cfg;
|
|
1137
|
+
let deleted = false;
|
|
1138
|
+
for (const e of errors) {
|
|
1139
|
+
const segs = pointerSegments(e.instancePath);
|
|
1140
|
+
if (segs.length === 0) return void 0;
|
|
1141
|
+
if (deleteAtPointer(cfg, segs)) deleted = true;
|
|
1142
|
+
}
|
|
1143
|
+
if (!deleted) return void 0;
|
|
1144
|
+
}
|
|
1145
|
+
return value_exports.Check(WebToolsConfigSchema, cfg) ? cfg : void 0;
|
|
1146
|
+
}, "salvageConfig");
|
|
1147
|
+
function readConfig() {
|
|
1148
|
+
const raw = loadJsonConfigWithLegacyFallback("rpiv-web-tools");
|
|
1149
|
+
if (value_exports.Check(WebToolsConfigSchema, raw)) return raw;
|
|
1150
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
1151
|
+
return salvageConfig(raw) ?? {};
|
|
1152
|
+
}
|
|
1153
|
+
__name(readConfig, "readConfig");
|
|
1154
|
+
function writeConfig(c) {
|
|
1155
|
+
return saveJsonConfig(CONFIG_PATH, c);
|
|
1156
|
+
}
|
|
1157
|
+
__name(writeConfig, "writeConfig");
|
|
1158
|
+
|
|
1159
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/interceptors/github.ts?ext=privateer-web
|
|
1160
|
+
var GITHUB_TOKEN_ENV_VAR = "GITHUB_TOKEN";
|
|
1161
|
+
var MAX_TREE_ENTRIES = 200;
|
|
1162
|
+
var MAX_INLINE_FILE_CHARS = 1e5;
|
|
1163
|
+
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1164
|
+
".png",
|
|
1165
|
+
".jpg",
|
|
1166
|
+
".jpeg",
|
|
1167
|
+
".gif",
|
|
1168
|
+
".bmp",
|
|
1169
|
+
".ico",
|
|
1170
|
+
".webp",
|
|
1171
|
+
".svg",
|
|
1172
|
+
".tiff",
|
|
1173
|
+
".tif",
|
|
1174
|
+
".mp3",
|
|
1175
|
+
".mp4",
|
|
1176
|
+
".avi",
|
|
1177
|
+
".mov",
|
|
1178
|
+
".mkv",
|
|
1179
|
+
".flv",
|
|
1180
|
+
".wmv",
|
|
1181
|
+
".wav",
|
|
1182
|
+
".ogg",
|
|
1183
|
+
".webm",
|
|
1184
|
+
".flac",
|
|
1185
|
+
".aac",
|
|
1186
|
+
".zip",
|
|
1187
|
+
".tar",
|
|
1188
|
+
".gz",
|
|
1189
|
+
".bz2",
|
|
1190
|
+
".xz",
|
|
1191
|
+
".7z",
|
|
1192
|
+
".rar",
|
|
1193
|
+
".zst",
|
|
1194
|
+
".exe",
|
|
1195
|
+
".dll",
|
|
1196
|
+
".so",
|
|
1197
|
+
".dylib",
|
|
1198
|
+
".bin",
|
|
1199
|
+
".o",
|
|
1200
|
+
".a",
|
|
1201
|
+
".lib",
|
|
1202
|
+
".woff",
|
|
1203
|
+
".woff2",
|
|
1204
|
+
".ttf",
|
|
1205
|
+
".otf",
|
|
1206
|
+
".eot",
|
|
1207
|
+
".pdf",
|
|
1208
|
+
".doc",
|
|
1209
|
+
".docx",
|
|
1210
|
+
".xls",
|
|
1211
|
+
".xlsx",
|
|
1212
|
+
".ppt",
|
|
1213
|
+
".pptx",
|
|
1214
|
+
".sqlite",
|
|
1215
|
+
".db",
|
|
1216
|
+
".sqlite3",
|
|
1217
|
+
".pyc",
|
|
1218
|
+
".pyo",
|
|
1219
|
+
".class",
|
|
1220
|
+
".jar",
|
|
1221
|
+
".war",
|
|
1222
|
+
".iso",
|
|
1223
|
+
".img",
|
|
1224
|
+
".dmg"
|
|
1225
|
+
]);
|
|
1226
|
+
var NOISE_DIRS = /* @__PURE__ */ new Set([
|
|
1227
|
+
"node_modules",
|
|
1228
|
+
"vendor",
|
|
1229
|
+
".next",
|
|
1230
|
+
"dist",
|
|
1231
|
+
"build",
|
|
1232
|
+
"__pycache__",
|
|
1233
|
+
".venv",
|
|
1234
|
+
"venv",
|
|
1235
|
+
".tox",
|
|
1236
|
+
".mypy_cache",
|
|
1237
|
+
".pytest_cache",
|
|
1238
|
+
"target",
|
|
1239
|
+
".gradle",
|
|
1240
|
+
".idea",
|
|
1241
|
+
".vscode"
|
|
1242
|
+
]);
|
|
1243
|
+
var NON_CODE_SEGMENTS = /* @__PURE__ */ new Set([
|
|
1244
|
+
"issues",
|
|
1245
|
+
"pull",
|
|
1246
|
+
"pulls",
|
|
1247
|
+
"discussions",
|
|
1248
|
+
"releases",
|
|
1249
|
+
"wiki",
|
|
1250
|
+
"actions",
|
|
1251
|
+
"settings",
|
|
1252
|
+
"security",
|
|
1253
|
+
"projects",
|
|
1254
|
+
"graphs",
|
|
1255
|
+
"compare",
|
|
1256
|
+
"commits",
|
|
1257
|
+
"tags",
|
|
1258
|
+
"branches",
|
|
1259
|
+
"stargazers",
|
|
1260
|
+
"watchers",
|
|
1261
|
+
"network",
|
|
1262
|
+
"forks",
|
|
1263
|
+
"milestone",
|
|
1264
|
+
"labels",
|
|
1265
|
+
"packages",
|
|
1266
|
+
"codespaces",
|
|
1267
|
+
"contribute",
|
|
1268
|
+
"community",
|
|
1269
|
+
"sponsors",
|
|
1270
|
+
"invitations",
|
|
1271
|
+
"notifications",
|
|
1272
|
+
"insights"
|
|
1273
|
+
]);
|
|
1274
|
+
function parseGitHubUrl(url) {
|
|
1275
|
+
let parsed;
|
|
1276
|
+
try {
|
|
1277
|
+
parsed = new URL(url);
|
|
1278
|
+
} catch {
|
|
1279
|
+
return null;
|
|
1280
|
+
}
|
|
1281
|
+
const host = parsed.hostname.toLowerCase();
|
|
1282
|
+
if (host !== "github.com" && host !== "www.github.com") return null;
|
|
1283
|
+
const segments = parsed.pathname.split("/").filter(Boolean).map((segment) => {
|
|
1284
|
+
try {
|
|
1285
|
+
return decodeURIComponent(segment);
|
|
1286
|
+
} catch {
|
|
1287
|
+
return segment;
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
if (segments.length < 2) return null;
|
|
1291
|
+
const owner = segments[0];
|
|
1292
|
+
const repo = segments[1].replace(/\.git$/, "");
|
|
1293
|
+
if (NON_CODE_SEGMENTS.has(segments[2]?.toLowerCase())) return null;
|
|
1294
|
+
if (segments.length === 2) {
|
|
1295
|
+
return { owner, repo, refIsFullSha: false, type: "root" };
|
|
1296
|
+
}
|
|
1297
|
+
const action = segments[2];
|
|
1298
|
+
if (action !== "blob" && action !== "tree") return null;
|
|
1299
|
+
if (segments.length < 4) return null;
|
|
1300
|
+
const ref = segments[3];
|
|
1301
|
+
const refIsFullSha = /^[0-9a-f]{40}$/.test(ref);
|
|
1302
|
+
const pathParts = segments.slice(4);
|
|
1303
|
+
const path = pathParts.length > 0 ? pathParts.join("/") : "";
|
|
1304
|
+
return { owner, repo, ref, refIsFullSha, path, type: action };
|
|
1305
|
+
}
|
|
1306
|
+
__name(parseGitHubUrl, "parseGitHubUrl");
|
|
1307
|
+
var DEFAULTS = {
|
|
1308
|
+
enabled: false,
|
|
1309
|
+
maxRepoSizeMB: 350,
|
|
1310
|
+
cloneTimeoutSeconds: 30,
|
|
1311
|
+
clonePath: join2(tmpdir(), "pi-github-repos")
|
|
1312
|
+
};
|
|
1313
|
+
function resolveGitHubOptions(userConfig, consumerDefault) {
|
|
1314
|
+
if (userConfig === false) return { ...DEFAULTS, enabled: false };
|
|
1315
|
+
if (userConfig === true) return { ...DEFAULTS, enabled: true };
|
|
1316
|
+
if (userConfig && typeof userConfig === "object") {
|
|
1317
|
+
const enabled = userConfig.enabled ?? true;
|
|
1318
|
+
return {
|
|
1319
|
+
enabled,
|
|
1320
|
+
maxRepoSizeMB: userConfig.maxRepoSizeMB ?? DEFAULTS.maxRepoSizeMB,
|
|
1321
|
+
cloneTimeoutSeconds: userConfig.cloneTimeoutSeconds ?? DEFAULTS.cloneTimeoutSeconds,
|
|
1322
|
+
clonePath: userConfig.clonePath ?? DEFAULTS.clonePath
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
if (consumerDefault === true) return { ...DEFAULTS, enabled: true };
|
|
1326
|
+
return { ...DEFAULTS };
|
|
1327
|
+
}
|
|
1328
|
+
__name(resolveGitHubOptions, "resolveGitHubOptions");
|
|
1329
|
+
function readUserGitHubConfig() {
|
|
1330
|
+
return readConfig().interceptors?.github;
|
|
1331
|
+
}
|
|
1332
|
+
__name(readUserGitHubConfig, "readUserGitHubConfig");
|
|
1333
|
+
function cacheKey(owner, repo, ref) {
|
|
1334
|
+
return ref ? `${owner}/${repo}@${ref}` : `${owner}/${repo}`;
|
|
1335
|
+
}
|
|
1336
|
+
__name(cacheKey, "cacheKey");
|
|
1337
|
+
function cloneDir(clonePath, owner, repo, ref) {
|
|
1338
|
+
const dirName = ref ? `${repo}@${ref}` : repo;
|
|
1339
|
+
return join2(clonePath, owner, dirName);
|
|
1340
|
+
}
|
|
1341
|
+
__name(cloneDir, "cloneDir");
|
|
1342
|
+
var GitHubInterceptor = class {
|
|
1343
|
+
static {
|
|
1344
|
+
__name(this, "GitHubInterceptor");
|
|
1345
|
+
}
|
|
1346
|
+
name = "github";
|
|
1347
|
+
options;
|
|
1348
|
+
ghAvailable = null;
|
|
1349
|
+
ghHintShown = false;
|
|
1350
|
+
cloneCache;
|
|
1351
|
+
constructor(opts = {}) {
|
|
1352
|
+
const { cloneCache, ...rest } = opts;
|
|
1353
|
+
this.options = resolveGitHubOptions(rest, void 0);
|
|
1354
|
+
this.cloneCache = cloneCache ?? /* @__PURE__ */ new Map();
|
|
1355
|
+
}
|
|
1356
|
+
get resolvedOptions() {
|
|
1357
|
+
return this.options;
|
|
1358
|
+
}
|
|
1359
|
+
async intercept(url, opts) {
|
|
1360
|
+
return this.fetchGitHub(url, opts.signal, opts.forceClone);
|
|
1361
|
+
}
|
|
1362
|
+
/** @internal — exposed for direct unit testing of the gh-availability probe. */
|
|
1363
|
+
async checkGhAvailable() {
|
|
1364
|
+
if (this.ghAvailable !== null) return this.ghAvailable;
|
|
1365
|
+
return new Promise((resolve) => {
|
|
1366
|
+
execFile("gh", ["--version"], { timeout: 5e3 }, (err) => {
|
|
1367
|
+
this.ghAvailable = !err;
|
|
1368
|
+
resolve(this.ghAvailable);
|
|
1369
|
+
});
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
/** @internal — exposed for direct unit testing of the repo-size probe. */
|
|
1373
|
+
async checkRepoSize(owner, repo) {
|
|
1374
|
+
if (!await this.checkGhAvailable()) return null;
|
|
1375
|
+
return new Promise((resolve) => {
|
|
1376
|
+
execFile("gh", ["api", `repos/${owner}/${repo}`, "--jq", ".size"], { timeout: 1e4 }, (err, stdout) => {
|
|
1377
|
+
if (err) {
|
|
1378
|
+
resolve(null);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
const kb = parseInt(stdout.trim(), 10);
|
|
1382
|
+
resolve(Number.isNaN(kb) ? null : kb);
|
|
1383
|
+
});
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Injects an entry into the clone cache — for testing generateCloneContent
|
|
1388
|
+
* without running a real git clone. Never call in production.
|
|
1389
|
+
* @internal
|
|
1390
|
+
*/
|
|
1391
|
+
_seedCache(key, entry) {
|
|
1392
|
+
this.cloneCache.set(key, entry);
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Clears the in-memory clone cache and removes all cloned directories.
|
|
1396
|
+
* Also resets the gh-availability probe so the next intercept re-checks.
|
|
1397
|
+
* @internal
|
|
1398
|
+
*/
|
|
1399
|
+
reset() {
|
|
1400
|
+
for (const entry of this.cloneCache.values()) {
|
|
1401
|
+
try {
|
|
1402
|
+
rmSync(entry.localPath, { recursive: true, force: true });
|
|
1403
|
+
} catch {
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
this.cloneCache.clear();
|
|
1407
|
+
this.ghAvailable = null;
|
|
1408
|
+
this.ghHintShown = false;
|
|
1409
|
+
}
|
|
1410
|
+
showGhHint() {
|
|
1411
|
+
if (!this.ghHintShown) {
|
|
1412
|
+
this.ghHintShown = true;
|
|
1413
|
+
console.error("[rpiv-web-tools] Install `gh` CLI for better GitHub repo access including private repos.");
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
async getDefaultBranch(owner, repo) {
|
|
1417
|
+
if (!await this.checkGhAvailable()) return null;
|
|
1418
|
+
return new Promise((resolve) => {
|
|
1419
|
+
execFile(
|
|
1420
|
+
"gh",
|
|
1421
|
+
["api", `repos/${owner}/${repo}`, "--jq", ".default_branch"],
|
|
1422
|
+
{ timeout: 1e4 },
|
|
1423
|
+
(err, stdout) => {
|
|
1424
|
+
if (err) {
|
|
1425
|
+
resolve(null);
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
resolve(stdout.trim() || null);
|
|
1429
|
+
}
|
|
1430
|
+
);
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
async fetchTreeViaApi(owner, repo, ref) {
|
|
1434
|
+
if (!await this.checkGhAvailable()) return null;
|
|
1435
|
+
return new Promise((resolve) => {
|
|
1436
|
+
execFile(
|
|
1437
|
+
"gh",
|
|
1438
|
+
["api", `repos/${owner}/${repo}/git/trees/${ref}?recursive=1`, "--jq", ".tree[].path"],
|
|
1439
|
+
{ timeout: 15e3, maxBuffer: 5 * 1024 * 1024 },
|
|
1440
|
+
(err, stdout) => {
|
|
1441
|
+
if (err) {
|
|
1442
|
+
resolve(null);
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const paths = stdout.trim().split("\n").filter(Boolean);
|
|
1446
|
+
if (paths.length === 0) {
|
|
1447
|
+
resolve(null);
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
const truncated = paths.length > MAX_TREE_ENTRIES;
|
|
1451
|
+
const display = paths.slice(0, MAX_TREE_ENTRIES).join("\n");
|
|
1452
|
+
resolve(truncated ? `${display}
|
|
1453
|
+
... (${paths.length} total entries)` : display);
|
|
1454
|
+
}
|
|
1455
|
+
);
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
async fetchReadmeViaApi(owner, repo, ref) {
|
|
1459
|
+
if (!await this.checkGhAvailable()) return null;
|
|
1460
|
+
return new Promise((resolve) => {
|
|
1461
|
+
execFile(
|
|
1462
|
+
"gh",
|
|
1463
|
+
["api", `repos/${owner}/${repo}/readme?ref=${ref}`, "--jq", ".content"],
|
|
1464
|
+
{ timeout: 1e4 },
|
|
1465
|
+
(err, stdout) => {
|
|
1466
|
+
if (err) {
|
|
1467
|
+
resolve(null);
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
try {
|
|
1471
|
+
const decoded = Buffer.from(stdout.trim(), "base64").toString("utf-8");
|
|
1472
|
+
resolve(
|
|
1473
|
+
decoded.length > 8192 ? `${decoded.slice(0, 8192)}
|
|
1474
|
+
|
|
1475
|
+
[README truncated at 8K chars]` : decoded
|
|
1476
|
+
);
|
|
1477
|
+
} catch {
|
|
1478
|
+
resolve(null);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
);
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
async fetchFileViaApi(owner, repo, path, ref) {
|
|
1485
|
+
if (!await this.checkGhAvailable()) return null;
|
|
1486
|
+
return new Promise((resolve) => {
|
|
1487
|
+
execFile(
|
|
1488
|
+
"gh",
|
|
1489
|
+
["api", `repos/${owner}/${repo}/contents/${path}?ref=${ref}`, "--jq", ".content"],
|
|
1490
|
+
{ timeout: 1e4, maxBuffer: 2 * 1024 * 1024 },
|
|
1491
|
+
(err, stdout) => {
|
|
1492
|
+
if (err) {
|
|
1493
|
+
resolve(null);
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
try {
|
|
1497
|
+
resolve(Buffer.from(stdout.trim(), "base64").toString("utf-8"));
|
|
1498
|
+
} catch {
|
|
1499
|
+
resolve(null);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
);
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
async fetchViaApi(_url, owner, repo, info, sizeNote) {
|
|
1506
|
+
const ref = info.ref || await this.getDefaultBranch(owner, repo);
|
|
1507
|
+
if (!ref) return null;
|
|
1508
|
+
const lines = [];
|
|
1509
|
+
if (sizeNote) {
|
|
1510
|
+
lines.push(sizeNote);
|
|
1511
|
+
lines.push("");
|
|
1512
|
+
}
|
|
1513
|
+
if (info.type === "blob" && info.path) {
|
|
1514
|
+
const content = await this.fetchFileViaApi(owner, repo, info.path, ref);
|
|
1515
|
+
if (!content) return null;
|
|
1516
|
+
lines.push(`## ${info.path}`);
|
|
1517
|
+
if (content.length > MAX_INLINE_FILE_CHARS) {
|
|
1518
|
+
lines.push(content.slice(0, MAX_INLINE_FILE_CHARS));
|
|
1519
|
+
lines.push("\n[File truncated at 100K chars]");
|
|
1520
|
+
} else {
|
|
1521
|
+
lines.push(content);
|
|
1522
|
+
}
|
|
1523
|
+
const title2 = `${owner}/${repo} - ${info.path}`;
|
|
1524
|
+
return { text: lines.join("\n"), title: title2, contentType: "text/plain" };
|
|
1525
|
+
}
|
|
1526
|
+
const [tree, readme] = await Promise.all([
|
|
1527
|
+
this.fetchTreeViaApi(owner, repo, ref),
|
|
1528
|
+
this.fetchReadmeViaApi(owner, repo, ref)
|
|
1529
|
+
]);
|
|
1530
|
+
if (!tree && !readme) return null;
|
|
1531
|
+
if (tree) {
|
|
1532
|
+
lines.push("## Structure");
|
|
1533
|
+
lines.push(tree);
|
|
1534
|
+
lines.push("");
|
|
1535
|
+
}
|
|
1536
|
+
if (readme) {
|
|
1537
|
+
lines.push("## README.md");
|
|
1538
|
+
lines.push(readme);
|
|
1539
|
+
lines.push("");
|
|
1540
|
+
}
|
|
1541
|
+
lines.push("This is an API-only view. Clone the repo or use `read`/`bash` for deeper exploration.");
|
|
1542
|
+
const title = info.path ? `${owner}/${repo} - ${info.path}` : `${owner}/${repo}`;
|
|
1543
|
+
return { text: lines.join("\n"), title, contentType: "text/plain" };
|
|
1544
|
+
}
|
|
1545
|
+
execClone(args, localPath, timeoutMs, signal) {
|
|
1546
|
+
return new Promise((resolve) => {
|
|
1547
|
+
const child = execFile(args[0], args.slice(1), { timeout: timeoutMs }, (err) => {
|
|
1548
|
+
if (err) {
|
|
1549
|
+
try {
|
|
1550
|
+
rmSync(localPath, { recursive: true, force: true });
|
|
1551
|
+
} catch {
|
|
1552
|
+
}
|
|
1553
|
+
resolve(null);
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
resolve(localPath);
|
|
1557
|
+
});
|
|
1558
|
+
if (signal) {
|
|
1559
|
+
const onAbort = /* @__PURE__ */ __name(() => child.kill(), "onAbort");
|
|
1560
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1561
|
+
child.on("exit", () => signal.removeEventListener("abort", onAbort));
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
async cloneRepo(owner, repo, ref, signal) {
|
|
1566
|
+
const localPath = cloneDir(this.options.clonePath, owner, repo, ref);
|
|
1567
|
+
try {
|
|
1568
|
+
rmSync(localPath, { recursive: true, force: true });
|
|
1569
|
+
} catch {
|
|
1570
|
+
}
|
|
1571
|
+
const timeoutMs = this.options.cloneTimeoutSeconds * 1e3;
|
|
1572
|
+
const hasGh = await this.checkGhAvailable();
|
|
1573
|
+
if (hasGh) {
|
|
1574
|
+
const args2 = ["gh", "repo", "clone", `${owner}/${repo}`, localPath, "--", "--depth", "1", "--single-branch"];
|
|
1575
|
+
if (ref) args2.push("--branch", ref);
|
|
1576
|
+
return this.execClone(args2, localPath, timeoutMs, signal);
|
|
1577
|
+
}
|
|
1578
|
+
this.showGhHint();
|
|
1579
|
+
const gitUrl = `https://github.com/${owner}/${repo}.git`;
|
|
1580
|
+
const args = ["git", "clone", "--depth", "1", "--single-branch"];
|
|
1581
|
+
if (ref) args.push("--branch", ref);
|
|
1582
|
+
args.push(gitUrl, localPath);
|
|
1583
|
+
return this.execClone(args, localPath, timeoutMs, signal);
|
|
1584
|
+
}
|
|
1585
|
+
async awaitCachedClone(cached, url, owner, repo, info, signal) {
|
|
1586
|
+
if (signal?.aborted) return null;
|
|
1587
|
+
const result = await cached.clonePromise;
|
|
1588
|
+
if (signal?.aborted) return null;
|
|
1589
|
+
if (result) {
|
|
1590
|
+
const text = generateCloneContent(result, info);
|
|
1591
|
+
const title = info.path ? `${owner}/${repo} - ${info.path}` : `${owner}/${repo}`;
|
|
1592
|
+
return { text, title, contentType: "text/plain" };
|
|
1593
|
+
}
|
|
1594
|
+
return this.fetchViaApi(url, owner, repo, info);
|
|
1595
|
+
}
|
|
1596
|
+
async fetchGitHub(url, signal, forceClone) {
|
|
1597
|
+
const info = parseGitHubUrl(url);
|
|
1598
|
+
if (!info) return null;
|
|
1599
|
+
if (signal?.aborted) return null;
|
|
1600
|
+
if (!this.options.enabled) return null;
|
|
1601
|
+
const { owner, repo } = info;
|
|
1602
|
+
const key = cacheKey(owner, repo, info.ref);
|
|
1603
|
+
const cached = this.cloneCache.get(key);
|
|
1604
|
+
if (cached) return this.awaitCachedClone(cached, url, owner, repo, info, signal);
|
|
1605
|
+
if (info.refIsFullSha) {
|
|
1606
|
+
if (signal?.aborted) return null;
|
|
1607
|
+
const sizeNote = "Note: Commit SHA URLs use the GitHub API instead of cloning.";
|
|
1608
|
+
return this.fetchViaApi(url, owner, repo, info, sizeNote);
|
|
1609
|
+
}
|
|
1610
|
+
if (!forceClone) {
|
|
1611
|
+
const sizeKB = await this.checkRepoSize(owner, repo);
|
|
1612
|
+
if (signal?.aborted) return null;
|
|
1613
|
+
if (sizeKB !== null) {
|
|
1614
|
+
const sizeMB = sizeKB / 1024;
|
|
1615
|
+
if (sizeMB > this.options.maxRepoSizeMB) {
|
|
1616
|
+
if (signal?.aborted) return null;
|
|
1617
|
+
const sizeNote = `Note: Repository is ${Math.round(sizeMB)}MB (threshold: ${this.options.maxRepoSizeMB}MB). Showing API-fetched content instead of full clone. Ask the user if they'd like to clone the full repo \u2014 if yes, call web_fetch again with the same URL.`;
|
|
1618
|
+
const apiView = await this.fetchViaApi(url, owner, repo, info, sizeNote);
|
|
1619
|
+
if (apiView) return apiView;
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
if (signal?.aborted) return null;
|
|
1625
|
+
const cachedAfterCheck = this.cloneCache.get(key);
|
|
1626
|
+
if (cachedAfterCheck) return this.awaitCachedClone(cachedAfterCheck, url, owner, repo, info, signal);
|
|
1627
|
+
const clonePromise = this.cloneRepo(owner, repo, info.ref, signal);
|
|
1628
|
+
const localPath = cloneDir(this.options.clonePath, owner, repo, info.ref);
|
|
1629
|
+
this.cloneCache.set(key, { localPath, clonePromise });
|
|
1630
|
+
const result = await clonePromise;
|
|
1631
|
+
if (signal?.aborted) {
|
|
1632
|
+
if (!result) this.cloneCache.delete(key);
|
|
1633
|
+
return null;
|
|
1634
|
+
}
|
|
1635
|
+
if (!result) {
|
|
1636
|
+
this.cloneCache.delete(key);
|
|
1637
|
+
if (signal?.aborted) return null;
|
|
1638
|
+
return this.fetchViaApi(url, owner, repo, info);
|
|
1639
|
+
}
|
|
1640
|
+
const text = generateCloneContent(result, info);
|
|
1641
|
+
const title = info.path ? `${owner}/${repo} - ${info.path}` : `${owner}/${repo}`;
|
|
1642
|
+
return { text, title, contentType: "text/plain" };
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
function isBinaryFile(filePath) {
|
|
1646
|
+
const ext = extname(filePath).toLowerCase();
|
|
1647
|
+
if (BINARY_EXTENSIONS.has(ext)) return true;
|
|
1648
|
+
let fd;
|
|
1649
|
+
try {
|
|
1650
|
+
fd = openSync(filePath, "r");
|
|
1651
|
+
} catch {
|
|
1652
|
+
return false;
|
|
1653
|
+
}
|
|
1654
|
+
try {
|
|
1655
|
+
const buf = Buffer.alloc(512);
|
|
1656
|
+
const bytesRead = readSync(fd, buf, 0, 512, 0);
|
|
1657
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
1658
|
+
if (buf[i] === 0) return true;
|
|
1659
|
+
}
|
|
1660
|
+
} catch {
|
|
1661
|
+
return false;
|
|
1662
|
+
} finally {
|
|
1663
|
+
closeSync(fd);
|
|
1664
|
+
}
|
|
1665
|
+
return false;
|
|
1666
|
+
}
|
|
1667
|
+
__name(isBinaryFile, "isBinaryFile");
|
|
1668
|
+
function formatFileSize(bytes) {
|
|
1669
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1670
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1671
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1672
|
+
}
|
|
1673
|
+
__name(formatFileSize, "formatFileSize");
|
|
1674
|
+
function resolveWithinRepo(rootPath, relativePath) {
|
|
1675
|
+
const normalizedRoot = resolvePath(rootPath);
|
|
1676
|
+
const candidate = resolvePath(normalizedRoot, relativePath);
|
|
1677
|
+
if (candidate !== normalizedRoot) {
|
|
1678
|
+
const rootPrefix = normalizedRoot.endsWith(pathSep) ? normalizedRoot : normalizedRoot + pathSep;
|
|
1679
|
+
if (!candidate.startsWith(rootPrefix)) return null;
|
|
1680
|
+
}
|
|
1681
|
+
if (!existsSync2(candidate)) return candidate;
|
|
1682
|
+
try {
|
|
1683
|
+
const realRoot = realpathSync(normalizedRoot);
|
|
1684
|
+
const realCandidate = realpathSync(candidate);
|
|
1685
|
+
if (realCandidate === realRoot) return candidate;
|
|
1686
|
+
const realRootPrefix = realRoot.endsWith(pathSep) ? realRoot : realRoot + pathSep;
|
|
1687
|
+
return realCandidate.startsWith(realRootPrefix) ? candidate : null;
|
|
1688
|
+
} catch {
|
|
1689
|
+
return null;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
__name(resolveWithinRepo, "resolveWithinRepo");
|
|
1693
|
+
function buildTree(rootPath) {
|
|
1694
|
+
const entries = [];
|
|
1695
|
+
function walk(dir, relPath) {
|
|
1696
|
+
if (entries.length >= MAX_TREE_ENTRIES) return;
|
|
1697
|
+
let items;
|
|
1698
|
+
try {
|
|
1699
|
+
items = readdirSync(dir).sort();
|
|
1700
|
+
} catch {
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
for (const item of items) {
|
|
1704
|
+
if (entries.length >= MAX_TREE_ENTRIES) return;
|
|
1705
|
+
if (item === ".git") continue;
|
|
1706
|
+
const rel = relPath ? `${relPath}/${item}` : item;
|
|
1707
|
+
const safePath = resolveWithinRepo(rootPath, rel);
|
|
1708
|
+
if (!safePath) {
|
|
1709
|
+
entries.push(`${rel} [outside repo skipped]`);
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
let stat;
|
|
1713
|
+
try {
|
|
1714
|
+
stat = statSync(safePath);
|
|
1715
|
+
} catch {
|
|
1716
|
+
continue;
|
|
1717
|
+
}
|
|
1718
|
+
if (stat.isDirectory()) {
|
|
1719
|
+
if (NOISE_DIRS.has(item)) {
|
|
1720
|
+
entries.push(`${rel}/ [skipped]`);
|
|
1721
|
+
continue;
|
|
1722
|
+
}
|
|
1723
|
+
entries.push(`${rel}/`);
|
|
1724
|
+
walk(safePath, rel);
|
|
1725
|
+
} else {
|
|
1726
|
+
entries.push(rel);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
__name(walk, "walk");
|
|
1731
|
+
walk(rootPath, "");
|
|
1732
|
+
if (entries.length >= MAX_TREE_ENTRIES) {
|
|
1733
|
+
entries.push(`... (truncated at ${MAX_TREE_ENTRIES} entries)`);
|
|
1734
|
+
}
|
|
1735
|
+
return entries.join("\n");
|
|
1736
|
+
}
|
|
1737
|
+
__name(buildTree, "buildTree");
|
|
1738
|
+
function buildDirListing(rootPath, subPath) {
|
|
1739
|
+
const targetPath = resolveWithinRepo(rootPath, subPath);
|
|
1740
|
+
if (!targetPath) return "(path escapes repository root)";
|
|
1741
|
+
const lines = [];
|
|
1742
|
+
let items;
|
|
1743
|
+
try {
|
|
1744
|
+
items = readdirSync(targetPath).sort();
|
|
1745
|
+
} catch {
|
|
1746
|
+
return "(directory not readable)";
|
|
1747
|
+
}
|
|
1748
|
+
for (const item of items) {
|
|
1749
|
+
if (item === ".git") continue;
|
|
1750
|
+
const rel = subPath ? `${subPath}/${item}` : item;
|
|
1751
|
+
const safePath = resolveWithinRepo(rootPath, rel);
|
|
1752
|
+
if (!safePath) {
|
|
1753
|
+
lines.push(` ${item} (outside repo)`);
|
|
1754
|
+
continue;
|
|
1755
|
+
}
|
|
1756
|
+
try {
|
|
1757
|
+
const stat = statSync(safePath);
|
|
1758
|
+
lines.push(stat.isDirectory() ? ` ${item}/` : ` ${item} (${formatFileSize(stat.size)})`);
|
|
1759
|
+
} catch {
|
|
1760
|
+
lines.push(` ${item} (unreadable)`);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
return lines.join("\n");
|
|
1764
|
+
}
|
|
1765
|
+
__name(buildDirListing, "buildDirListing");
|
|
1766
|
+
function readReadme(localPath) {
|
|
1767
|
+
const candidates = ["README.md", "readme.md", "README", "README.txt", "README.rst"];
|
|
1768
|
+
for (const name of candidates) {
|
|
1769
|
+
const readmePath = join2(localPath, name);
|
|
1770
|
+
if (existsSync2(readmePath)) {
|
|
1771
|
+
try {
|
|
1772
|
+
const content = readFileSync2(readmePath, "utf-8");
|
|
1773
|
+
return content.length > 8192 ? `${content.slice(0, 8192)}
|
|
1774
|
+
|
|
1775
|
+
[README truncated at 8K chars]` : content;
|
|
1776
|
+
} catch {
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
return null;
|
|
1781
|
+
}
|
|
1782
|
+
__name(readReadme, "readReadme");
|
|
1783
|
+
function generateCloneContent(localPath, info) {
|
|
1784
|
+
const lines = [];
|
|
1785
|
+
lines.push(`Repository cloned to: ${localPath}`);
|
|
1786
|
+
lines.push("");
|
|
1787
|
+
if (info.type === "root") {
|
|
1788
|
+
lines.push("## Structure");
|
|
1789
|
+
lines.push(buildTree(localPath));
|
|
1790
|
+
lines.push("");
|
|
1791
|
+
const readme = readReadme(localPath);
|
|
1792
|
+
if (readme) {
|
|
1793
|
+
lines.push("## README.md");
|
|
1794
|
+
lines.push(readme);
|
|
1795
|
+
lines.push("");
|
|
1796
|
+
}
|
|
1797
|
+
lines.push("Use `read` and `bash` tools at the path above to explore further.");
|
|
1798
|
+
return lines.join("\n");
|
|
1799
|
+
}
|
|
1800
|
+
if (info.type === "tree") {
|
|
1801
|
+
const dirPath = info.path || "";
|
|
1802
|
+
const fullDirPath = resolveWithinRepo(localPath, dirPath);
|
|
1803
|
+
if (!fullDirPath || !existsSync2(fullDirPath)) {
|
|
1804
|
+
lines.push(`Path \`${dirPath}\` not found in clone. Showing repository root instead.`);
|
|
1805
|
+
lines.push("");
|
|
1806
|
+
lines.push("## Structure");
|
|
1807
|
+
lines.push(buildTree(localPath));
|
|
1808
|
+
} else {
|
|
1809
|
+
lines.push(`## ${dirPath || "/"}`);
|
|
1810
|
+
lines.push(buildDirListing(localPath, dirPath));
|
|
1811
|
+
}
|
|
1812
|
+
lines.push("");
|
|
1813
|
+
lines.push("Use `read` and `bash` tools at the path above to explore further.");
|
|
1814
|
+
return lines.join("\n");
|
|
1815
|
+
}
|
|
1816
|
+
const filePath = info.path || "";
|
|
1817
|
+
const fullFilePath = resolveWithinRepo(localPath, filePath);
|
|
1818
|
+
if (!fullFilePath || !existsSync2(fullFilePath)) {
|
|
1819
|
+
lines.push(`Path \`${filePath}\` not found in clone. Showing repository root instead.`);
|
|
1820
|
+
lines.push("");
|
|
1821
|
+
lines.push("## Structure");
|
|
1822
|
+
lines.push(buildTree(localPath));
|
|
1823
|
+
lines.push("");
|
|
1824
|
+
lines.push("Use `read` and `bash` tools at the path above to explore further.");
|
|
1825
|
+
return lines.join("\n");
|
|
1826
|
+
}
|
|
1827
|
+
let stat;
|
|
1828
|
+
try {
|
|
1829
|
+
stat = statSync(fullFilePath);
|
|
1830
|
+
} catch (err) {
|
|
1831
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1832
|
+
lines.push(`Could not inspect \`${filePath}\`: ${message}`);
|
|
1833
|
+
lines.push("");
|
|
1834
|
+
lines.push("Use `read` and `bash` tools at the path above to explore further.");
|
|
1835
|
+
return lines.join("\n");
|
|
1836
|
+
}
|
|
1837
|
+
if (stat.isDirectory()) {
|
|
1838
|
+
lines.push(`## ${filePath || "/"}`);
|
|
1839
|
+
lines.push(buildDirListing(localPath, filePath));
|
|
1840
|
+
lines.push("");
|
|
1841
|
+
lines.push("Use `read` and `bash` tools at the path above to explore further.");
|
|
1842
|
+
return lines.join("\n");
|
|
1843
|
+
}
|
|
1844
|
+
if (isBinaryFile(fullFilePath)) {
|
|
1845
|
+
const ext = extname(filePath).replace(".", "");
|
|
1846
|
+
lines.push(`## ${filePath}`);
|
|
1847
|
+
lines.push(
|
|
1848
|
+
`Binary file (${ext}, ${formatFileSize(stat.size)}). Use \`read\` or \`bash\` tools at the path above to inspect.`
|
|
1849
|
+
);
|
|
1850
|
+
return lines.join("\n");
|
|
1851
|
+
}
|
|
1852
|
+
let content;
|
|
1853
|
+
try {
|
|
1854
|
+
content = readFileSync2(fullFilePath, "utf-8");
|
|
1855
|
+
} catch {
|
|
1856
|
+
lines.push(`Could not read \`${filePath}\` as UTF-8 text.`);
|
|
1857
|
+
lines.push("");
|
|
1858
|
+
lines.push("Use `read` and `bash` tools at the path above to explore further.");
|
|
1859
|
+
return lines.join("\n");
|
|
1860
|
+
}
|
|
1861
|
+
lines.push(`## ${filePath}`);
|
|
1862
|
+
if (content.length > MAX_INLINE_FILE_CHARS) {
|
|
1863
|
+
lines.push(content.slice(0, MAX_INLINE_FILE_CHARS));
|
|
1864
|
+
lines.push(`
|
|
1865
|
+
[File truncated at 100K chars. Full file: ${fullFilePath}]`);
|
|
1866
|
+
} else {
|
|
1867
|
+
lines.push(content);
|
|
1868
|
+
}
|
|
1869
|
+
lines.push("");
|
|
1870
|
+
lines.push("Use `read` and `bash` tools at the path above to explore further.");
|
|
1871
|
+
return lines.join("\n");
|
|
1872
|
+
}
|
|
1873
|
+
__name(generateCloneContent, "generateCloneContent");
|
|
1874
|
+
|
|
1875
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/interceptors/index.ts?ext=privateer-web
|
|
1876
|
+
var activeInterceptors = [];
|
|
1877
|
+
var activeGitHubInterceptor = null;
|
|
1878
|
+
function buildInterceptors(consumer) {
|
|
1879
|
+
const userCfg = readUserGitHubConfig();
|
|
1880
|
+
const resolved = resolveGitHubOptions(userCfg, consumer?.github);
|
|
1881
|
+
if (resolved.enabled) {
|
|
1882
|
+
activeGitHubInterceptor = new GitHubInterceptor(resolved);
|
|
1883
|
+
activeInterceptors = [activeGitHubInterceptor];
|
|
1884
|
+
} else {
|
|
1885
|
+
activeGitHubInterceptor = null;
|
|
1886
|
+
activeInterceptors = [];
|
|
1887
|
+
}
|
|
1888
|
+
return activeInterceptors;
|
|
1889
|
+
}
|
|
1890
|
+
__name(buildInterceptors, "buildInterceptors");
|
|
1891
|
+
function getInterceptors() {
|
|
1892
|
+
return activeInterceptors;
|
|
1893
|
+
}
|
|
1894
|
+
__name(getInterceptors, "getInterceptors");
|
|
1895
|
+
function getActiveGitHubInterceptor() {
|
|
1896
|
+
return activeGitHubInterceptor;
|
|
1897
|
+
}
|
|
1898
|
+
__name(getActiveGitHubInterceptor, "getActiveGitHubInterceptor");
|
|
1899
|
+
function clearCloneCache() {
|
|
1900
|
+
activeGitHubInterceptor?.reset();
|
|
1901
|
+
}
|
|
1902
|
+
__name(clearCloneCache, "clearCloneCache");
|
|
1903
|
+
|
|
1904
|
+
// node_modules/@juicesharp/rpiv-web-tools/providers/index.ts?ext=privateer-web
|
|
1905
|
+
var PROVIDERS = [
|
|
1906
|
+
BRAVE_PROVIDER_META,
|
|
1907
|
+
TAVILY_PROVIDER_META,
|
|
1908
|
+
SERPER_PROVIDER_META,
|
|
1909
|
+
EXA_PROVIDER_META,
|
|
1910
|
+
YOUCOM_PROVIDER_META,
|
|
1911
|
+
JINA_PROVIDER_META,
|
|
1912
|
+
FIRECRAWL_PROVIDER_META,
|
|
1913
|
+
PERPLEXITY_PROVIDER_META,
|
|
1914
|
+
SEARXNG_PROVIDER_META,
|
|
1915
|
+
OLLAMA_PROVIDER_META
|
|
1916
|
+
];
|
|
1917
|
+
|
|
1918
|
+
export {
|
|
1919
|
+
validateGuidanceFields,
|
|
1920
|
+
getConfigPath,
|
|
1921
|
+
readConfig,
|
|
1922
|
+
writeConfig,
|
|
1923
|
+
GITHUB_TOKEN_ENV_VAR,
|
|
1924
|
+
parseGitHubUrl,
|
|
1925
|
+
resolveGitHubOptions,
|
|
1926
|
+
GitHubInterceptor,
|
|
1927
|
+
buildInterceptors,
|
|
1928
|
+
getInterceptors,
|
|
1929
|
+
getActiveGitHubInterceptor,
|
|
1930
|
+
clearCloneCache,
|
|
1931
|
+
BRAVE_API_KEY_ENV_VAR,
|
|
1932
|
+
BRAVE_PROVIDER_META,
|
|
1933
|
+
BraveProvider,
|
|
1934
|
+
EXA_API_KEY_ENV_VAR,
|
|
1935
|
+
EXA_PROVIDER_META,
|
|
1936
|
+
ExaProvider,
|
|
1937
|
+
FIRECRAWL_API_KEY_ENV_VAR,
|
|
1938
|
+
FIRECRAWL_PROVIDER_META,
|
|
1939
|
+
FirecrawlProvider,
|
|
1940
|
+
JINA_API_KEY_ENV_VAR,
|
|
1941
|
+
JINA_PROVIDER_META,
|
|
1942
|
+
JinaProvider,
|
|
1943
|
+
OLLAMA_API_KEY_ENV_VAR,
|
|
1944
|
+
OLLAMA_HOST_ENV_VAR,
|
|
1945
|
+
OLLAMA_DEFAULT_URL,
|
|
1946
|
+
OLLAMA_PROVIDER_META,
|
|
1947
|
+
OllamaProvider,
|
|
1948
|
+
configureOllama,
|
|
1949
|
+
PERPLEXITY_API_KEY_ENV_VAR,
|
|
1950
|
+
PERPLEXITY_PROVIDER_META,
|
|
1951
|
+
PerplexityProvider,
|
|
1952
|
+
SEARXNG_API_KEY_ENV_VAR,
|
|
1953
|
+
SEARXNG_URL_ENV_VAR,
|
|
1954
|
+
SEARXNG_DEFAULT_URL,
|
|
1955
|
+
SEARXNG_PROVIDER_META,
|
|
1956
|
+
SearxngProvider,
|
|
1957
|
+
configureSearxng,
|
|
1958
|
+
SERPER_API_KEY_ENV_VAR,
|
|
1959
|
+
SERPER_PROVIDER_META,
|
|
1960
|
+
SerperProvider,
|
|
1961
|
+
TAVILY_API_KEY_ENV_VAR,
|
|
1962
|
+
TAVILY_PROVIDER_META,
|
|
1963
|
+
TavilyProvider,
|
|
1964
|
+
YOUCOM_API_KEY_ENV_VAR,
|
|
1965
|
+
YOUCOM_PROVIDER_META,
|
|
1966
|
+
YouComProvider,
|
|
1967
|
+
createSearchProvider,
|
|
1968
|
+
PROVIDERS
|
|
1969
|
+
};
|