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/CHANGELOG.md
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Unreleased
|
|
4
|
+
- Remove legacy stdio MCP (`docdexd mcp` / `docdex-mcp-server`); MCP is served only over HTTP/SSE.
|
|
5
|
+
|
|
6
|
+
## 0.2.23
|
|
4
7
|
- Add Smithery session config schema metadata (titles/descriptions, defaults, example config) for local MCP sessions.
|
|
5
8
|
- Enrich MCP tools with titles, descriptions, parameter descriptions, and annotations to improve Smithery scoring.
|
|
6
9
|
- Expose MCP prompts and resources (with titles/mime types/annotations) for onboarding, incident triage, and refactor planning.
|
|
10
|
+
- Switch web scraping to Chromium-only installs under `~/.docdex/state/bin/chromium/` and remove legacy browser tooling.
|
|
7
11
|
|
|
8
12
|
## 0.2.21
|
|
9
13
|
- Prompt for npm updates at CLI start (TTY-only, opt-out via `DOCDEX_UPDATE_CHECK=0`).
|
|
10
|
-
- Export bundled Playwright fetcher for daemon startup (launchd/systemd/schtasks + immediate spawn).
|
|
11
|
-
- Pass `DOCDEX_PLAYWRIGHT_FETCHER` in the npm wrapper when launching the daemon.
|
|
12
14
|
|
|
13
15
|
## 0.2.19
|
|
14
|
-
- Playwright issue fix
|
|
15
16
|
- Agents md adding command manually
|
|
16
17
|
- Agents md append repeat fix
|
|
17
18
|
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
- Publish npm wrapper with the latest MCP-compliant binary.
|
|
39
40
|
|
|
40
41
|
## 0.1.5
|
|
41
|
-
- Publish the MCP-enabled CLI wrapper
|
|
42
|
+
- Publish the MCP-enabled CLI wrapper and align docs with MCP mode.
|
|
42
43
|
- Keep npm version in sync with the MCP release for binary downloads.
|
|
43
44
|
|
|
44
45
|
## 0.1.4
|
package/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
[](https://docdex.org)
|
|
2
|
-
[](https://smithery.ai/server/bekirdag/docdex)
|
|
3
2
|

|
|
4
3
|

|
|
5
4
|

|
|
@@ -55,7 +54,7 @@ npm i -g docdex
|
|
|
55
54
|
|
|
56
55
|
### 2. Auto-Configuration
|
|
57
56
|
|
|
58
|
-
If you have any of the following clients installed, Docdex automatically configures them to use the local MCP
|
|
57
|
+
If you have any of the following clients installed, Docdex automatically configures them to use the local MCP endpoint (daemon HTTP/SSE):
|
|
59
58
|
|
|
60
59
|
> **Claude Desktop, Cursor, Windsurf, Cline, Roo Code, Continue, VS Code, PearAI, Void, Zed, Codex.**
|
|
61
60
|
|
|
@@ -79,7 +78,7 @@ docdexd index --repo /path/to/my-project
|
|
|
79
78
|
Start the shared server. This handles HTTP requests and MCP connections.
|
|
80
79
|
|
|
81
80
|
```bash
|
|
82
|
-
docdexd daemon --
|
|
81
|
+
docdexd daemon --host 127.0.0.1 --port 28491
|
|
83
82
|
|
|
84
83
|
```
|
|
85
84
|
|
|
@@ -109,6 +108,8 @@ flowchart LR
|
|
|
109
108
|
|
|
110
109
|
```
|
|
111
110
|
|
|
111
|
+
Use the daemon HTTP/SSE endpoint.
|
|
112
|
+
|
|
112
113
|
### Manual Configuration
|
|
113
114
|
|
|
114
115
|
If you need to configure your client manually:
|
|
@@ -119,7 +120,7 @@ If you need to configure your client manually:
|
|
|
119
120
|
{
|
|
120
121
|
"mcpServers": {
|
|
121
122
|
"docdex": {
|
|
122
|
-
"url": "http://localhost:
|
|
123
|
+
"url": "http://localhost:28491/v1/mcp/sse"
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
}
|
|
@@ -130,7 +131,7 @@ If you need to configure your client manually:
|
|
|
130
131
|
|
|
131
132
|
```toml
|
|
132
133
|
[mcp_servers]
|
|
133
|
-
docdex = { url = "http://localhost:
|
|
134
|
+
docdex = { url = "http://localhost:28491/v1/mcp" }
|
|
134
135
|
|
|
135
136
|
```
|
|
136
137
|
|
|
@@ -144,10 +145,10 @@ Don't just find the string "addressGenerator"; find the **definition** and what
|
|
|
144
145
|
|
|
145
146
|
```bash
|
|
146
147
|
# Find definition
|
|
147
|
-
curl "http://127.0.0.1:
|
|
148
|
+
curl "http://127.0.0.1:28491/v1/ast?name=addressGenerator&pathPrefix=src"
|
|
148
149
|
|
|
149
150
|
# Track downstream impact (what breaks if I change this?)
|
|
150
|
-
curl "http://127.0.0.1:
|
|
151
|
+
curl "http://127.0.0.1:28491/v1/graph/impact?file=src/app.ts&maxDepth=3"
|
|
151
152
|
|
|
152
153
|
```
|
|
153
154
|
|
|
@@ -182,7 +183,7 @@ Docdex uses Ollama for embeddings and optional local chat.
|
|
|
182
183
|
* **Manual:** Ensure `nomic-embed-text` is pulled in Ollama (`ollama pull nomic-embed-text`).
|
|
183
184
|
* **Custom URL:**
|
|
184
185
|
```bash
|
|
185
|
-
DOCDEX_OLLAMA_BASE_URL=http://127.0.0.1:11434 docdexd daemon
|
|
186
|
+
DOCDEX_OLLAMA_BASE_URL=http://127.0.0.1:11434 docdexd daemon --host 127.0.0.1 --port 28491
|
|
186
187
|
|
|
187
188
|
```
|
|
188
189
|
|
|
@@ -196,23 +197,27 @@ Docdex runs as a local daemon serving:
|
|
|
196
197
|
|
|
197
198
|
* **CLI Commands:** `docdexd chat`
|
|
198
199
|
* **HTTP API:** `/search`, `/v1/ast`, `/v1/graph/impact`
|
|
199
|
-
* **MCP Endpoints:** `/v1/mcp` and `/sse`
|
|
200
|
+
* **MCP Endpoints:** `/v1/mcp` and `/v1/mcp/sse`
|
|
200
201
|
|
|
201
202
|
### Multi-Repo Setup
|
|
202
203
|
|
|
203
204
|
Run a single daemon and mount additional repos on demand.
|
|
204
205
|
|
|
205
206
|
```bash
|
|
206
|
-
docdexd daemon --
|
|
207
|
+
docdexd daemon --port 28491
|
|
208
|
+
|
|
209
|
+
# Mount repos and capture repo_id values
|
|
210
|
+
curl -X POST "http://127.0.0.1:28491/v1/initialize" \
|
|
211
|
+
-H "Content-Type: application/json" \
|
|
212
|
+
-d '{"rootUri":"file:///path/to/repo-a"}'
|
|
207
213
|
|
|
208
|
-
|
|
209
|
-
curl -X POST "http://127.0.0.1:3210/v1/initialize" \
|
|
214
|
+
curl -X POST "http://127.0.0.1:28491/v1/initialize" \
|
|
210
215
|
-H "Content-Type: application/json" \
|
|
211
216
|
-d '{"rootUri":"file:///path/to/repo-b"}'
|
|
212
217
|
```
|
|
213
218
|
|
|
214
219
|
Notes:
|
|
215
|
-
- When more than one repo is mounted, include `x-docdex-repo-id: <sha256>` on HTTP requests.
|
|
220
|
+
- When more than one repo is mounted (or the daemon starts without a default repo), include `x-docdex-repo-id: <sha256>` on HTTP requests.
|
|
216
221
|
- MCP sessions bind to the repo provided in `initialize.rootUri` and reuse that repo automatically.
|
|
217
222
|
|
|
218
223
|
### Security
|
|
@@ -225,7 +230,6 @@ Notes:
|
|
|
225
230
|
|
|
226
231
|
## 📚 Learn More
|
|
227
232
|
|
|
228
|
-
* **Smithery:** [View on Smithery.ai](https://smithery.ai/server/@bekirdag/docdex)
|
|
229
233
|
* **Detailed Usage:** `docs/usage.md`
|
|
230
234
|
* **API Reference:** `docs/http_api.md`
|
|
231
235
|
* **MCP Specs:** `docs/mcp/errors.md`
|
package/assets/agents.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## Priority Directives (Mandatory)
|
|
2
|
+
|
|
3
|
+
- Always use both memory lobes early: docdex_get_profile + docdex_memory_recall; save new facts via docdex_memory_save and new preferences via docdex_save_preference.
|
|
4
|
+
- Maintain agent-specific profile memory: use your agent_id with docdex_get_profile/docdex_save_preference; store generic self-knowledge (role, tooling, preferences).
|
|
5
|
+
- Use impact analysis for every code change: call HTTP /v1/graph/impact?file=... and account for inbound/outbound deps; if unavailable, state it and proceed cautiously.
|
|
6
|
+
- Apply DAG reasoning for planning: prefer dependency graph facts (impact results and /v1/dag/export) to choose the right change order and scope.
|
|
7
|
+
- Use Docdex tools intentionally: docdex_search/symbols/ast for repo truth; docdex_stats/files/repo_inspect/index for index health.
|
|
8
|
+
- When you do not know something, run docdex_web_research (force_web=true). Web research is encouraged by default for non-repo facts and external APIs.
|
|
9
|
+
|
|
1
10
|
# Docdex Agent Usage Instructions
|
|
2
11
|
|
|
3
12
|
> Context for AI Agents: Docdex is your local-first Dual-Lobe Memory and Code Intelligence daemon. Unlike simple vector stores, it provides structural understanding of code (AST/Graph), persistent behavioral profiles (Agent Memory), and gated web enrichment, all strictly scoped to the local machine.
|
|
@@ -33,6 +42,7 @@ Docdex v2.1 introduces a strict separation between "facts" and "preferences." Us
|
|
|
33
42
|
- Content: Your persona, user preferences, coding style, tooling constraints.
|
|
34
43
|
- Example: "Always use Zod for validation," or "User prefers strict TypeScript types."
|
|
35
44
|
- Tools: docdex_save_preference, docdex_get_profile
|
|
45
|
+
- Agent-specific: Each agent should use its own agent_id and store generic self-knowledge (role, tooling, preferences).
|
|
36
46
|
- Usage: Use this to "learn" from corrections. If a user corrects your style, save it here so you do not repeat the mistake in a different repo.
|
|
37
47
|
|
|
38
48
|
## Tool Capabilities (MCP & HTTP)
|
|
@@ -66,6 +76,17 @@ Precision tools for structural analysis. Do not rely on text search for definiti
|
|
|
66
76
|
| docdex_save_preference | Store a global user preference (Style, Tooling, Constraint). |
|
|
67
77
|
| docdex_get_profile | Retrieve global preferences. |
|
|
68
78
|
|
|
79
|
+
## Quick Tool Map (Often Missed)
|
|
80
|
+
|
|
81
|
+
- docdex_files: List indexed docs with rel_path/doc_id/token_estimate; use to verify indexing coverage.
|
|
82
|
+
- docdex_stats: Show index size, state dir, and last update time.
|
|
83
|
+
- docdex_repo_inspect: Confirm normalized repo root and repo identity mapping.
|
|
84
|
+
- docdex_index: Reindex the full repo or ingest specific files when stale.
|
|
85
|
+
- docdex_search diff: Limit search to working tree, staged, or ref ranges; filter by paths.
|
|
86
|
+
- docdex_web_research knobs: force_web, skip_local_search, repo_only, no_cache, web_limit, llm_filter_local_results, llm_model.
|
|
87
|
+
- HTTP /v1/initialize: Bind a default repo root for MCP when clients omit project_root.
|
|
88
|
+
- HTTP /v1/dag/export: Export the dependency graph for external analysis.
|
|
89
|
+
|
|
69
90
|
## Interaction Patterns
|
|
70
91
|
|
|
71
92
|
### 1. Reasoning Workflow
|
|
@@ -93,6 +114,21 @@ If the user asks: "Safe to delete getUser?"
|
|
|
93
114
|
- Action: Call GET /v1/graph/impact?file=src/user.ts
|
|
94
115
|
- Output: Analyze the inbound edges. If the list is not empty, it is unsafe.
|
|
95
116
|
|
|
117
|
+
### 4. Non-Repo Real-World Queries (Web First)
|
|
118
|
+
|
|
119
|
+
If the user asks a non-repo, real-world question (weather, news, general facts), immediately call docdex_web_research with force_web=true.
|
|
120
|
+
- Resolve relative dates ("yesterday", "last week") using system time by default.
|
|
121
|
+
- Do not run docdex_search unless the user explicitly wants repo-local context.
|
|
122
|
+
- Assume web access is allowed unless the user forbids it; if the web call fails, report the failure and ask for a source or permission.
|
|
123
|
+
|
|
124
|
+
### 5. Failure Handling (Missing Results or Errors)
|
|
125
|
+
|
|
126
|
+
- Ensure project_root or repo_path is set, or call /v1/initialize to bind a default root.
|
|
127
|
+
- Use docdex_repo_inspect to confirm repo identity and normalized root.
|
|
128
|
+
- Use docdex_stats and docdex_files to check whether the index exists and contains files.
|
|
129
|
+
- Reindex with docdex_index (or docdexd index) if the index is stale or empty.
|
|
130
|
+
- Add a repo-local .docdexignore for large generated artifacts or local caches when indexing is slow.
|
|
131
|
+
|
|
96
132
|
## Operational Context
|
|
97
133
|
|
|
98
134
|
### Repository Identification
|
package/bin/docdex.js
CHANGED
|
@@ -29,6 +29,12 @@ function printLines(lines, { stderr } = {}) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
function envBool(value) {
|
|
33
|
+
if (!value) return false;
|
|
34
|
+
const normalized = String(value).trim().toLowerCase();
|
|
35
|
+
return ["1", "true", "t", "yes", "y", "on"].includes(normalized);
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
function readInstallMetadata({ fsModule, pathModule, basePath }) {
|
|
33
39
|
if (!fsModule || typeof fsModule.readFileSync !== "function") return null;
|
|
34
40
|
const metadataPath = pathModule.join(basePath, "docdexd-install.json");
|
|
@@ -178,10 +184,6 @@ async function run() {
|
|
|
178
184
|
basePath,
|
|
179
185
|
process.platform === "win32" ? "docdexd.exe" : "docdexd"
|
|
180
186
|
);
|
|
181
|
-
const mcpBinaryPath = path.join(
|
|
182
|
-
basePath,
|
|
183
|
-
process.platform === "win32" ? "docdex-mcp-server.exe" : "docdex-mcp-server"
|
|
184
|
-
);
|
|
185
187
|
|
|
186
188
|
if (!fs.existsSync(binaryPath)) {
|
|
187
189
|
console.error(`[docdex] Missing binary for ${platformKey}. Try reinstalling or set DOCDEX_DOWNLOAD_REPO to a repo with release assets.`);
|
|
@@ -202,13 +204,6 @@ async function run() {
|
|
|
202
204
|
});
|
|
203
205
|
|
|
204
206
|
const env = { ...process.env };
|
|
205
|
-
if (!env.DOCDEX_MCP_SERVER_BIN && fs.existsSync(mcpBinaryPath)) {
|
|
206
|
-
env.DOCDEX_MCP_SERVER_BIN = mcpBinaryPath;
|
|
207
|
-
}
|
|
208
|
-
const fetcherPath = path.join(__dirname, "..", "lib", "playwright_fetch.js");
|
|
209
|
-
if (!env.DOCDEX_PLAYWRIGHT_FETCHER && fs.existsSync(fetcherPath)) {
|
|
210
|
-
env.DOCDEX_PLAYWRIGHT_FETCHER = fetcherPath;
|
|
211
|
-
}
|
|
212
207
|
const child = spawn(binaryPath, process.argv.slice(2), { stdio: "inherit", env });
|
|
213
208
|
child.on("exit", (code) => process.exit(code ?? 1));
|
|
214
209
|
child.on("error", (err) => {
|
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) => {
|