docdex 0.2.21 → 0.2.23
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 +10 -4
- package/README.md +221 -272
- package/assets/agents.md +14 -0
- package/bin/docdex.js +6 -11
- package/lib/install.js +38 -136
- package/lib/postinstall_setup.js +289 -98
- 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/install.js
CHANGED
|
@@ -8,7 +8,7 @@ const os = require("node:os");
|
|
|
8
8
|
const path = require("node:path");
|
|
9
9
|
const { pipeline } = require("node:stream/promises");
|
|
10
10
|
const crypto = require("node:crypto");
|
|
11
|
-
const { spawn
|
|
11
|
+
const { spawn } = require("node:child_process");
|
|
12
12
|
|
|
13
13
|
const pkg = require("../package.json");
|
|
14
14
|
const {
|
|
@@ -41,8 +41,6 @@ const DEFAULT_INTEGRITY_CONFIG = Object.freeze({
|
|
|
41
41
|
const LOCAL_FALLBACK_ENV = "DOCDEX_LOCAL_FALLBACK";
|
|
42
42
|
const LOCAL_BINARY_ENV = "DOCDEX_LOCAL_BINARY";
|
|
43
43
|
const AGENTS_DOC_FILENAME = "agents.md";
|
|
44
|
-
const PLAYWRIGHT_INSTALL_GUARD = "DOCDEX_INTERNAL_PLAYWRIGHT_INSTALL";
|
|
45
|
-
const PLAYWRIGHT_SKIP_ENV = "DOCDEX_SKIP_PLAYWRIGHT_DEP_INSTALL";
|
|
46
44
|
|
|
47
45
|
const EXIT_CODE_BY_ERROR_CODE = Object.freeze({
|
|
48
46
|
DOCDEX_INSTALLER_CONFIG: 2,
|
|
@@ -232,60 +230,6 @@ function writeAgentInstructions() {
|
|
|
232
230
|
}
|
|
233
231
|
}
|
|
234
232
|
|
|
235
|
-
function resolvePlaywrightPackage() {
|
|
236
|
-
const baseDir = path.join(__dirname, "..");
|
|
237
|
-
try {
|
|
238
|
-
return require.resolve("playwright/package.json", { paths: [baseDir] });
|
|
239
|
-
} catch {}
|
|
240
|
-
try {
|
|
241
|
-
return require.resolve("playwright/package.json");
|
|
242
|
-
} catch {}
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function resolveNpmCommand() {
|
|
247
|
-
const npmExec = process.env.npm_execpath;
|
|
248
|
-
if (npmExec) {
|
|
249
|
-
return { cmd: process.execPath, args: [npmExec] };
|
|
250
|
-
}
|
|
251
|
-
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
252
|
-
return { cmd: npmCmd, args: [] };
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function ensurePlaywrightDependency({ logger = console } = {}) {
|
|
256
|
-
if (process.env[PLAYWRIGHT_SKIP_ENV]) return;
|
|
257
|
-
if (process.env[PLAYWRIGHT_INSTALL_GUARD]) return;
|
|
258
|
-
if (resolvePlaywrightPackage()) return;
|
|
259
|
-
|
|
260
|
-
const rootDir = path.join(__dirname, "..");
|
|
261
|
-
const { cmd, args } = resolveNpmCommand();
|
|
262
|
-
const installArgs = args.concat([
|
|
263
|
-
"install",
|
|
264
|
-
"--no-save",
|
|
265
|
-
"--ignore-scripts",
|
|
266
|
-
"--no-package-lock",
|
|
267
|
-
"--no-audit",
|
|
268
|
-
"--no-fund",
|
|
269
|
-
"playwright"
|
|
270
|
-
]);
|
|
271
|
-
const result = spawnSync(cmd, installArgs, {
|
|
272
|
-
cwd: rootDir,
|
|
273
|
-
stdio: "inherit",
|
|
274
|
-
env: {
|
|
275
|
-
...process.env,
|
|
276
|
-
[PLAYWRIGHT_INSTALL_GUARD]: "1"
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
if (result.error || (typeof result.status === "number" && result.status !== 0)) {
|
|
280
|
-
const message = result.error?.message || `npm exit status ${result.status}`;
|
|
281
|
-
logger.warn?.(`[docdex] Playwright dependency install failed: ${message}`);
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
if (!resolvePlaywrightPackage()) {
|
|
285
|
-
logger.warn?.("[docdex] Playwright dependency still missing after install attempt");
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
233
|
function selectHttpClient(url) {
|
|
290
234
|
try {
|
|
291
235
|
const protocol = new URL(url).protocol;
|
|
@@ -577,13 +521,16 @@ function isLocalInstallRequest({ env, pathModule }) {
|
|
|
577
521
|
return argv.some((arg) => isLikelyLocalInstallArg(arg, pathModule));
|
|
578
522
|
}
|
|
579
523
|
|
|
580
|
-
function shouldPreferLocalInstall({ env, localBinaryPath, pathModule }) {
|
|
524
|
+
function shouldPreferLocalInstall({ env, localBinaryPath, pathModule, localRepoRoot }) {
|
|
581
525
|
if (!localBinaryPath) return false;
|
|
582
526
|
if (parseEnvBool(env?.[LOCAL_FALLBACK_ENV]) === false) return false;
|
|
583
527
|
if (env?.[LOCAL_BINARY_ENV]) return true;
|
|
584
|
-
if (!env?.INIT_CWD) return false;
|
|
585
528
|
if (env?.npm_lifecycle_event !== "postinstall") return false;
|
|
586
|
-
|
|
529
|
+
if (isLocalInstallRequest({ env, pathModule })) return true;
|
|
530
|
+
if (!env?.INIT_CWD || !localRepoRoot) return false;
|
|
531
|
+
const initCwd = pathModule.resolve(env.INIT_CWD);
|
|
532
|
+
const repoRoot = pathModule.resolve(localRepoRoot);
|
|
533
|
+
return initCwd === repoRoot || initCwd.startsWith(`${repoRoot}${pathModule.sep}`);
|
|
587
534
|
}
|
|
588
535
|
|
|
589
536
|
function resolveLocalBinaryCandidate({
|
|
@@ -599,35 +546,16 @@ function resolveLocalBinaryCandidate({
|
|
|
599
546
|
if (fsModule.existsSync(resolved)) return resolved;
|
|
600
547
|
}
|
|
601
548
|
const isWin32 = platform === "win32";
|
|
602
|
-
const mcpName = isWin32 ? "docdex-mcp-server.exe" : "docdex-mcp-server";
|
|
603
549
|
const root = repoRoot || detectLocalRepoRoot({ pathModule, fsModule });
|
|
604
550
|
if (!root) return null;
|
|
605
551
|
const binaryName = platform === "win32" ? "docdexd.exe" : "docdexd";
|
|
606
552
|
const releasePath = pathModule.join(root, "target", "release", binaryName);
|
|
607
|
-
if (fsModule.existsSync(releasePath))
|
|
608
|
-
if (localMcpPresent({ fsModule, pathModule, binaryPath: releasePath, mcpName })) {
|
|
609
|
-
return releasePath;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
553
|
+
if (fsModule.existsSync(releasePath)) return releasePath;
|
|
612
554
|
const debugPath = pathModule.join(root, "target", "debug", binaryName);
|
|
613
|
-
if (fsModule.existsSync(debugPath))
|
|
614
|
-
if (localMcpPresent({ fsModule, pathModule, binaryPath: debugPath, mcpName })) {
|
|
615
|
-
return debugPath;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
555
|
+
if (fsModule.existsSync(debugPath)) return debugPath;
|
|
618
556
|
return null;
|
|
619
557
|
}
|
|
620
558
|
|
|
621
|
-
function localMcpPresent({ fsModule, pathModule, binaryPath, mcpName }) {
|
|
622
|
-
const dir = pathModule.dirname(binaryPath);
|
|
623
|
-
const candidates = [
|
|
624
|
-
pathModule.join(dir, mcpName),
|
|
625
|
-
pathModule.join(pathModule.dirname(dir), "release", mcpName),
|
|
626
|
-
pathModule.join(pathModule.dirname(dir), "debug", mcpName)
|
|
627
|
-
];
|
|
628
|
-
return candidates.some((candidate) => fsModule.existsSync(candidate));
|
|
629
|
-
}
|
|
630
|
-
|
|
631
559
|
async function installFromLocalBinary({
|
|
632
560
|
fsModule,
|
|
633
561
|
pathModule,
|
|
@@ -650,22 +578,6 @@ async function installFromLocalBinary({
|
|
|
650
578
|
if (!isWin32) {
|
|
651
579
|
await fsModule.promises.chmod(destPath, 0o755).catch(() => {});
|
|
652
580
|
}
|
|
653
|
-
const mcpName = isWin32 ? "docdex-mcp-server.exe" : "docdex-mcp-server";
|
|
654
|
-
const mcpCandidates = [
|
|
655
|
-
pathModule.join(pathModule.dirname(binaryPath), mcpName),
|
|
656
|
-
pathModule.join(pathModule.dirname(pathModule.dirname(binaryPath)), "release", mcpName),
|
|
657
|
-
pathModule.join(pathModule.dirname(pathModule.dirname(binaryPath)), "debug", mcpName)
|
|
658
|
-
];
|
|
659
|
-
const mcpSource = mcpCandidates.find((candidate) => fsModule.existsSync(candidate));
|
|
660
|
-
if (mcpSource) {
|
|
661
|
-
const mcpDest = pathModule.join(distDir, mcpName);
|
|
662
|
-
await fsModule.promises.copyFile(mcpSource, mcpDest);
|
|
663
|
-
if (!isWin32) {
|
|
664
|
-
await fsModule.promises.chmod(mcpDest, 0o755).catch(() => {});
|
|
665
|
-
}
|
|
666
|
-
} else {
|
|
667
|
-
logger?.warn?.(`[docdex] local MCP binary not found; expected near ${binaryPath}`);
|
|
668
|
-
}
|
|
669
581
|
const binarySha256 = await sha256FileFn(destPath);
|
|
670
582
|
const metadata = {
|
|
671
583
|
schemaVersion: INSTALL_METADATA_SCHEMA_VERSION,
|
|
@@ -718,7 +630,13 @@ async function maybeInstallLocalFallback({
|
|
|
718
630
|
|
|
719
631
|
const candidate =
|
|
720
632
|
localBinaryPath ||
|
|
721
|
-
resolveLocalBinaryCandidate({
|
|
633
|
+
resolveLocalBinaryCandidate({
|
|
634
|
+
env,
|
|
635
|
+
platform: process.platform,
|
|
636
|
+
pathModule,
|
|
637
|
+
fsModule,
|
|
638
|
+
repoRoot: localRepoRoot
|
|
639
|
+
});
|
|
722
640
|
if (!candidate) return null;
|
|
723
641
|
|
|
724
642
|
return installFromLocalBinary({
|
|
@@ -1074,9 +992,6 @@ function decideInstallAction({
|
|
|
1074
992
|
integrityResult
|
|
1075
993
|
}) {
|
|
1076
994
|
if (!discoveredInstalledState?.binaryPresent) return { outcome: "update", reason: "binary_missing" };
|
|
1077
|
-
if (discoveredInstalledState.mcpBinaryPresent === false) {
|
|
1078
|
-
return { outcome: "update", reason: "mcp_binary_missing" };
|
|
1079
|
-
}
|
|
1080
995
|
|
|
1081
996
|
if (discoveredInstalledState.metadataStatus !== "valid") {
|
|
1082
997
|
return {
|
|
@@ -1111,10 +1026,6 @@ function decideInstallAction({
|
|
|
1111
1026
|
|
|
1112
1027
|
async function discoverInstalledState({ fsModule, pathModule, distDir, platformKey, isWin32 }) {
|
|
1113
1028
|
const binaryPath = pathModule.join(distDir, isWin32 ? "docdexd.exe" : "docdexd");
|
|
1114
|
-
const mcpBinaryPath = pathModule.join(
|
|
1115
|
-
distDir,
|
|
1116
|
-
isWin32 ? "docdex-mcp-server.exe" : "docdex-mcp-server"
|
|
1117
|
-
);
|
|
1118
1029
|
const metadataPath = installMetadataPath(distDir, pathModule);
|
|
1119
1030
|
|
|
1120
1031
|
const existsSync = typeof fsModule?.existsSync === "function" ? fsModule.existsSync.bind(fsModule) : null;
|
|
@@ -1123,7 +1034,6 @@ async function discoverInstalledState({ fsModule, pathModule, distDir, platformK
|
|
|
1123
1034
|
binaryPath,
|
|
1124
1035
|
metadataPath,
|
|
1125
1036
|
binaryPresent: false,
|
|
1126
|
-
mcpBinaryPresent: false,
|
|
1127
1037
|
installedVersion: null,
|
|
1128
1038
|
metadata: null,
|
|
1129
1039
|
metadataStatus: "unavailable",
|
|
@@ -1137,7 +1047,6 @@ async function discoverInstalledState({ fsModule, pathModule, distDir, platformK
|
|
|
1137
1047
|
binaryPath,
|
|
1138
1048
|
metadataPath,
|
|
1139
1049
|
binaryPresent: false,
|
|
1140
|
-
mcpBinaryPresent: false,
|
|
1141
1050
|
installedVersion: null,
|
|
1142
1051
|
metadata: null,
|
|
1143
1052
|
metadataStatus: "missing",
|
|
@@ -1146,13 +1055,11 @@ async function discoverInstalledState({ fsModule, pathModule, distDir, platformK
|
|
|
1146
1055
|
};
|
|
1147
1056
|
}
|
|
1148
1057
|
|
|
1149
|
-
const mcpBinaryPresent = existsSync(mcpBinaryPath);
|
|
1150
1058
|
const metaResult = await readJsonFileIfPossible({ fsModule, filePath: metadataPath });
|
|
1151
1059
|
const meta = metaResult.value;
|
|
1152
1060
|
if (!isValidInstallMetadata(meta)) {
|
|
1153
1061
|
return {
|
|
1154
1062
|
binaryPath,
|
|
1155
|
-
mcpBinaryPresent,
|
|
1156
1063
|
metadataPath,
|
|
1157
1064
|
binaryPresent: true,
|
|
1158
1065
|
installedVersion: typeof meta?.version === "string" ? meta.version : null,
|
|
@@ -1175,7 +1082,6 @@ async function discoverInstalledState({ fsModule, pathModule, distDir, platformK
|
|
|
1175
1082
|
|
|
1176
1083
|
return {
|
|
1177
1084
|
binaryPath,
|
|
1178
|
-
mcpBinaryPresent,
|
|
1179
1085
|
metadataPath,
|
|
1180
1086
|
binaryPresent: true,
|
|
1181
1087
|
installedVersion: meta.version,
|
|
@@ -1249,7 +1155,6 @@ async function determineLocalInstallerOutcome({
|
|
|
1249
1155
|
|
|
1250
1156
|
const shouldVerifyIntegrity =
|
|
1251
1157
|
discoveredInstalledState.binaryPresent &&
|
|
1252
|
-
discoveredInstalledState.mcpBinaryPresent !== false &&
|
|
1253
1158
|
!discoveredInstalledState.platformMismatch &&
|
|
1254
1159
|
discoveredInstalledState.installedVersion === expectedVersion &&
|
|
1255
1160
|
(normalizeSha256Hex(expectedBinarySha256) || discoveredInstalledState.metadataStatus === "valid");
|
|
@@ -1805,6 +1710,7 @@ async function runInstaller(options) {
|
|
|
1805
1710
|
});
|
|
1806
1711
|
|
|
1807
1712
|
const priorRunnable = existsSync ? existsSync(local.binaryPath) : false;
|
|
1713
|
+
const preferLocal = shouldPreferLocalInstall({ env, localBinaryPath, pathModule, localRepoRoot });
|
|
1808
1714
|
|
|
1809
1715
|
const forceLocalBinary = Boolean(env?.[LOCAL_BINARY_ENV]);
|
|
1810
1716
|
if (forceLocalBinary && localBinaryPath) {
|
|
@@ -1825,6 +1731,24 @@ async function runInstaller(options) {
|
|
|
1825
1731
|
return localInstall;
|
|
1826
1732
|
}
|
|
1827
1733
|
|
|
1734
|
+
if (preferLocal) {
|
|
1735
|
+
const localInstall = await installFromLocalBinary({
|
|
1736
|
+
fsModule,
|
|
1737
|
+
pathModule,
|
|
1738
|
+
distDir,
|
|
1739
|
+
binaryPath: localBinaryPath,
|
|
1740
|
+
isWin32,
|
|
1741
|
+
version,
|
|
1742
|
+
platformKey,
|
|
1743
|
+
targetTriple,
|
|
1744
|
+
repoSlug: null,
|
|
1745
|
+
sha256FileFn,
|
|
1746
|
+
writeJsonFileAtomicFn,
|
|
1747
|
+
logger
|
|
1748
|
+
});
|
|
1749
|
+
return localInstall;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1828
1752
|
if (local.outcome === "no-op") {
|
|
1829
1753
|
logger.log("[docdex] Install outcome: no-op");
|
|
1830
1754
|
await cleanupInstallArtifacts({
|
|
@@ -1853,24 +1777,6 @@ async function runInstaller(options) {
|
|
|
1853
1777
|
};
|
|
1854
1778
|
}
|
|
1855
1779
|
|
|
1856
|
-
if (shouldPreferLocalInstall({ env, localBinaryPath, pathModule })) {
|
|
1857
|
-
const localInstall = await installFromLocalBinary({
|
|
1858
|
-
fsModule,
|
|
1859
|
-
pathModule,
|
|
1860
|
-
distDir,
|
|
1861
|
-
binaryPath: localBinaryPath,
|
|
1862
|
-
isWin32,
|
|
1863
|
-
version,
|
|
1864
|
-
platformKey,
|
|
1865
|
-
targetTriple,
|
|
1866
|
-
repoSlug: null,
|
|
1867
|
-
sha256FileFn,
|
|
1868
|
-
writeJsonFileAtomicFn,
|
|
1869
|
-
logger
|
|
1870
|
-
});
|
|
1871
|
-
return localInstall;
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
1780
|
let repoSlug = null;
|
|
1875
1781
|
let archive;
|
|
1876
1782
|
let expectedSha256;
|
|
@@ -2189,18 +2095,14 @@ async function main() {
|
|
|
2189
2095
|
try {
|
|
2190
2096
|
await runPostInstallSetup({ binaryPath: result?.binaryPath });
|
|
2191
2097
|
} catch (err) {
|
|
2192
|
-
console.warn(`[docdex] postinstall setup
|
|
2098
|
+
console.warn(`[docdex] postinstall setup failed: ${err?.message || err}`);
|
|
2099
|
+
throw err;
|
|
2193
2100
|
}
|
|
2194
2101
|
try {
|
|
2195
2102
|
writeAgentInstructions();
|
|
2196
2103
|
} catch (err) {
|
|
2197
2104
|
console.warn(`[docdex] agent instructions skipped: ${err?.message || err}`);
|
|
2198
2105
|
}
|
|
2199
|
-
try {
|
|
2200
|
-
ensurePlaywrightDependency();
|
|
2201
|
-
} catch (err) {
|
|
2202
|
-
console.warn(`[docdex] playwright dependency check skipped: ${err?.message || err}`);
|
|
2203
|
-
}
|
|
2204
2106
|
printPostInstallBanner();
|
|
2205
2107
|
}
|
|
2206
2108
|
|
|
@@ -2230,7 +2132,7 @@ function printPostInstallBanner() {
|
|
|
2230
2132
|
"\x1b[32mDocdex installed successfully!\x1b[0m",
|
|
2231
2133
|
"\x1b[41m\x1b[97m IMPORTANT \x1b[0m \x1b[33mNext step:\x1b[0m run \x1b[32m`docdex setup`\x1b[0m to complete the installation.",
|
|
2232
2134
|
"\x1b[33mSetup:\x1b[0m configures Ollama/models + browser.",
|
|
2233
|
-
"\x1b[34mTip:\x1b[0m after setup,
|
|
2135
|
+
"\x1b[34mTip:\x1b[0m after setup, the daemon should auto-start; if not, run \x1b[36m`docdexd daemon`\x1b[0m"
|
|
2234
2136
|
];
|
|
2235
2137
|
width = Math.max(72, content.reduce((max, line) => Math.max(max, stripAnsi(line).length), 0));
|
|
2236
2138
|
const padLine = (text) => {
|