@undefineds.co/linx 0.3.5 → 0.3.8
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 +58 -23
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/index.js +336 -162
- package/dist/index.js.map +1 -1
- package/dist/lib/account-session.js +4 -8
- package/dist/lib/account-session.js.map +1 -1
- package/dist/lib/ai-command.js +228 -178
- package/dist/lib/ai-command.js.map +1 -1
- package/dist/lib/auto-mode/archive.js +38 -7
- package/dist/lib/auto-mode/archive.js.map +1 -1
- package/dist/lib/auto-mode/auth.js.map +1 -1
- package/dist/lib/auto-mode/display.js +71 -45
- package/dist/lib/auto-mode/display.js.map +1 -1
- package/dist/lib/auto-mode/format.js +9 -7
- package/dist/lib/auto-mode/format.js.map +1 -1
- package/dist/lib/auto-mode/hooks/claude.js +12 -2
- package/dist/lib/auto-mode/hooks/claude.js.map +1 -1
- package/dist/lib/auto-mode/hooks/codex.js +17 -7
- package/dist/lib/auto-mode/hooks/codex.js.map +1 -1
- package/dist/lib/auto-mode/hooks/index.js +28 -8
- package/dist/lib/auto-mode/hooks/index.js.map +1 -1
- package/dist/lib/auto-mode/pod-ai.js +20 -37
- package/dist/lib/auto-mode/pod-ai.js.map +1 -1
- package/dist/lib/auto-mode/pod-approval.js +124 -195
- package/dist/lib/auto-mode/pod-approval.js.map +1 -1
- package/dist/lib/auto-mode/pod-persistence.js +169 -90
- package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
- package/dist/lib/auto-mode/runner.js +683 -81
- package/dist/lib/auto-mode/runner.js.map +1 -1
- package/dist/lib/auto-mode/secretary.js +186 -41
- package/dist/lib/auto-mode/secretary.js.map +1 -1
- package/dist/lib/auto-mode-command.js +32 -32
- package/dist/lib/auto-mode-command.js.map +1 -1
- package/dist/lib/chat-api.js +242 -50
- package/dist/lib/chat-api.js.map +1 -1
- package/dist/lib/codex-plugin/bridge.js +164 -17
- package/dist/lib/codex-plugin/bridge.js.map +1 -1
- package/dist/lib/codex-plugin/codex-native-proxy.js +370 -34
- package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
- package/dist/lib/credentials-store.js +33 -42
- package/dist/lib/credentials-store.js.map +1 -1
- package/dist/lib/linx-cloud-errors.js +61 -0
- package/dist/lib/linx-cloud-errors.js.map +1 -0
- package/dist/lib/linx-tui-contract.js +8 -5
- package/dist/lib/linx-tui-contract.js.map +1 -1
- package/dist/lib/login-command.js +9 -2
- package/dist/lib/login-command.js.map +1 -1
- package/dist/lib/models.js +3 -20
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/oidc-auth.js +143 -17
- package/dist/lib/oidc-auth.js.map +1 -1
- package/dist/lib/oidc-session-storage.js +2 -6
- package/dist/lib/oidc-session-storage.js.map +1 -1
- package/dist/lib/pi-adapter/auto-input-controller.js +988 -0
- package/dist/lib/pi-adapter/auto-input-controller.js.map +1 -0
- package/dist/lib/pi-adapter/backend-command.js +2 -0
- package/dist/lib/pi-adapter/backend-command.js.map +1 -0
- package/dist/lib/pi-adapter/backend-credentials.js +80 -0
- package/dist/lib/pi-adapter/backend-credentials.js.map +1 -0
- package/dist/lib/pi-adapter/branding.js +246 -108
- package/dist/lib/pi-adapter/branding.js.map +1 -1
- package/dist/lib/pi-adapter/control-state.js +72 -0
- package/dist/lib/pi-adapter/control-state.js.map +1 -0
- package/dist/lib/pi-adapter/interactive.js +2634 -30
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-approval.js +382 -210
- package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror-mapping.js +71 -17
- package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +531 -64
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +81 -85
- package/dist/lib/pi-adapter/pod-native.js.map +1 -1
- package/dist/lib/pi-adapter/pod-status-output.js +54 -0
- package/dist/lib/pi-adapter/pod-status-output.js.map +1 -0
- package/dist/lib/pi-adapter/runtime.js +458 -228
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/session-control.js +509 -0
- package/dist/lib/pi-adapter/session-control.js.map +1 -0
- package/dist/lib/pi-adapter/session.js +35 -22
- package/dist/lib/pi-adapter/session.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +89 -32
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pi-adapter/sync-recovery.js +89 -0
- package/dist/lib/pi-adapter/sync-recovery.js.map +1 -0
- package/dist/lib/pi-adapter/web-fetch.js +13 -14
- package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
- package/dist/lib/pod-chat-store.js +254 -78
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/pod-data-session.js +156 -35
- package/dist/lib/pod-data-session.js.map +1 -1
- package/dist/lib/solid-auth-store.js +27 -0
- package/dist/lib/solid-auth-store.js.map +1 -0
- package/dist/lib/solid-auth.js +2 -4
- package/dist/lib/solid-auth.js.map +1 -1
- package/dist/lib/solid-client-credentials-login.js +100 -0
- package/dist/lib/solid-client-credentials-login.js.map +1 -0
- package/dist/lib/solid-local-store.js +31 -0
- package/dist/lib/solid-local-store.js.map +1 -0
- package/dist/lib/symphony/archive.js +328 -18
- package/dist/lib/symphony/archive.js.map +1 -1
- package/dist/lib/symphony/pod-projection.js +2222 -0
- package/dist/lib/symphony/pod-projection.js.map +1 -0
- package/dist/lib/symphony-command.js +602 -178
- package/dist/lib/symphony-command.js.map +1 -1
- package/dist/lib/sync-checkpoint-store.js +74 -0
- package/dist/lib/sync-checkpoint-store.js.map +1 -0
- package/dist/skills/symphony/SKILL.md +665 -0
- package/package.json +15 -9
- package/vendor/agent-runtime/dist/agent-runtime.d.ts +137 -0
- package/vendor/agent-runtime/dist/agent-runtime.js +211 -0
- package/vendor/agent-runtime/dist/auto-mode.d.ts +78 -13
- package/vendor/agent-runtime/dist/auto-mode.js +288 -31
- package/vendor/agent-runtime/dist/control-plane.d.ts +28 -0
- package/vendor/agent-runtime/dist/control-plane.js +79 -0
- package/vendor/agent-runtime/dist/file-sync.d.ts +157 -0
- package/vendor/agent-runtime/dist/file-sync.js +314 -0
- package/vendor/agent-runtime/dist/index.d.ts +7 -0
- package/vendor/agent-runtime/dist/index.js +7 -0
- package/vendor/agent-runtime/dist/reconciler.d.ts +117 -0
- package/vendor/agent-runtime/dist/reconciler.js +361 -0
- package/vendor/agent-runtime/dist/symphony.d.ts +128 -8
- package/vendor/agent-runtime/dist/symphony.js +362 -57
- package/vendor/agent-runtime/dist/sync.d.ts +271 -0
- package/vendor/agent-runtime/dist/sync.js +550 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.d.ts +58 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.js +137 -0
- package/vendor/agent-runtime/dist/turn-controller.js +2 -2
- package/vendor/agent-runtime/dist/wake-scheduler.d.ts +67 -0
- package/vendor/agent-runtime/dist/wake-scheduler.js +194 -0
- package/vendor/agent-runtime/package.json +8 -1
- package/vendor/pi-web-access/CHANGELOG.md +387 -0
- package/vendor/pi-web-access/LICENSE +21 -0
- package/vendor/pi-web-access/README.md +352 -0
- package/vendor/pi-web-access/activity.ts +101 -0
- package/vendor/pi-web-access/banner.png +0 -0
- package/vendor/pi-web-access/chrome-cookies.ts +322 -0
- package/vendor/pi-web-access/code-search.ts +107 -0
- package/vendor/pi-web-access/curator-page.ts +3359 -0
- package/vendor/pi-web-access/curator-server.ts +605 -0
- package/vendor/pi-web-access/exa.ts +520 -0
- package/vendor/pi-web-access/extract.ts +641 -0
- package/vendor/pi-web-access/gemini-api.ts +112 -0
- package/vendor/pi-web-access/gemini-search.ts +361 -0
- package/vendor/pi-web-access/gemini-url-context.ts +126 -0
- package/vendor/pi-web-access/gemini-web-config.ts +52 -0
- package/vendor/pi-web-access/gemini-web.ts +396 -0
- package/vendor/pi-web-access/github-api.ts +196 -0
- package/vendor/pi-web-access/github-extract.ts +634 -0
- package/vendor/pi-web-access/index.ts +2346 -0
- package/vendor/pi-web-access/package.json +45 -0
- package/vendor/pi-web-access/pdf-extract.ts +192 -0
- package/vendor/pi-web-access/perplexity.ts +195 -0
- package/vendor/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
- package/vendor/pi-web-access/rsc-extract.ts +338 -0
- package/vendor/pi-web-access/skills/librarian/SKILL.md +195 -0
- package/vendor/pi-web-access/storage.ts +72 -0
- package/vendor/pi-web-access/summary-review.ts +276 -0
- package/vendor/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +41 -0
- package/vendor/pi-web-access/test/pdf-extract.test.mjs +95 -0
- package/vendor/pi-web-access/utils.ts +44 -0
- package/vendor/pi-web-access/video-extract.ts +378 -0
- package/vendor/pi-web-access/youtube-extract.ts +310 -0
- package/dist/lib/pi-adapter/auth.js +0 -68
- package/dist/lib/pi-adapter/auth.js.map +0 -1
- package/dist/lib/pi-adapter/pod-tools.js +0 -140
- package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
- package/dist/skills/drizzle-solid/SKILL.md +0 -340
- package/dist/skills/pod-storage/SKILL.md +0 -100
- package/dist/skills/solid-modeling/SKILL.md +0 -274
- package/dist/skills/xpod-componentsjs/SKILL.md +0 -284
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { pbkdf2Sync, createDecipheriv } from "node:crypto";
|
|
3
|
+
import { copyFileSync, existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir, homedir, platform } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
|
|
7
|
+
export type CookieMap = Record<string, string>;
|
|
8
|
+
|
|
9
|
+
interface BrowserConfig {
|
|
10
|
+
name: string;
|
|
11
|
+
baseDir: string;
|
|
12
|
+
keychainService?: string;
|
|
13
|
+
keychainAccount?: string;
|
|
14
|
+
secretToolApp?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const GOOGLE_ORIGINS = [
|
|
18
|
+
"https://gemini.google.com",
|
|
19
|
+
"https://accounts.google.com",
|
|
20
|
+
"https://www.google.com",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const ALL_COOKIE_NAMES = new Set([
|
|
24
|
+
"__Secure-1PSID",
|
|
25
|
+
"__Secure-1PSIDTS",
|
|
26
|
+
"__Secure-1PSIDCC",
|
|
27
|
+
"__Secure-1PAPISID",
|
|
28
|
+
"NID",
|
|
29
|
+
"AEC",
|
|
30
|
+
"SOCS",
|
|
31
|
+
"__Secure-BUCKET",
|
|
32
|
+
"__Secure-ENID",
|
|
33
|
+
"SID",
|
|
34
|
+
"HSID",
|
|
35
|
+
"SSID",
|
|
36
|
+
"APISID",
|
|
37
|
+
"SAPISID",
|
|
38
|
+
"__Secure-3PSID",
|
|
39
|
+
"__Secure-3PSIDTS",
|
|
40
|
+
"__Secure-3PAPISID",
|
|
41
|
+
"SIDCC",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
const MACOS_BROWSER_CONFIGS: BrowserConfig[] = [
|
|
45
|
+
{
|
|
46
|
+
name: "Helium",
|
|
47
|
+
baseDir: "Library/Application Support/net.imput.helium",
|
|
48
|
+
keychainService: "Helium Storage Key",
|
|
49
|
+
keychainAccount: "Helium",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "Chrome",
|
|
53
|
+
baseDir: "Library/Application Support/Google/Chrome",
|
|
54
|
+
keychainService: "Chrome Safe Storage",
|
|
55
|
+
keychainAccount: "Chrome",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "Arc",
|
|
59
|
+
baseDir: "Library/Application Support/Arc/User Data",
|
|
60
|
+
keychainService: "Arc Safe Storage",
|
|
61
|
+
keychainAccount: "Arc",
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const LINUX_BROWSER_CONFIGS: BrowserConfig[] = [
|
|
66
|
+
{ name: "Chromium", baseDir: ".config/chromium", secretToolApp: "chromium" },
|
|
67
|
+
{ name: "Chrome", baseDir: ".config/google-chrome", secretToolApp: "chrome" },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
export async function getGoogleCookies(
|
|
71
|
+
options?: { profile?: string; requiredCookies?: string[] },
|
|
72
|
+
): Promise<{ cookies: CookieMap; warnings: string[] } | null> {
|
|
73
|
+
const currentPlatform = platform();
|
|
74
|
+
const configs = currentPlatform === "darwin"
|
|
75
|
+
? MACOS_BROWSER_CONFIGS
|
|
76
|
+
: currentPlatform === "linux"
|
|
77
|
+
? LINUX_BROWSER_CONFIGS
|
|
78
|
+
: [];
|
|
79
|
+
if (configs.length === 0) return null;
|
|
80
|
+
|
|
81
|
+
const warnings: string[] = [];
|
|
82
|
+
const profile = options?.profile ?? "Default";
|
|
83
|
+
const hosts = GOOGLE_ORIGINS.map((origin) => new URL(origin).hostname);
|
|
84
|
+
|
|
85
|
+
for (const config of configs) {
|
|
86
|
+
const cookiesPath = join(homedir(), config.baseDir, profile, "Cookies");
|
|
87
|
+
if (!existsSync(cookiesPath)) continue;
|
|
88
|
+
|
|
89
|
+
const password = await readBrowserPassword(config, currentPlatform);
|
|
90
|
+
if (!password) {
|
|
91
|
+
warnings.push(`Could not read ${config.name} cookie encryption password`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const key = pbkdf2Sync(password, "saltysalt", currentPlatform === "darwin" ? 1003 : 1, 16, "sha1");
|
|
96
|
+
const tempDir = mkdtempSync(join(tmpdir(), "pi-chrome-cookies-"));
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const tempDb = join(tempDir, "Cookies");
|
|
100
|
+
copyFileSync(cookiesPath, tempDb);
|
|
101
|
+
copySidecar(cookiesPath, tempDb, "-wal");
|
|
102
|
+
copySidecar(cookiesPath, tempDb, "-shm");
|
|
103
|
+
|
|
104
|
+
const metaVersion = await readMetaVersion(tempDb);
|
|
105
|
+
const stripHash = metaVersion >= 24;
|
|
106
|
+
const rows = await queryCookieRows(tempDb, hosts);
|
|
107
|
+
if (!rows) {
|
|
108
|
+
warnings.push(`Failed to query ${config.name} cookie database`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const cookies: CookieMap = {};
|
|
113
|
+
for (const row of rows) {
|
|
114
|
+
const name = row.name as string;
|
|
115
|
+
if (!ALL_COOKIE_NAMES.has(name)) continue;
|
|
116
|
+
if (cookies[name]) continue;
|
|
117
|
+
|
|
118
|
+
let value = typeof row.value === "string" && row.value.length > 0 ? row.value : null;
|
|
119
|
+
if (!value) {
|
|
120
|
+
const encrypted = row.encrypted_value;
|
|
121
|
+
if (encrypted instanceof Uint8Array) {
|
|
122
|
+
value = decryptCookieValue(encrypted, key, stripHash);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (value) cookies[name] = value;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (options?.requiredCookies?.length && !options.requiredCookies.every((name) => Boolean(cookies[name]))) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { cookies, warnings };
|
|
133
|
+
} finally {
|
|
134
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function decryptCookieValue(encrypted: Uint8Array, key: Buffer, stripHash: boolean): string | null {
|
|
142
|
+
const buf = Buffer.from(encrypted);
|
|
143
|
+
if (buf.length < 3) return null;
|
|
144
|
+
|
|
145
|
+
const prefix = buf.subarray(0, 3).toString("utf8");
|
|
146
|
+
if (!/^v\d\d$/.test(prefix)) return null;
|
|
147
|
+
|
|
148
|
+
const ciphertext = buf.subarray(3);
|
|
149
|
+
if (!ciphertext.length) return "";
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const iv = Buffer.alloc(16, 0x20);
|
|
153
|
+
const decipher = createDecipheriv("aes-128-cbc", key, iv);
|
|
154
|
+
decipher.setAutoPadding(false);
|
|
155
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
156
|
+
const unpadded = removePkcs7Padding(plaintext);
|
|
157
|
+
const bytes = stripHash && unpadded.length >= 32 ? unpadded.subarray(32) : unpadded;
|
|
158
|
+
const decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
159
|
+
let i = 0;
|
|
160
|
+
while (i < decoded.length && decoded.charCodeAt(i) < 0x20) i++;
|
|
161
|
+
return decoded.slice(i);
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function removePkcs7Padding(buf: Buffer): Buffer {
|
|
168
|
+
if (!buf.length) return buf;
|
|
169
|
+
const padding = buf[buf.length - 1];
|
|
170
|
+
if (!padding || padding > 16) return buf;
|
|
171
|
+
return buf.subarray(0, buf.length - padding);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function readBrowserPassword(
|
|
175
|
+
config: BrowserConfig,
|
|
176
|
+
currentPlatform: ReturnType<typeof platform>,
|
|
177
|
+
): Promise<string | null> {
|
|
178
|
+
if (currentPlatform === "darwin") {
|
|
179
|
+
if (!config.keychainAccount || !config.keychainService) return Promise.resolve(null);
|
|
180
|
+
return readKeychainPassword(config.keychainAccount, config.keychainService);
|
|
181
|
+
}
|
|
182
|
+
if (currentPlatform === "linux") {
|
|
183
|
+
return readLinuxPassword(config.secretToolApp);
|
|
184
|
+
}
|
|
185
|
+
return Promise.resolve(null);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function readKeychainPassword(account: string, service: string): Promise<string | null> {
|
|
189
|
+
return new Promise((resolve) => {
|
|
190
|
+
execFile(
|
|
191
|
+
"security",
|
|
192
|
+
["find-generic-password", "-w", "-a", account, "-s", service],
|
|
193
|
+
{ timeout: 5000 },
|
|
194
|
+
(err, stdout) => {
|
|
195
|
+
if (err) { resolve(null); return; }
|
|
196
|
+
resolve(stdout.trim() || null);
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function readLinuxPassword(secretToolApp: string | undefined): Promise<string> {
|
|
203
|
+
if (!secretToolApp) return Promise.resolve("peanuts");
|
|
204
|
+
|
|
205
|
+
return new Promise((resolve) => {
|
|
206
|
+
execFile(
|
|
207
|
+
"secret-tool",
|
|
208
|
+
["lookup", "application", secretToolApp],
|
|
209
|
+
{ timeout: 5000 },
|
|
210
|
+
(err, stdout) => {
|
|
211
|
+
if (err) {
|
|
212
|
+
// KDE Wallet users fall through to peanuts intentionally.
|
|
213
|
+
resolve("peanuts");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
resolve(stdout.trim() || "peanuts");
|
|
217
|
+
},
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let sqliteModule: typeof import("node:sqlite") | null = null;
|
|
223
|
+
|
|
224
|
+
async function importSqlite(): Promise<typeof import("node:sqlite") | null> {
|
|
225
|
+
if (sqliteModule) return sqliteModule;
|
|
226
|
+
const orig = process.emitWarning.bind(process);
|
|
227
|
+
process.emitWarning = ((warning: string | Error, ...args: unknown[]) => {
|
|
228
|
+
const msg = typeof warning === "string" ? warning : warning?.message ?? "";
|
|
229
|
+
if (msg.includes("SQLite is an experimental feature")) return;
|
|
230
|
+
return (orig as Function)(warning, ...args);
|
|
231
|
+
}) as typeof process.emitWarning;
|
|
232
|
+
try {
|
|
233
|
+
sqliteModule = await import("node:sqlite");
|
|
234
|
+
return sqliteModule;
|
|
235
|
+
} catch {
|
|
236
|
+
return null;
|
|
237
|
+
} finally {
|
|
238
|
+
process.emitWarning = orig;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function supportsReadBigInts(): boolean {
|
|
243
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
244
|
+
if (major > 24) return true;
|
|
245
|
+
if (major < 24) return false;
|
|
246
|
+
return minor >= 4;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function readMetaVersion(dbPath: string): Promise<number> {
|
|
250
|
+
const sqlite = await importSqlite();
|
|
251
|
+
if (!sqlite) return 0;
|
|
252
|
+
const opts: Record<string, unknown> = { readOnly: true };
|
|
253
|
+
if (supportsReadBigInts()) opts.readBigInts = true;
|
|
254
|
+
const db = new sqlite.DatabaseSync(dbPath, opts);
|
|
255
|
+
try {
|
|
256
|
+
const rows = db.prepare("SELECT value FROM meta WHERE key = 'version'").all() as Array<Record<string, unknown>>;
|
|
257
|
+
const val = rows[0]?.value;
|
|
258
|
+
if (typeof val === "number") return Math.floor(val);
|
|
259
|
+
if (typeof val === "bigint") return Number(val);
|
|
260
|
+
if (typeof val === "string") return parseInt(val, 10) || 0;
|
|
261
|
+
return 0;
|
|
262
|
+
} catch {
|
|
263
|
+
return 0;
|
|
264
|
+
} finally {
|
|
265
|
+
db.close();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function queryCookieRows(
|
|
270
|
+
dbPath: string,
|
|
271
|
+
hosts: string[],
|
|
272
|
+
): Promise<Array<Record<string, unknown>> | null> {
|
|
273
|
+
const sqlite = await importSqlite();
|
|
274
|
+
if (!sqlite) return null;
|
|
275
|
+
|
|
276
|
+
const clauses: string[] = [];
|
|
277
|
+
for (const host of hosts) {
|
|
278
|
+
for (const candidate of expandHosts(host)) {
|
|
279
|
+
const esc = candidate.replaceAll("'", "''");
|
|
280
|
+
clauses.push(`host_key = '${esc}'`);
|
|
281
|
+
clauses.push(`host_key = '.${esc}'`);
|
|
282
|
+
clauses.push(`host_key LIKE '%.${esc}'`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const where = clauses.join(" OR ");
|
|
286
|
+
|
|
287
|
+
const opts: Record<string, unknown> = { readOnly: true };
|
|
288
|
+
if (supportsReadBigInts()) opts.readBigInts = true;
|
|
289
|
+
const db = new sqlite.DatabaseSync(dbPath, opts);
|
|
290
|
+
try {
|
|
291
|
+
return db
|
|
292
|
+
.prepare(
|
|
293
|
+
`SELECT name, value, host_key, encrypted_value FROM cookies WHERE (${where}) ORDER BY expires_utc DESC`,
|
|
294
|
+
)
|
|
295
|
+
.all() as Array<Record<string, unknown>>;
|
|
296
|
+
} catch {
|
|
297
|
+
return null;
|
|
298
|
+
} finally {
|
|
299
|
+
db.close();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function expandHosts(host: string): string[] {
|
|
304
|
+
const parts = host.split(".").filter(Boolean);
|
|
305
|
+
if (parts.length <= 1) return [host];
|
|
306
|
+
const candidates = new Set<string>();
|
|
307
|
+
candidates.add(host);
|
|
308
|
+
for (let i = 1; i <= parts.length - 2; i++) {
|
|
309
|
+
const c = parts.slice(i).join(".");
|
|
310
|
+
if (c) candidates.add(c);
|
|
311
|
+
}
|
|
312
|
+
return Array.from(candidates);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function copySidecar(srcDb: string, targetDb: string, suffix: string): void {
|
|
316
|
+
const sidecar = `${srcDb}${suffix}`;
|
|
317
|
+
if (!existsSync(sidecar)) return;
|
|
318
|
+
try {
|
|
319
|
+
copyFileSync(sidecar, `${targetDb}${suffix}`);
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { activityMonitor } from "./activity.js";
|
|
2
|
+
import { callExaMcp } from "./exa.js";
|
|
3
|
+
|
|
4
|
+
const CODE_CONTEXT_TOOL = "get_code_context_exa";
|
|
5
|
+
const WEB_SEARCH_TOOL = "web_search_exa";
|
|
6
|
+
const DEFAULT_MAX_TOKENS = 5000;
|
|
7
|
+
|
|
8
|
+
let codeContextToolMissing = false;
|
|
9
|
+
|
|
10
|
+
function isMissingMcpToolError(message: string): boolean {
|
|
11
|
+
const normalized = message.toLowerCase();
|
|
12
|
+
return normalized.includes("tool") && normalized.includes("not found");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function buildFallbackQuery(query: string): string {
|
|
16
|
+
const normalized = query.toLowerCase();
|
|
17
|
+
const hasCodeTerms = /\b(api|code|docs?|documentation|example|github|implementation|library|source|stackoverflow|stack overflow)\b/.test(normalized);
|
|
18
|
+
return hasCodeTerms ? query : `${query} code examples documentation GitHub Stack Overflow official docs`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function maxTokensToResultCount(maxTokens: number): number {
|
|
22
|
+
return Math.min(20, Math.max(5, Math.ceil(maxTokens / 1000)));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function trimApproxTokens(text: string, maxTokens: number): string {
|
|
26
|
+
const maxCharacters = Math.max(1000, maxTokens * 4);
|
|
27
|
+
if (text.length <= maxCharacters) return text;
|
|
28
|
+
return `${text.slice(0, maxCharacters).trimEnd()}\n\n[Truncated by code_search to approximately ${maxTokens} tokens.]`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function executeFallbackSearch(query: string, maxTokens: number, signal?: AbortSignal): Promise<string> {
|
|
32
|
+
const text = await callExaMcp(
|
|
33
|
+
WEB_SEARCH_TOOL,
|
|
34
|
+
{
|
|
35
|
+
query: buildFallbackQuery(query),
|
|
36
|
+
numResults: maxTokensToResultCount(maxTokens),
|
|
37
|
+
livecrawl: "fallback",
|
|
38
|
+
type: "auto",
|
|
39
|
+
contextMaxCharacters: Math.min(50000, Math.max(1000, maxTokens * 4)),
|
|
40
|
+
},
|
|
41
|
+
signal,
|
|
42
|
+
);
|
|
43
|
+
return trimApproxTokens(text, maxTokens);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function executeCodeSearch(
|
|
47
|
+
_toolCallId: string,
|
|
48
|
+
params: { query: string; maxTokens?: number },
|
|
49
|
+
signal?: AbortSignal,
|
|
50
|
+
): Promise<{
|
|
51
|
+
content: Array<{ type: "text"; text: string }>;
|
|
52
|
+
details: { query: string; maxTokens: number; error?: string; mode?: "code-context" | "web-search-fallback" };
|
|
53
|
+
}> {
|
|
54
|
+
const query = params.query.trim();
|
|
55
|
+
if (!query) {
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: "Error: No query provided." }],
|
|
58
|
+
details: { query: "", maxTokens: params.maxTokens ?? DEFAULT_MAX_TOKENS, error: "No query provided" },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const maxTokens = params.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
63
|
+
const activityId = activityMonitor.logStart({ type: "api", query });
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
let mode: "code-context" | "web-search-fallback" = "web-search-fallback";
|
|
67
|
+
let text: string;
|
|
68
|
+
|
|
69
|
+
if (codeContextToolMissing) {
|
|
70
|
+
text = await executeFallbackSearch(query, maxTokens, signal);
|
|
71
|
+
} else {
|
|
72
|
+
try {
|
|
73
|
+
text = await callExaMcp(
|
|
74
|
+
CODE_CONTEXT_TOOL,
|
|
75
|
+
{
|
|
76
|
+
query,
|
|
77
|
+
tokensNum: maxTokens,
|
|
78
|
+
},
|
|
79
|
+
signal,
|
|
80
|
+
);
|
|
81
|
+
mode = "code-context";
|
|
82
|
+
} catch (err) {
|
|
83
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
84
|
+
if (!isMissingMcpToolError(message)) throw err;
|
|
85
|
+
codeContextToolMissing = true;
|
|
86
|
+
text = await executeFallbackSearch(query, maxTokens, signal);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
activityMonitor.logComplete(activityId, 200);
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text", text }],
|
|
93
|
+
details: { query, maxTokens, mode },
|
|
94
|
+
};
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
97
|
+
if (message.toLowerCase().includes("abort")) {
|
|
98
|
+
activityMonitor.logComplete(activityId, 0);
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
activityMonitor.logError(activityId, message);
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
104
|
+
details: { query, maxTokens, error: message },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|