docdex 0.2.22 → 0.2.24
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/CHANGELOG.md +6 -5
- package/README.md +18 -14
- package/assets/agents.md +36 -0
- package/bin/docdex.js +6 -11
- package/lib/install.js +38 -136
- package/lib/postinstall_setup.js +575 -116
- package/lib/uninstall.js +263 -34
- package/package.json +2 -4
- package/bin/docdex-mcp-server.js +0 -66
- package/lib/playwright_fetch.js +0 -174
- package/lib/playwright_install.js +0 -302
package/lib/uninstall.js
CHANGED
|
@@ -2,25 +2,138 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
const fs = require("node:fs");
|
|
5
|
+
const net = require("node:net");
|
|
5
6
|
const os = require("node:os");
|
|
6
7
|
const path = require("node:path");
|
|
7
8
|
const { spawnSync } = require("node:child_process");
|
|
8
9
|
|
|
9
10
|
const DAEMON_TASK_NAME = "Docdex Daemon";
|
|
10
11
|
const STARTUP_FAILURE_MARKER = "startup_registration_failed.json";
|
|
12
|
+
const BIN_NAMES = ["docdex", "docdexd"];
|
|
13
|
+
const PACKAGE_NAME = "docdex";
|
|
14
|
+
const DEFAULT_HOST = "127.0.0.1";
|
|
15
|
+
const DEFAULT_DAEMON_PORT = 28491;
|
|
16
|
+
const PORT_WAIT_TIMEOUT_MS = 3000;
|
|
17
|
+
const PORT_WAIT_INTERVAL_MS = 200;
|
|
18
|
+
|
|
19
|
+
function docdexRootPath() {
|
|
20
|
+
return path.join(os.homedir(), ".docdex");
|
|
21
|
+
}
|
|
11
22
|
|
|
12
23
|
function daemonRootPath() {
|
|
13
|
-
return path.join(
|
|
24
|
+
return path.join(docdexRootPath(), "daemon_root");
|
|
14
25
|
}
|
|
15
26
|
|
|
16
27
|
function stateDir() {
|
|
17
|
-
return path.join(
|
|
28
|
+
return path.join(docdexRootPath(), "state");
|
|
18
29
|
}
|
|
19
30
|
|
|
20
|
-
function
|
|
31
|
+
function daemonLockPaths() {
|
|
32
|
+
const paths = [];
|
|
21
33
|
const override = process.env.DOCDEX_DAEMON_LOCK_PATH;
|
|
22
|
-
if (override && override.trim())
|
|
23
|
-
|
|
34
|
+
if (override && override.trim()) paths.push(override.trim());
|
|
35
|
+
const root = docdexRootPath();
|
|
36
|
+
paths.push(path.join(root, "locks", "daemon.lock"));
|
|
37
|
+
paths.push(path.join(root, "daemon.lock"));
|
|
38
|
+
return Array.from(new Set(paths));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isPortAvailable(port, host) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const server = net.createServer();
|
|
44
|
+
server.unref();
|
|
45
|
+
server.once("error", () => resolve(false));
|
|
46
|
+
server.once("listening", () => {
|
|
47
|
+
server.close(() => resolve(true));
|
|
48
|
+
});
|
|
49
|
+
server.listen(port, host);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function sleep(ms) {
|
|
54
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function sleepSync(ms) {
|
|
58
|
+
if (!ms || ms <= 0) return;
|
|
59
|
+
const buffer = new SharedArrayBuffer(4);
|
|
60
|
+
const view = new Int32Array(buffer);
|
|
61
|
+
Atomics.wait(view, 0, 0, ms);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isPidRunning(pid) {
|
|
65
|
+
if (!pid) return false;
|
|
66
|
+
try {
|
|
67
|
+
process.kill(pid, 0);
|
|
68
|
+
return true;
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readLockMetadata(lockPath) {
|
|
75
|
+
if (!lockPath || !fs.existsSync(lockPath)) return null;
|
|
76
|
+
try {
|
|
77
|
+
const raw = fs.readFileSync(lockPath, "utf8");
|
|
78
|
+
const payload = JSON.parse(raw);
|
|
79
|
+
if (!payload || typeof payload !== "object") return null;
|
|
80
|
+
const pid = typeof payload.pid === "number" ? payload.pid : null;
|
|
81
|
+
const port = typeof payload.port === "number" ? payload.port : null;
|
|
82
|
+
return { pid, port, lockPath };
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function findRunningDaemonFromLocks() {
|
|
89
|
+
for (const lockPath of daemonLockPaths()) {
|
|
90
|
+
const meta = readLockMetadata(lockPath);
|
|
91
|
+
if (!meta) continue;
|
|
92
|
+
if (isPidRunning(meta.pid)) return meta;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function manualStopInstructions() {
|
|
98
|
+
if (process.platform === "darwin") {
|
|
99
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
100
|
+
const domain = uid != null ? `gui/${uid}` : "gui/$UID";
|
|
101
|
+
return [
|
|
102
|
+
"Manual cleanup required:",
|
|
103
|
+
`- launchctl bootout ${domain} ~/Library/LaunchAgents/com.docdex.daemon.plist`,
|
|
104
|
+
"- launchctl remove com.docdex.daemon",
|
|
105
|
+
"- pkill -f docdexd",
|
|
106
|
+
"- rm -f ~/.docdex/locks/daemon.lock",
|
|
107
|
+
"- lsof -iTCP:28491 -sTCP:LISTEN",
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
if (process.platform === "win32") {
|
|
111
|
+
return [
|
|
112
|
+
"Manual cleanup required:",
|
|
113
|
+
`- schtasks /End /TN "${DAEMON_TASK_NAME}"`,
|
|
114
|
+
"- schtasks /Delete /TN \"Docdex Daemon\" /F",
|
|
115
|
+
"- taskkill /IM docdexd.exe /T /F",
|
|
116
|
+
"- del %USERPROFILE%\\.docdex\\locks\\daemon.lock",
|
|
117
|
+
"- netstat -ano | findstr 28491",
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
return [
|
|
121
|
+
"Manual cleanup required:",
|
|
122
|
+
"- systemctl --user stop docdexd.service",
|
|
123
|
+
"- systemctl --user disable --now docdexd.service",
|
|
124
|
+
"- pkill -f docdexd",
|
|
125
|
+
"- rm -f ~/.docdex/locks/daemon.lock",
|
|
126
|
+
"- lsof -iTCP:28491 -sTCP:LISTEN",
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function waitForPortFree({ host = DEFAULT_HOST, port = DEFAULT_DAEMON_PORT } = {}) {
|
|
131
|
+
const deadline = Date.now() + PORT_WAIT_TIMEOUT_MS;
|
|
132
|
+
while (Date.now() < deadline) {
|
|
133
|
+
if (await isPortAvailable(port, host)) return true;
|
|
134
|
+
await sleep(PORT_WAIT_INTERVAL_MS);
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
24
137
|
}
|
|
25
138
|
|
|
26
139
|
function clientConfigPaths() {
|
|
@@ -376,6 +489,10 @@ function killPid(pid) {
|
|
|
376
489
|
return true;
|
|
377
490
|
}
|
|
378
491
|
process.kill(pid, "SIGTERM");
|
|
492
|
+
sleepSync(150);
|
|
493
|
+
if (isPidRunning(pid)) {
|
|
494
|
+
process.kill(pid, "SIGKILL");
|
|
495
|
+
}
|
|
379
496
|
return true;
|
|
380
497
|
} catch {
|
|
381
498
|
return false;
|
|
@@ -383,20 +500,23 @@ function killPid(pid) {
|
|
|
383
500
|
}
|
|
384
501
|
|
|
385
502
|
function stopDaemonFromLock() {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
503
|
+
let stopped = false;
|
|
504
|
+
for (const lockPath of daemonLockPaths()) {
|
|
505
|
+
if (!fs.existsSync(lockPath)) continue;
|
|
506
|
+
try {
|
|
507
|
+
const raw = fs.readFileSync(lockPath, "utf8");
|
|
508
|
+
const payload = JSON.parse(raw);
|
|
509
|
+
const pid = payload && typeof payload.pid === "number" ? payload.pid : null;
|
|
510
|
+
stopped = killPid(pid) || stopped;
|
|
511
|
+
fs.unlinkSync(lockPath);
|
|
512
|
+
} catch {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
397
515
|
}
|
|
516
|
+
return stopped;
|
|
398
517
|
}
|
|
399
518
|
|
|
519
|
+
|
|
400
520
|
function stopDaemonByName() {
|
|
401
521
|
if (process.platform === "win32") {
|
|
402
522
|
spawnSync("taskkill", ["/IM", "docdexd.exe", "/T", "/F"]);
|
|
@@ -404,40 +524,46 @@ function stopDaemonByName() {
|
|
|
404
524
|
}
|
|
405
525
|
spawnSync("pkill", ["-TERM", "-x", "docdexd"]);
|
|
406
526
|
spawnSync("pkill", ["-TERM", "-f", "docdexd daemon"]);
|
|
527
|
+
spawnSync("pkill", ["-TERM", "-f", "docdexd serve"]);
|
|
528
|
+
spawnSync("pkill", ["-KILL", "-x", "docdexd"]);
|
|
529
|
+
spawnSync("pkill", ["-KILL", "-f", "docdexd daemon"]);
|
|
530
|
+
spawnSync("pkill", ["-KILL", "-f", "docdexd serve"]);
|
|
407
531
|
return true;
|
|
408
532
|
}
|
|
409
533
|
|
|
410
|
-
function stopMcpByName() {
|
|
411
|
-
if (process.platform === "win32") {
|
|
412
|
-
spawnSync("taskkill", ["/IM", "docdex-mcp-server.exe", "/T", "/F"]);
|
|
413
|
-
return true;
|
|
414
|
-
}
|
|
415
|
-
spawnSync("pkill", ["-TERM", "-x", "docdex-mcp-server"]);
|
|
416
|
-
spawnSync("pkill", ["-TERM", "-f", "docdex-mcp-server"]);
|
|
417
|
-
return true;
|
|
418
|
-
}
|
|
419
534
|
|
|
420
535
|
function unregisterStartup() {
|
|
421
536
|
if (process.platform === "darwin") {
|
|
422
537
|
const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", "com.docdex.daemon.plist");
|
|
538
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
539
|
+
const domain = uid != null ? `gui/${uid}` : null;
|
|
540
|
+
if (domain) {
|
|
541
|
+
spawnSync("launchctl", ["bootout", domain, "com.docdex.daemon"]);
|
|
542
|
+
} else {
|
|
543
|
+
spawnSync("launchctl", ["bootout", "com.docdex.daemon"]);
|
|
544
|
+
}
|
|
545
|
+
spawnSync("launchctl", ["stop", "com.docdex.daemon"]);
|
|
423
546
|
if (fs.existsSync(plistPath)) {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
547
|
+
if (domain) {
|
|
548
|
+
spawnSync("launchctl", ["bootout", domain, plistPath]);
|
|
549
|
+
} else {
|
|
550
|
+
spawnSync("launchctl", ["bootout", plistPath]);
|
|
427
551
|
}
|
|
428
552
|
spawnSync("launchctl", ["unload", "-w", plistPath]);
|
|
429
|
-
spawnSync("launchctl", ["remove", "com.docdex.daemon"]);
|
|
430
553
|
try {
|
|
431
554
|
fs.unlinkSync(plistPath);
|
|
432
555
|
} catch {}
|
|
433
556
|
}
|
|
557
|
+
spawnSync("launchctl", ["remove", "com.docdex.daemon"]);
|
|
434
558
|
return true;
|
|
435
559
|
}
|
|
436
560
|
|
|
437
561
|
if (process.platform === "linux") {
|
|
438
562
|
const systemdDir = path.join(os.homedir(), ".config", "systemd", "user");
|
|
439
563
|
const unitPath = path.join(systemdDir, "docdexd.service");
|
|
564
|
+
spawnSync("systemctl", ["--user", "stop", "docdexd.service"]);
|
|
440
565
|
spawnSync("systemctl", ["--user", "disable", "--now", "docdexd.service"]);
|
|
566
|
+
spawnSync("systemctl", ["--user", "reset-failed", "docdexd.service"]);
|
|
441
567
|
if (fs.existsSync(unitPath)) {
|
|
442
568
|
try {
|
|
443
569
|
fs.unlinkSync(unitPath);
|
|
@@ -490,16 +616,119 @@ function removeClientConfigs() {
|
|
|
490
616
|
}
|
|
491
617
|
}
|
|
492
618
|
|
|
493
|
-
|
|
494
|
-
const
|
|
495
|
-
if (!
|
|
496
|
-
|
|
619
|
+
function removeDocdexRoot() {
|
|
620
|
+
const root = docdexRootPath();
|
|
621
|
+
if (!fs.existsSync(root)) return false;
|
|
622
|
+
const resolvedRoot = path.resolve(root);
|
|
623
|
+
const resolvedHome = path.resolve(os.homedir());
|
|
624
|
+
const expectedRoot = path.join(resolvedHome, ".docdex");
|
|
625
|
+
if (resolvedRoot !== expectedRoot) return false;
|
|
626
|
+
try {
|
|
627
|
+
fs.rmSync(resolvedRoot, { recursive: true, force: true });
|
|
628
|
+
return true;
|
|
629
|
+
} catch {
|
|
630
|
+
return false;
|
|
497
631
|
}
|
|
498
|
-
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function removePath(target) {
|
|
635
|
+
if (!target || !fs.existsSync(target)) return false;
|
|
636
|
+
try {
|
|
637
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
638
|
+
return true;
|
|
639
|
+
} catch {
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function removeBinsInDir(dirPath) {
|
|
645
|
+
if (!dirPath || !fs.existsSync(dirPath)) return false;
|
|
646
|
+
let removed = false;
|
|
647
|
+
for (const name of BIN_NAMES) {
|
|
648
|
+
removed = removePath(path.join(dirPath, name)) || removed;
|
|
649
|
+
}
|
|
650
|
+
return removed;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function removeNodeModuleAt(dirPath) {
|
|
654
|
+
if (!dirPath) return false;
|
|
655
|
+
return removePath(path.join(dirPath, PACKAGE_NAME));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function removePrefixInstalls(prefix) {
|
|
659
|
+
if (!prefix) return;
|
|
660
|
+
removeBinsInDir(path.join(prefix, "bin"));
|
|
661
|
+
removeBinsInDir(prefix);
|
|
662
|
+
removeNodeModuleAt(path.join(prefix, "lib", "node_modules"));
|
|
663
|
+
removeNodeModuleAt(path.join(prefix, "node_modules"));
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function removeHomebrewInstalls() {
|
|
667
|
+
const prefixes = new Set();
|
|
668
|
+
if (process.env.HOMEBREW_PREFIX) prefixes.add(process.env.HOMEBREW_PREFIX);
|
|
669
|
+
prefixes.add("/opt/homebrew");
|
|
670
|
+
prefixes.add("/usr/local");
|
|
671
|
+
for (const prefix of prefixes) {
|
|
672
|
+
removeBinsInDir(path.join(prefix, "bin"));
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function removeNvmInstalls() {
|
|
677
|
+
const home = os.homedir();
|
|
678
|
+
const roots = new Set();
|
|
679
|
+
if (process.env.NVM_DIR) roots.add(process.env.NVM_DIR);
|
|
680
|
+
if (process.env.NVM_HOME) roots.add(process.env.NVM_HOME);
|
|
681
|
+
roots.add(path.join(home, ".nvm"));
|
|
682
|
+
if (process.env.NVM_BIN) {
|
|
683
|
+
removeBinsInDir(process.env.NVM_BIN);
|
|
684
|
+
}
|
|
685
|
+
for (const root of roots) {
|
|
686
|
+
if (!root || !fs.existsSync(root)) continue;
|
|
687
|
+
const versionsRoot = path.join(root, "versions", "node");
|
|
688
|
+
if (!fs.existsSync(versionsRoot)) continue;
|
|
689
|
+
const entries = fs.readdirSync(versionsRoot, { withFileTypes: true });
|
|
690
|
+
for (const entry of entries) {
|
|
691
|
+
if (!entry.isDirectory()) continue;
|
|
692
|
+
const versionDir = path.join(versionsRoot, entry.name);
|
|
693
|
+
removeBinsInDir(path.join(versionDir, "bin"));
|
|
694
|
+
removeNodeModuleAt(path.join(versionDir, "lib", "node_modules"));
|
|
695
|
+
removeNodeModuleAt(path.join(versionDir, "node_modules"));
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function removeCargoInstalls() {
|
|
701
|
+
const home = os.homedir();
|
|
702
|
+
removeBinsInDir(path.join(home, ".cargo", "bin"));
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function purgeExternalInstalls() {
|
|
706
|
+
const prefix = process.env.npm_config_prefix || process.env.PREFIX;
|
|
707
|
+
if (prefix) removePrefixInstalls(prefix);
|
|
708
|
+
removeHomebrewInstalls();
|
|
709
|
+
removeNvmInstalls();
|
|
710
|
+
removeCargoInstalls();
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
async function main() {
|
|
499
714
|
unregisterStartup();
|
|
715
|
+
stopDaemonFromLock();
|
|
716
|
+
stopDaemonByName();
|
|
717
|
+
const freed = await waitForPortFree();
|
|
718
|
+
const running = findRunningDaemonFromLocks();
|
|
719
|
+
if (!freed || running) {
|
|
720
|
+
console.warn(
|
|
721
|
+
`[docdex] ${DEFAULT_HOST}:${DEFAULT_DAEMON_PORT} is still in use or a daemon PID is alive; stop the process manually before reinstalling.`
|
|
722
|
+
);
|
|
723
|
+
for (const line of manualStopInstructions()) {
|
|
724
|
+
console.warn(`[docdex] ${line}`);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
500
727
|
removeClientConfigs();
|
|
501
728
|
clearStartupFailure();
|
|
502
729
|
removeDaemonRootNotice();
|
|
730
|
+
removeDocdexRoot();
|
|
731
|
+
purgeExternalInstalls();
|
|
503
732
|
}
|
|
504
733
|
|
|
505
734
|
if (require.main === module) {
|
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docdex",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.24",
|
|
4
4
|
"mcpName": "io.github.bekirdag/docdex",
|
|
5
5
|
"description": "Local-first documentation and code indexer with HTTP/MCP search, AST, and agent memory.",
|
|
6
6
|
"bin": {
|
|
7
7
|
"docdex": "bin/docdex.js",
|
|
8
|
-
"docdexd": "bin/docdex.js"
|
|
9
|
-
"docdex-mcp-server": "bin/docdex-mcp-server.js"
|
|
8
|
+
"docdexd": "bin/docdex.js"
|
|
10
9
|
},
|
|
11
10
|
"files": [
|
|
12
11
|
"bin",
|
|
@@ -62,7 +61,6 @@
|
|
|
62
61
|
"access": "public"
|
|
63
62
|
},
|
|
64
63
|
"dependencies": {
|
|
65
|
-
"playwright": "^1.49.0",
|
|
66
64
|
"tar": "^6.2.1"
|
|
67
65
|
}
|
|
68
66
|
}
|
package/bin/docdex-mcp-server.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const fs = require("node:fs");
|
|
5
|
-
const path = require("node:path");
|
|
6
|
-
const { spawn } = require("node:child_process");
|
|
7
|
-
|
|
8
|
-
const {
|
|
9
|
-
detectPlatformKey,
|
|
10
|
-
targetTripleForPlatformKey,
|
|
11
|
-
assetPatternForPlatformKey,
|
|
12
|
-
UnsupportedPlatformError
|
|
13
|
-
} = require("../lib/platform");
|
|
14
|
-
|
|
15
|
-
function run() {
|
|
16
|
-
let platformKey;
|
|
17
|
-
try {
|
|
18
|
-
platformKey = detectPlatformKey();
|
|
19
|
-
} catch (err) {
|
|
20
|
-
if (err instanceof UnsupportedPlatformError) {
|
|
21
|
-
const detected = `${err.details?.platform ?? process.platform}/${err.details?.arch ?? process.arch}`;
|
|
22
|
-
const libc = err.details?.libc ? `/${err.details.libc}` : "";
|
|
23
|
-
console.error(`[docdex] unsupported platform (${detected}${libc})`);
|
|
24
|
-
console.error(`[docdex] error code: ${err.code}`);
|
|
25
|
-
console.error("[docdex] No download/run was attempted for this platform.");
|
|
26
|
-
if (Array.isArray(err.details?.supportedPlatformKeys) && err.details.supportedPlatformKeys.length) {
|
|
27
|
-
console.error(`[docdex] Supported platforms: ${err.details.supportedPlatformKeys.join(", ")}`);
|
|
28
|
-
}
|
|
29
|
-
if (typeof err.details?.candidatePlatformKey === "string") {
|
|
30
|
-
console.error(`[docdex] Asset naming pattern: ${assetPatternForPlatformKey(err.details.candidatePlatformKey)}`);
|
|
31
|
-
}
|
|
32
|
-
console.error("[docdex] Next steps: use a supported platform or build from source (Rust).");
|
|
33
|
-
process.exit(err.exitCode || 3);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
console.error(`[docdex] failed to detect platform: ${err?.message || String(err)}`);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const basePath = path.join(__dirname, "..", "dist", platformKey);
|
|
42
|
-
const binaryPath = path.join(
|
|
43
|
-
basePath,
|
|
44
|
-
process.platform === "win32" ? "docdex-mcp-server.exe" : "docdex-mcp-server"
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
if (!fs.existsSync(binaryPath)) {
|
|
48
|
-
console.error(
|
|
49
|
-
`[docdex] Missing MCP server binary for ${platformKey}. Try reinstalling or set DOCDEX_MCP_SERVER_BIN.`
|
|
50
|
-
);
|
|
51
|
-
try {
|
|
52
|
-
console.error(`[docdex] Expected target triple: ${targetTripleForPlatformKey(platformKey)}`);
|
|
53
|
-
console.error(`[docdex] Asset naming pattern: ${assetPatternForPlatformKey(platformKey)}`);
|
|
54
|
-
} catch {}
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const child = spawn(binaryPath, process.argv.slice(2), { stdio: "inherit" });
|
|
59
|
-
child.on("exit", (code) => process.exit(code ?? 1));
|
|
60
|
-
child.on("error", (err) => {
|
|
61
|
-
console.error(`[docdex] failed to launch MCP server: ${err.message}`);
|
|
62
|
-
process.exit(1);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
run();
|
package/lib/playwright_fetch.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const { chromium, firefox, webkit } = require("playwright");
|
|
5
|
-
|
|
6
|
-
const DEFAULT_TIMEOUT_MS = 15000;
|
|
7
|
-
const DEFAULT_BROWSER = "chromium";
|
|
8
|
-
const VIEWPORT = { width: 1920, height: 1080 };
|
|
9
|
-
const CHROMIUM_ARGS = [
|
|
10
|
-
"--disable-blink-features=AutomationControlled",
|
|
11
|
-
"--disable-dev-shm-usage",
|
|
12
|
-
"--disable-gpu",
|
|
13
|
-
"--no-sandbox",
|
|
14
|
-
"--no-first-run",
|
|
15
|
-
"--no-default-browser-check"
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
function normalizeBrowser(value) {
|
|
19
|
-
const trimmed = String(value || "").trim().toLowerCase();
|
|
20
|
-
if (trimmed === "chrome" || trimmed === "chromium" || trimmed === "chromium-browser") {
|
|
21
|
-
return "chromium";
|
|
22
|
-
}
|
|
23
|
-
if (trimmed === "firefox") return "firefox";
|
|
24
|
-
if (trimmed === "webkit") return "webkit";
|
|
25
|
-
return DEFAULT_BROWSER;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function resolveBrowserType(name) {
|
|
29
|
-
switch (name) {
|
|
30
|
-
case "chromium":
|
|
31
|
-
return chromium;
|
|
32
|
-
case "firefox":
|
|
33
|
-
return firefox;
|
|
34
|
-
case "webkit":
|
|
35
|
-
return webkit;
|
|
36
|
-
default:
|
|
37
|
-
return chromium;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function parseArgs(argv) {
|
|
42
|
-
const parsed = {
|
|
43
|
-
url: "",
|
|
44
|
-
browser: DEFAULT_BROWSER,
|
|
45
|
-
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
46
|
-
userAgent: "",
|
|
47
|
-
headless: true,
|
|
48
|
-
userDataDir: ""
|
|
49
|
-
};
|
|
50
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
51
|
-
const value = argv[i];
|
|
52
|
-
if (value === "--url" && argv[i + 1]) {
|
|
53
|
-
parsed.url = argv[i + 1];
|
|
54
|
-
i += 1;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (value === "--browser" && argv[i + 1]) {
|
|
58
|
-
parsed.browser = argv[i + 1];
|
|
59
|
-
i += 1;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (value === "--timeout-ms" && argv[i + 1]) {
|
|
63
|
-
parsed.timeoutMs = Number(argv[i + 1]);
|
|
64
|
-
i += 1;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (value === "--user-agent" && argv[i + 1]) {
|
|
68
|
-
parsed.userAgent = argv[i + 1];
|
|
69
|
-
i += 1;
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if (value === "--user-data-dir" && argv[i + 1]) {
|
|
73
|
-
parsed.userDataDir = argv[i + 1];
|
|
74
|
-
i += 1;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (value === "--headless") {
|
|
78
|
-
parsed.headless = true;
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (value === "--headed") {
|
|
82
|
-
parsed.headless = false;
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
parsed.browser = normalizeBrowser(parsed.browser);
|
|
87
|
-
if (!parsed.url) {
|
|
88
|
-
throw new Error("missing --url");
|
|
89
|
-
}
|
|
90
|
-
if (!Number.isFinite(parsed.timeoutMs) || parsed.timeoutMs <= 0) {
|
|
91
|
-
parsed.timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
92
|
-
}
|
|
93
|
-
return parsed;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function fetchWithPlaywright(options) {
|
|
97
|
-
const browserName = normalizeBrowser(options.browser);
|
|
98
|
-
const browserType = resolveBrowserType(browserName);
|
|
99
|
-
const launchOptions = {
|
|
100
|
-
headless: options.headless
|
|
101
|
-
};
|
|
102
|
-
if (browserName === "chromium") {
|
|
103
|
-
launchOptions.args = CHROMIUM_ARGS;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
let browser;
|
|
107
|
-
let context;
|
|
108
|
-
try {
|
|
109
|
-
if (options.userDataDir) {
|
|
110
|
-
context = await browserType.launchPersistentContext(options.userDataDir, {
|
|
111
|
-
...launchOptions,
|
|
112
|
-
viewport: VIEWPORT,
|
|
113
|
-
userAgent: options.userAgent || undefined
|
|
114
|
-
});
|
|
115
|
-
} else {
|
|
116
|
-
browser = await browserType.launch(launchOptions);
|
|
117
|
-
context = await browser.newContext({
|
|
118
|
-
viewport: VIEWPORT,
|
|
119
|
-
userAgent: options.userAgent || undefined
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
await context.addInitScript(() => {
|
|
124
|
-
Object.defineProperty(navigator, "webdriver", { get: () => undefined });
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const page = await context.newPage();
|
|
128
|
-
page.setDefaultTimeout(options.timeoutMs);
|
|
129
|
-
const response = await page.goto(options.url, {
|
|
130
|
-
waitUntil: "domcontentloaded",
|
|
131
|
-
timeout: options.timeoutMs
|
|
132
|
-
});
|
|
133
|
-
const html = await page.content();
|
|
134
|
-
const status = response ? response.status() : null;
|
|
135
|
-
const finalUrl = page.url();
|
|
136
|
-
await page.close();
|
|
137
|
-
|
|
138
|
-
if (!html || !String(html).trim()) {
|
|
139
|
-
throw new Error("empty HTML response");
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return { html: String(html), status, final_url: finalUrl };
|
|
143
|
-
} finally {
|
|
144
|
-
if (context) {
|
|
145
|
-
await context.close();
|
|
146
|
-
}
|
|
147
|
-
if (browser) {
|
|
148
|
-
await browser.close();
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async function main() {
|
|
154
|
-
try {
|
|
155
|
-
const options = parseArgs(process.argv.slice(2));
|
|
156
|
-
const result = await fetchWithPlaywright(options);
|
|
157
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
158
|
-
} catch (err) {
|
|
159
|
-
const message = err?.message || String(err);
|
|
160
|
-
console.error(`[docdex] playwright fetch failed: ${message}`);
|
|
161
|
-
process.exit(1);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (require.main === module) {
|
|
166
|
-
main();
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
module.exports = {
|
|
170
|
-
fetchWithPlaywright,
|
|
171
|
-
normalizeBrowser,
|
|
172
|
-
parseArgs,
|
|
173
|
-
resolveBrowserType
|
|
174
|
-
};
|