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/postinstall_setup.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
const fs = require("node:fs");
|
|
5
5
|
const net = require("node:net");
|
|
6
|
+
const http = require("node:http");
|
|
6
7
|
const os = require("node:os");
|
|
7
8
|
const path = require("node:path");
|
|
8
9
|
const readline = require("node:readline");
|
|
@@ -12,8 +13,11 @@ const { spawn, spawnSync } = require("node:child_process");
|
|
|
12
13
|
const { detectPlatformKey, UnsupportedPlatformError } = require("./platform");
|
|
13
14
|
|
|
14
15
|
const DEFAULT_HOST = "127.0.0.1";
|
|
15
|
-
const
|
|
16
|
-
const
|
|
16
|
+
const DEFAULT_DAEMON_PORT = 28491;
|
|
17
|
+
const DAEMON_HEALTH_TIMEOUT_MS = 8000;
|
|
18
|
+
const DAEMON_HEALTH_REQUEST_TIMEOUT_MS = 1000;
|
|
19
|
+
const DAEMON_HEALTH_POLL_INTERVAL_MS = 200;
|
|
20
|
+
const DAEMON_HEALTH_PATH = "/healthz";
|
|
17
21
|
const STARTUP_FAILURE_MARKER = "startup_registration_failed.json";
|
|
18
22
|
const DEFAULT_OLLAMA_MODEL = "nomic-embed-text";
|
|
19
23
|
const DEFAULT_OLLAMA_CHAT_MODEL = "phi3.5:3.8b";
|
|
@@ -39,6 +43,17 @@ function setupPendingPath() {
|
|
|
39
43
|
return path.join(stateDir(), SETUP_PENDING_MARKER);
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
function daemonLockPaths() {
|
|
47
|
+
const root = path.join(os.homedir(), ".docdex");
|
|
48
|
+
const paths = [];
|
|
49
|
+
if (process.env.DOCDEX_DAEMON_LOCK_PATH) {
|
|
50
|
+
paths.push(process.env.DOCDEX_DAEMON_LOCK_PATH);
|
|
51
|
+
}
|
|
52
|
+
paths.push(path.join(root, "locks", "daemon.lock"));
|
|
53
|
+
paths.push(path.join(root, "daemon.lock"));
|
|
54
|
+
return Array.from(new Set(paths.filter(Boolean)));
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
function configUrlForPort(port) {
|
|
43
58
|
return `http://localhost:${port}/sse`;
|
|
44
59
|
}
|
|
@@ -59,22 +74,51 @@ function isPortAvailable(port, host) {
|
|
|
59
74
|
});
|
|
60
75
|
}
|
|
61
76
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
function sleep(ms) {
|
|
78
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function checkDaemonHealth({ host, port, timeoutMs = DAEMON_HEALTH_REQUEST_TIMEOUT_MS }) {
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
const req = http.request(
|
|
84
|
+
{
|
|
85
|
+
host,
|
|
86
|
+
port,
|
|
87
|
+
path: DAEMON_HEALTH_PATH,
|
|
88
|
+
method: "GET",
|
|
89
|
+
timeout: timeoutMs
|
|
90
|
+
},
|
|
91
|
+
(res) => {
|
|
92
|
+
let body = "";
|
|
93
|
+
res.setEncoding("utf8");
|
|
94
|
+
res.on("data", (chunk) => {
|
|
95
|
+
body += chunk;
|
|
96
|
+
});
|
|
97
|
+
res.on("end", () => {
|
|
98
|
+
resolve(res.statusCode === 200 && body.trim() === "ok");
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
req.on("timeout", () => {
|
|
103
|
+
req.destroy();
|
|
104
|
+
resolve(false);
|
|
73
105
|
});
|
|
74
|
-
|
|
106
|
+
req.on("error", () => resolve(false));
|
|
107
|
+
req.end();
|
|
75
108
|
});
|
|
76
109
|
}
|
|
77
110
|
|
|
111
|
+
async function waitForDaemonHealthy({ host, port, timeoutMs = DAEMON_HEALTH_TIMEOUT_MS }) {
|
|
112
|
+
const deadline = Date.now() + timeoutMs;
|
|
113
|
+
while (Date.now() < deadline) {
|
|
114
|
+
if (await checkDaemonHealth({ host, port })) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
await sleep(DAEMON_HEALTH_POLL_INTERVAL_MS);
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
78
122
|
function parseServerBind(contents) {
|
|
79
123
|
let inServer = false;
|
|
80
124
|
const lines = contents.split(/\r?\n/);
|
|
@@ -91,6 +135,125 @@ function parseServerBind(contents) {
|
|
|
91
135
|
return null;
|
|
92
136
|
}
|
|
93
137
|
|
|
138
|
+
function stopDaemonService({ logger } = {}) {
|
|
139
|
+
if (process.platform === "darwin") {
|
|
140
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
141
|
+
const domain = uid != null ? `gui/${uid}` : null;
|
|
142
|
+
const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", "com.docdex.daemon.plist");
|
|
143
|
+
const bootoutByLabel = domain
|
|
144
|
+
? spawnSync("launchctl", ["bootout", domain, "com.docdex.daemon"])
|
|
145
|
+
: spawnSync("launchctl", ["bootout", "com.docdex.daemon"]);
|
|
146
|
+
const bootoutByPath = domain
|
|
147
|
+
? spawnSync("launchctl", ["bootout", domain, plistPath])
|
|
148
|
+
: spawnSync("launchctl", ["bootout", plistPath]);
|
|
149
|
+
const fallback = spawnSync("launchctl", ["unload", "-w", plistPath]);
|
|
150
|
+
spawnSync("launchctl", ["remove", "com.docdex.daemon"]);
|
|
151
|
+
if (bootoutByLabel.status === 0 || bootoutByPath.status === 0 || fallback.status === 0) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
logger?.warn?.(
|
|
155
|
+
`[docdex] launchctl stop failed: ${bootoutByLabel.stderr || bootoutByPath.stderr || fallback.stderr || "unknown error"}`
|
|
156
|
+
);
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
if (process.platform === "linux") {
|
|
160
|
+
const stop = spawnSync("systemctl", ["--user", "stop", "docdexd.service"]);
|
|
161
|
+
if (stop.status === 0) return true;
|
|
162
|
+
logger?.warn?.(`[docdex] systemd stop failed: ${stop.stderr || "unknown error"}`);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
if (process.platform === "win32") {
|
|
166
|
+
const stop = spawnSync("schtasks", ["/End", "/TN", "Docdex Daemon"]);
|
|
167
|
+
if (stop.status === 0) return true;
|
|
168
|
+
logger?.warn?.(`[docdex] schtasks stop failed: ${stop.stderr || "unknown error"}`);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function startDaemonService({ logger } = {}) {
|
|
175
|
+
if (process.platform === "darwin") {
|
|
176
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
177
|
+
const domain = uid != null ? `gui/${uid}` : null;
|
|
178
|
+
const kickstart = domain
|
|
179
|
+
? spawnSync("launchctl", ["kickstart", "-k", `${domain}/com.docdex.daemon`])
|
|
180
|
+
: spawnSync("launchctl", ["kickstart", "-k", "com.docdex.daemon"]);
|
|
181
|
+
if (kickstart.status === 0) return true;
|
|
182
|
+
const start = spawnSync("launchctl", ["start", "com.docdex.daemon"]);
|
|
183
|
+
if (start.status === 0) return true;
|
|
184
|
+
logger?.warn?.(`[docdex] launchctl start failed: ${kickstart.stderr || start.stderr || "unknown error"}`);
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
if (process.platform === "linux") {
|
|
188
|
+
const start = spawnSync("systemctl", ["--user", "restart", "docdexd.service"]);
|
|
189
|
+
if (start.status === 0) return true;
|
|
190
|
+
logger?.warn?.(`[docdex] systemd start failed: ${start.stderr || "unknown error"}`);
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
if (process.platform === "win32") {
|
|
194
|
+
const run = spawnSync("schtasks", ["/Run", "/TN", "Docdex Daemon"]);
|
|
195
|
+
if (run.status === 0) return true;
|
|
196
|
+
logger?.warn?.(`[docdex] schtasks run failed: ${run.stderr || "unknown error"}`);
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function stopDaemonByName({ logger } = {}) {
|
|
203
|
+
if (process.platform === "win32") {
|
|
204
|
+
const result = spawnSync("taskkill", ["/IM", "docdexd.exe", "/T", "/F"]);
|
|
205
|
+
if (result?.error?.code === "ENOENT") return false;
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
const result = spawnSync("pkill", ["-TERM", "-x", "docdexd"]);
|
|
209
|
+
if (result?.error?.code === "ENOENT") {
|
|
210
|
+
spawnSync("killall", ["-TERM", "docdexd"]);
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
spawnSync("pkill", ["-TERM", "-f", "docdexd"]);
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function clearDaemonLocks() {
|
|
218
|
+
let removed = false;
|
|
219
|
+
for (const lockPath of daemonLockPaths()) {
|
|
220
|
+
if (!lockPath || !fs.existsSync(lockPath)) continue;
|
|
221
|
+
try {
|
|
222
|
+
const resolved = path.resolve(lockPath);
|
|
223
|
+
const home = path.resolve(os.homedir());
|
|
224
|
+
if (!resolved.startsWith(home + path.sep)) continue;
|
|
225
|
+
fs.unlinkSync(lockPath);
|
|
226
|
+
removed = true;
|
|
227
|
+
} catch {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return removed;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function stopDaemonFromLock({ logger } = {}) {
|
|
235
|
+
let stopped = false;
|
|
236
|
+
for (const lockPath of daemonLockPaths()) {
|
|
237
|
+
if (!lockPath || !fs.existsSync(lockPath)) continue;
|
|
238
|
+
try {
|
|
239
|
+
const raw = fs.readFileSync(lockPath, "utf8");
|
|
240
|
+
if (!raw.trim()) continue;
|
|
241
|
+
const payload = JSON.parse(raw);
|
|
242
|
+
const pid = Number(payload?.pid);
|
|
243
|
+
if (!Number.isFinite(pid) || pid <= 0) continue;
|
|
244
|
+
try {
|
|
245
|
+
process.kill(pid, "SIGTERM");
|
|
246
|
+
stopped = true;
|
|
247
|
+
} catch (err) {
|
|
248
|
+
logger?.warn?.(`[docdex] failed to stop daemon pid ${pid}: ${err?.message || err}`);
|
|
249
|
+
}
|
|
250
|
+
} catch {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return stopped;
|
|
255
|
+
}
|
|
256
|
+
|
|
94
257
|
function upsertServerConfig(contents, httpBindAddr) {
|
|
95
258
|
const lines = contents.split(/\r?\n/);
|
|
96
259
|
const output = [];
|
|
@@ -400,7 +563,7 @@ function upsertClaudeInstructions(pathname, instructions) {
|
|
|
400
563
|
return true;
|
|
401
564
|
}
|
|
402
565
|
|
|
403
|
-
function
|
|
566
|
+
function upsertContinueJsonInstructions(pathname, instructions) {
|
|
404
567
|
const { value } = readJson(pathname);
|
|
405
568
|
if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
|
|
406
569
|
const merged = mergeInstructionText(value.systemMessage, instructions);
|
|
@@ -410,6 +573,198 @@ function upsertContinueInstructions(pathname, instructions) {
|
|
|
410
573
|
return true;
|
|
411
574
|
}
|
|
412
575
|
|
|
576
|
+
function countLeadingWhitespace(line) {
|
|
577
|
+
const match = line.match(/^\s*/);
|
|
578
|
+
return match ? match[0].length : 0;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function isYamlTopLevelKey(line, baseIndent) {
|
|
582
|
+
const indent = countLeadingWhitespace(line);
|
|
583
|
+
if (indent > baseIndent) return false;
|
|
584
|
+
const trimmed = line.trimStart();
|
|
585
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) return false;
|
|
586
|
+
return trimmed.includes(":");
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function hasYamlContent(lines) {
|
|
590
|
+
return lines.some((line) => {
|
|
591
|
+
const trimmed = line.trim();
|
|
592
|
+
return trimmed && !trimmed.startsWith("#");
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function splitInlineYamlList(value) {
|
|
597
|
+
const trimmed = String(value || "").trim();
|
|
598
|
+
if (trimmed === "[]") return [];
|
|
599
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
|
|
600
|
+
const inner = trimmed.slice(1, -1);
|
|
601
|
+
const items = [];
|
|
602
|
+
let current = "";
|
|
603
|
+
let inSingle = false;
|
|
604
|
+
let inDouble = false;
|
|
605
|
+
let escaped = false;
|
|
606
|
+
for (const ch of inner) {
|
|
607
|
+
if (escaped) {
|
|
608
|
+
current += ch;
|
|
609
|
+
escaped = false;
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
if (ch === "\\\\") {
|
|
613
|
+
escaped = true;
|
|
614
|
+
current += ch;
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
if (ch === "'" && !inDouble) {
|
|
618
|
+
inSingle = !inSingle;
|
|
619
|
+
current += ch;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (ch === '"' && !inSingle) {
|
|
623
|
+
inDouble = !inDouble;
|
|
624
|
+
current += ch;
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (ch === "," && !inSingle && !inDouble) {
|
|
628
|
+
const next = current.trim();
|
|
629
|
+
if (next) items.push(next);
|
|
630
|
+
current = "";
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
current += ch;
|
|
634
|
+
}
|
|
635
|
+
const next = current.trim();
|
|
636
|
+
if (next) items.push(next);
|
|
637
|
+
return items;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function inlineRulesToItems(value, itemIndent) {
|
|
641
|
+
const trimmed = String(value || "").trim();
|
|
642
|
+
if (!trimmed) return [];
|
|
643
|
+
const prefix = " ".repeat(itemIndent);
|
|
644
|
+
const split = splitInlineYamlList(trimmed);
|
|
645
|
+
if (split) {
|
|
646
|
+
return split.map((item) => [`${prefix}- ${item}`]).filter((item) => item[0].trim() !== `${prefix}-`);
|
|
647
|
+
}
|
|
648
|
+
return [[`${prefix}- ${trimmed}`]];
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function buildYamlRuleBlock(itemIndent, instructions) {
|
|
652
|
+
const prefix = " ".repeat(itemIndent);
|
|
653
|
+
const contentPrefix = " ".repeat(itemIndent + 2);
|
|
654
|
+
const lines = [`${prefix}- |`];
|
|
655
|
+
for (const line of String(instructions).split(/\r?\n/)) {
|
|
656
|
+
lines.push(`${contentPrefix}${line}`);
|
|
657
|
+
}
|
|
658
|
+
return lines;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function rewriteContinueYamlRules(source, instructions, addDocdex) {
|
|
662
|
+
const lines = String(source || "").split(/\r?\n/);
|
|
663
|
+
const ruleLineRe = /^(\s*)rules\s*:(.*)$/;
|
|
664
|
+
let rulesIndex = -1;
|
|
665
|
+
let rulesIndent = 0;
|
|
666
|
+
let rulesInline = "";
|
|
667
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
668
|
+
const match = lines[i].match(ruleLineRe);
|
|
669
|
+
if (!match) continue;
|
|
670
|
+
rulesIndex = i;
|
|
671
|
+
rulesIndent = match[1]?.length || 0;
|
|
672
|
+
rulesInline = (match[2] || "").trim();
|
|
673
|
+
if (rulesInline.includes("#")) {
|
|
674
|
+
rulesInline = rulesInline.split("#")[0].trim();
|
|
675
|
+
}
|
|
676
|
+
if (rulesInline.startsWith("#")) rulesInline = "";
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (rulesIndex === -1) {
|
|
681
|
+
if (!addDocdex) return null;
|
|
682
|
+
const trimmed = String(source || "").trimEnd();
|
|
683
|
+
const docdexBlock = buildYamlRuleBlock(2, instructions);
|
|
684
|
+
const prefix = trimmed ? `${trimmed}\n\n` : "";
|
|
685
|
+
return `${prefix}rules:\n${docdexBlock.join("\n")}`;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
let endIndex = lines.length;
|
|
689
|
+
for (let i = rulesIndex + 1; i < lines.length; i += 1) {
|
|
690
|
+
if (isYamlTopLevelKey(lines[i], rulesIndent)) {
|
|
691
|
+
endIndex = i;
|
|
692
|
+
break;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
const blockLines = lines.slice(rulesIndex + 1, endIndex);
|
|
696
|
+
const preLines = [];
|
|
697
|
+
const items = [];
|
|
698
|
+
let currentItem = [];
|
|
699
|
+
let itemIndent = null;
|
|
700
|
+
let startedItems = false;
|
|
701
|
+
|
|
702
|
+
for (const line of blockLines) {
|
|
703
|
+
const trimmed = line.trimStart();
|
|
704
|
+
const indent = countLeadingWhitespace(line);
|
|
705
|
+
const isItem = trimmed.startsWith("-") && indent > rulesIndent;
|
|
706
|
+
if (isItem) {
|
|
707
|
+
if (itemIndent == null) itemIndent = indent;
|
|
708
|
+
if (indent === itemIndent) {
|
|
709
|
+
if (startedItems && currentItem.length) {
|
|
710
|
+
items.push(currentItem);
|
|
711
|
+
currentItem = [];
|
|
712
|
+
}
|
|
713
|
+
startedItems = true;
|
|
714
|
+
}
|
|
715
|
+
currentItem.push(line);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
if (startedItems) {
|
|
719
|
+
currentItem.push(line);
|
|
720
|
+
} else {
|
|
721
|
+
preLines.push(line);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (currentItem.length) items.push(currentItem);
|
|
725
|
+
|
|
726
|
+
const inferredIndent = itemIndent == null ? rulesIndent + 2 : itemIndent;
|
|
727
|
+
if (!items.length && rulesInline) {
|
|
728
|
+
items.push(...inlineRulesToItems(rulesInline, inferredIndent));
|
|
729
|
+
rulesInline = "";
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const keptItems = items.filter((item) => {
|
|
733
|
+
const text = item.join("\n");
|
|
734
|
+
return !(text.includes(DOCDEX_INFO_START_PREFIX) && text.includes(DOCDEX_INFO_END));
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
if (addDocdex) {
|
|
738
|
+
keptItems.push(buildYamlRuleBlock(inferredIndent, instructions));
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const removeRulesBlock =
|
|
742
|
+
!addDocdex && !keptItems.length && !hasYamlContent(preLines) && !rulesInline;
|
|
743
|
+
|
|
744
|
+
const output = [];
|
|
745
|
+
output.push(...lines.slice(0, rulesIndex));
|
|
746
|
+
if (!removeRulesBlock) {
|
|
747
|
+
output.push(`${" ".repeat(rulesIndent)}rules:`);
|
|
748
|
+
output.push(...preLines);
|
|
749
|
+
for (const item of keptItems) {
|
|
750
|
+
output.push(...item);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
output.push(...lines.slice(endIndex));
|
|
754
|
+
const next = output.join("\n");
|
|
755
|
+
return next === source ? null : next;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function upsertContinueYamlRules(pathname, instructions) {
|
|
759
|
+
if (!fs.existsSync(pathname)) return false;
|
|
760
|
+
const normalized = normalizeInstructionText(instructions);
|
|
761
|
+
if (!normalized) return false;
|
|
762
|
+
const current = fs.readFileSync(pathname, "utf8");
|
|
763
|
+
const updated = rewriteContinueYamlRules(current, normalized, true);
|
|
764
|
+
if (!updated) return false;
|
|
765
|
+
return writeTextFile(pathname, updated);
|
|
766
|
+
}
|
|
767
|
+
|
|
413
768
|
function upsertZedInstructions(pathname, instructions) {
|
|
414
769
|
const { value } = readJson(pathname);
|
|
415
770
|
if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
|
|
@@ -423,12 +778,56 @@ function upsertZedInstructions(pathname, instructions) {
|
|
|
423
778
|
return true;
|
|
424
779
|
}
|
|
425
780
|
|
|
426
|
-
function
|
|
781
|
+
function upsertVsCodeInstructionKey(value, key, instructions) {
|
|
782
|
+
const existing = typeof value[key] === "string" ? value[key] : "";
|
|
783
|
+
const merged = mergeInstructionText(existing, instructions);
|
|
784
|
+
if (!merged || merged === existing) return false;
|
|
785
|
+
value[key] = merged;
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function upsertVsCodeInstructionLocations(value, instructionsDir) {
|
|
790
|
+
const key = "chat.instructionsFilesLocations";
|
|
791
|
+
const location = String(instructionsDir);
|
|
792
|
+
if (value[key] && typeof value[key] === "object" && !Array.isArray(value[key])) {
|
|
793
|
+
if (value[key][location] === true) return false;
|
|
794
|
+
value[key][location] = true;
|
|
795
|
+
return true;
|
|
796
|
+
}
|
|
797
|
+
if (Array.isArray(value[key])) {
|
|
798
|
+
if (value[key].some((entry) => entry === location)) return false;
|
|
799
|
+
value[key].push(location);
|
|
800
|
+
return true;
|
|
801
|
+
}
|
|
802
|
+
if (typeof value[key] === "string") {
|
|
803
|
+
if (value[key] === location) return false;
|
|
804
|
+
value[key] = [value[key], location];
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
value[key] = { [location]: true };
|
|
808
|
+
return true;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function upsertVsCodeInstructions(pathname, instructions, instructionsDir) {
|
|
427
812
|
const { value } = readJson(pathname);
|
|
428
813
|
if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
|
|
429
|
-
const
|
|
430
|
-
if (
|
|
431
|
-
|
|
814
|
+
const normalized = normalizeInstructionText(instructions);
|
|
815
|
+
if (!normalized) return false;
|
|
816
|
+
let updated = false;
|
|
817
|
+
if (upsertVsCodeInstructionKey(value, "github.copilot.chat.codeGeneration.instructions", instructions)) {
|
|
818
|
+
updated = true;
|
|
819
|
+
}
|
|
820
|
+
if (upsertVsCodeInstructionKey(value, "copilot.chat.codeGeneration.instructions", instructions)) {
|
|
821
|
+
updated = true;
|
|
822
|
+
}
|
|
823
|
+
if (value["github.copilot.chat.codeGeneration.useInstructionFiles"] !== true) {
|
|
824
|
+
value["github.copilot.chat.codeGeneration.useInstructionFiles"] = true;
|
|
825
|
+
updated = true;
|
|
826
|
+
}
|
|
827
|
+
if (upsertVsCodeInstructionLocations(value, instructionsDir)) {
|
|
828
|
+
updated = true;
|
|
829
|
+
}
|
|
830
|
+
if (!updated) return false;
|
|
432
831
|
writeJson(pathname, value);
|
|
433
832
|
return true;
|
|
434
833
|
}
|
|
@@ -758,6 +1157,12 @@ function clientInstructionPaths() {
|
|
|
758
1157
|
const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
759
1158
|
const userProfile = process.env.USERPROFILE || home;
|
|
760
1159
|
const vscodeGlobalInstructions = path.join(home, ".vscode", "global_instructions.md");
|
|
1160
|
+
const vscodeInstructionsDir = path.join(home, ".vscode", "instructions");
|
|
1161
|
+
const vscodeInstructionsFile = path.join(vscodeInstructionsDir, "docdex.md");
|
|
1162
|
+
const continueRoot = path.join(userProfile, ".continue");
|
|
1163
|
+
const continueJson = path.join(continueRoot, "config.json");
|
|
1164
|
+
const continueYaml = path.join(continueRoot, "config.yaml");
|
|
1165
|
+
const continueYml = path.join(continueRoot, "config.yml");
|
|
761
1166
|
const windsurfGlobalRules = path.join(userProfile, ".codeium", "windsurf", "memories", "global_rules.md");
|
|
762
1167
|
const rooRules = path.join(home, ".roo", "rules", "docdex.md");
|
|
763
1168
|
const pearaiAgent = path.join(home, ".config", "pearai", "agent.md");
|
|
@@ -769,10 +1174,14 @@ function clientInstructionPaths() {
|
|
|
769
1174
|
case "win32":
|
|
770
1175
|
return {
|
|
771
1176
|
claude: path.join(appData, "Claude", "claude_desktop_config.json"),
|
|
772
|
-
continue:
|
|
1177
|
+
continue: continueJson,
|
|
1178
|
+
continueYaml,
|
|
1179
|
+
continueYml,
|
|
773
1180
|
zed: path.join(appData, "Zed", "settings.json"),
|
|
774
1181
|
vscodeSettings: path.join(appData, "Code", "User", "settings.json"),
|
|
775
1182
|
vscodeGlobalInstructions,
|
|
1183
|
+
vscodeInstructionsDir,
|
|
1184
|
+
vscodeInstructionsFile,
|
|
776
1185
|
windsurfGlobalRules,
|
|
777
1186
|
rooRules,
|
|
778
1187
|
pearaiAgent,
|
|
@@ -784,10 +1193,14 @@ function clientInstructionPaths() {
|
|
|
784
1193
|
case "darwin":
|
|
785
1194
|
return {
|
|
786
1195
|
claude: path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
787
|
-
continue:
|
|
1196
|
+
continue: continueJson,
|
|
1197
|
+
continueYaml,
|
|
1198
|
+
continueYml,
|
|
788
1199
|
zed: path.join(home, ".config", "zed", "settings.json"),
|
|
789
1200
|
vscodeSettings: path.join(home, "Library", "Application Support", "Code", "User", "settings.json"),
|
|
790
1201
|
vscodeGlobalInstructions,
|
|
1202
|
+
vscodeInstructionsDir,
|
|
1203
|
+
vscodeInstructionsFile,
|
|
791
1204
|
windsurfGlobalRules,
|
|
792
1205
|
rooRules,
|
|
793
1206
|
pearaiAgent,
|
|
@@ -799,10 +1212,14 @@ function clientInstructionPaths() {
|
|
|
799
1212
|
default:
|
|
800
1213
|
return {
|
|
801
1214
|
claude: path.join(home, ".config", "Claude", "claude_desktop_config.json"),
|
|
802
|
-
continue:
|
|
1215
|
+
continue: continueJson,
|
|
1216
|
+
continueYaml,
|
|
1217
|
+
continueYml,
|
|
803
1218
|
zed: path.join(home, ".config", "zed", "settings.json"),
|
|
804
1219
|
vscodeSettings: path.join(home, ".config", "Code", "User", "settings.json"),
|
|
805
1220
|
vscodeGlobalInstructions,
|
|
1221
|
+
vscodeInstructionsDir,
|
|
1222
|
+
vscodeInstructionsFile,
|
|
806
1223
|
windsurfGlobalRules,
|
|
807
1224
|
rooRules,
|
|
808
1225
|
pearaiAgent,
|
|
@@ -826,6 +1243,41 @@ function resolveBinaryPath({ binaryPath } = {}) {
|
|
|
826
1243
|
return null;
|
|
827
1244
|
}
|
|
828
1245
|
|
|
1246
|
+
function isPathWithin(parent, candidate) {
|
|
1247
|
+
const base = path.resolve(parent);
|
|
1248
|
+
const target = path.resolve(candidate);
|
|
1249
|
+
if (base === target) return true;
|
|
1250
|
+
return target.startsWith(base + path.sep);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
function isMacProtectedPath(candidate) {
|
|
1254
|
+
if (process.platform !== "darwin") return false;
|
|
1255
|
+
const home = os.homedir();
|
|
1256
|
+
return ["Desktop", "Documents", "Downloads"].some((dir) => isPathWithin(path.join(home, dir), candidate));
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
function ensureStartupBinary(binaryPath, { logger } = {}) {
|
|
1260
|
+
if (!binaryPath) return null;
|
|
1261
|
+
if (!isMacProtectedPath(binaryPath) && !isTempPath(binaryPath)) return binaryPath;
|
|
1262
|
+
const binDir = path.join(os.homedir(), ".docdex", "bin");
|
|
1263
|
+
const target = path.join(binDir, path.basename(binaryPath));
|
|
1264
|
+
if (fs.existsSync(target)) return target;
|
|
1265
|
+
try {
|
|
1266
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
1267
|
+
fs.copyFileSync(binaryPath, target);
|
|
1268
|
+
fs.chmodSync(target, 0o755);
|
|
1269
|
+
return target;
|
|
1270
|
+
} catch (err) {
|
|
1271
|
+
logger?.warn?.(`[docdex] failed to stage daemon binary for startup: ${err?.message || err}`);
|
|
1272
|
+
return binaryPath;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function resolveStartupBinaryPaths({ binaryPath, logger } = {}) {
|
|
1277
|
+
const resolvedBinary = ensureStartupBinary(binaryPath, { logger });
|
|
1278
|
+
return { binaryPath: resolvedBinary };
|
|
1279
|
+
}
|
|
1280
|
+
|
|
829
1281
|
function applyAgentInstructions({ logger } = {}) {
|
|
830
1282
|
const instructions = buildDocdexInstructionBlock(loadAgentInstructions());
|
|
831
1283
|
if (!normalizeInstructionText(instructions)) return { ok: false, reason: "missing_instructions" };
|
|
@@ -847,8 +1299,15 @@ function applyAgentInstructions({ logger } = {}) {
|
|
|
847
1299
|
upsertPromptFile(paths.vscodeGlobalInstructions, instructions, { prepend: true })
|
|
848
1300
|
);
|
|
849
1301
|
}
|
|
850
|
-
if (paths.
|
|
851
|
-
safeApply("vscode-
|
|
1302
|
+
if (paths.vscodeInstructionsFile) {
|
|
1303
|
+
safeApply("vscode-instructions-file", () =>
|
|
1304
|
+
upsertPromptFile(paths.vscodeInstructionsFile, instructions, { prepend: true })
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
1307
|
+
if (paths.vscodeSettings && paths.vscodeInstructionsDir) {
|
|
1308
|
+
safeApply("vscode-settings", () =>
|
|
1309
|
+
upsertVsCodeInstructions(paths.vscodeSettings, instructions, paths.vscodeInstructionsDir)
|
|
1310
|
+
);
|
|
852
1311
|
}
|
|
853
1312
|
if (paths.windsurfGlobalRules) {
|
|
854
1313
|
safeApply("windsurf", () => upsertPromptFile(paths.windsurfGlobalRules, instructions, { prepend: true }));
|
|
@@ -862,18 +1321,25 @@ function applyAgentInstructions({ logger } = {}) {
|
|
|
862
1321
|
if (paths.claude) {
|
|
863
1322
|
safeApply("claude", () => upsertClaudeInstructions(paths.claude, instructions));
|
|
864
1323
|
}
|
|
865
|
-
|
|
866
|
-
|
|
1324
|
+
const continueYamlExists =
|
|
1325
|
+
(paths.continueYaml && fs.existsSync(paths.continueYaml)) ||
|
|
1326
|
+
(paths.continueYml && fs.existsSync(paths.continueYml));
|
|
1327
|
+
if (continueYamlExists) {
|
|
1328
|
+
if (paths.continueYaml && fs.existsSync(paths.continueYaml)) {
|
|
1329
|
+
safeApply("continue-yaml", () => upsertContinueYamlRules(paths.continueYaml, instructions));
|
|
1330
|
+
}
|
|
1331
|
+
if (paths.continueYml && fs.existsSync(paths.continueYml)) {
|
|
1332
|
+
safeApply("continue-yml", () => upsertContinueYamlRules(paths.continueYml, instructions));
|
|
1333
|
+
}
|
|
1334
|
+
if (paths.continue && fs.existsSync(paths.continue)) {
|
|
1335
|
+
safeApply("continue-json", () => upsertContinueJsonInstructions(paths.continue, instructions));
|
|
1336
|
+
}
|
|
1337
|
+
} else if (paths.continue) {
|
|
1338
|
+
safeApply("continue-json", () => upsertContinueJsonInstructions(paths.continue, instructions));
|
|
867
1339
|
}
|
|
868
1340
|
if (paths.zed) {
|
|
869
1341
|
safeApply("zed", () => upsertZedInstructions(paths.zed, instructions));
|
|
870
1342
|
}
|
|
871
|
-
if (paths.aiderConfig) {
|
|
872
|
-
safeApply("aider", () => upsertYamlInstruction(paths.aiderConfig, "system-prompt", instructions));
|
|
873
|
-
}
|
|
874
|
-
if (paths.gooseConfig) {
|
|
875
|
-
safeApply("goose", () => upsertYamlInstruction(paths.gooseConfig, "instructions", instructions));
|
|
876
|
-
}
|
|
877
1343
|
if (paths.openInterpreterConfig) {
|
|
878
1344
|
safeApply("open-interpreter", () =>
|
|
879
1345
|
upsertYamlInstruction(paths.openInterpreterConfig, "system_message", instructions)
|
|
@@ -886,14 +1352,6 @@ function applyAgentInstructions({ logger } = {}) {
|
|
|
886
1352
|
return { ok: true, updated };
|
|
887
1353
|
}
|
|
888
1354
|
|
|
889
|
-
function resolveMcpBinaryPath(binaryPath) {
|
|
890
|
-
if (!binaryPath) return null;
|
|
891
|
-
const dir = path.dirname(binaryPath);
|
|
892
|
-
const name = process.platform === "win32" ? "docdex-mcp-server.exe" : "docdex-mcp-server";
|
|
893
|
-
const candidate = path.join(dir, name);
|
|
894
|
-
return fs.existsSync(candidate) ? candidate : null;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
1355
|
function ensureDaemonRoot() {
|
|
898
1356
|
const root = daemonRootPath();
|
|
899
1357
|
fs.mkdirSync(root, { recursive: true });
|
|
@@ -1464,30 +1922,36 @@ async function maybePromptOllamaModel({
|
|
|
1464
1922
|
return { status: "skipped", reason: "invalid_selection" };
|
|
1465
1923
|
}
|
|
1466
1924
|
|
|
1467
|
-
function
|
|
1468
|
-
|
|
1469
|
-
|
|
1925
|
+
function envBool(value) {
|
|
1926
|
+
if (!value) return false;
|
|
1927
|
+
const normalized = String(value).trim().toLowerCase();
|
|
1928
|
+
return ["1", "true", "t", "yes", "y", "on"].includes(normalized);
|
|
1470
1929
|
}
|
|
1471
1930
|
|
|
1472
|
-
function
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1931
|
+
function isTempPath(value, osModule = os) {
|
|
1932
|
+
if (!value) return false;
|
|
1933
|
+
const tmpdir = osModule.tmpdir();
|
|
1934
|
+
if (!tmpdir) return false;
|
|
1935
|
+
const resolvedValue = path.resolve(value);
|
|
1936
|
+
const resolvedTmp = path.resolve(tmpdir);
|
|
1937
|
+
return resolvedValue === resolvedTmp || resolvedValue.startsWith(resolvedTmp + path.sep);
|
|
1478
1938
|
}
|
|
1479
1939
|
|
|
1480
|
-
function
|
|
1481
|
-
return
|
|
1940
|
+
function buildDaemonEnvPairs() {
|
|
1941
|
+
return [["DOCDEX_BROWSER_AUTO_INSTALL", "0"]];
|
|
1482
1942
|
}
|
|
1483
1943
|
|
|
1484
|
-
function
|
|
1944
|
+
function buildDaemonEnv() {
|
|
1945
|
+
return Object.fromEntries(buildDaemonEnvPairs());
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
function registerStartup({ binaryPath, port, repoRoot, logger }) {
|
|
1485
1949
|
if (!binaryPath) return { ok: false, reason: "missing_binary" };
|
|
1486
|
-
|
|
1950
|
+
stopDaemonService({ logger });
|
|
1951
|
+
const envPairs = buildDaemonEnvPairs();
|
|
1952
|
+
const workingDir = repoRoot ? path.resolve(repoRoot) : null;
|
|
1487
1953
|
const args = [
|
|
1488
1954
|
"daemon",
|
|
1489
|
-
"--repo",
|
|
1490
|
-
repoRoot,
|
|
1491
1955
|
"--host",
|
|
1492
1956
|
DEFAULT_HOST,
|
|
1493
1957
|
"--port",
|
|
@@ -1520,6 +1984,9 @@ function registerStartup({ binaryPath, mcpBinaryPath, port, repoRoot, logger })
|
|
|
1520
1984
|
` <array>\n` +
|
|
1521
1985
|
programArgs.map((arg) => ` <string>${arg}</string>\n`).join("") +
|
|
1522
1986
|
` </array>\n` +
|
|
1987
|
+
(workingDir
|
|
1988
|
+
? ` <key>WorkingDirectory</key>\n` + ` <string>${workingDir}</string>\n`
|
|
1989
|
+
: "") +
|
|
1523
1990
|
` <key>RunAtLoad</key>\n` +
|
|
1524
1991
|
` <true/>\n` +
|
|
1525
1992
|
` <key>KeepAlive</key>\n` +
|
|
@@ -1555,6 +2022,7 @@ function registerStartup({ binaryPath, mcpBinaryPath, port, repoRoot, logger })
|
|
|
1555
2022
|
"",
|
|
1556
2023
|
"[Service]",
|
|
1557
2024
|
`ExecStart=${binaryPath} ${args.join(" ")}`,
|
|
2025
|
+
workingDir ? `WorkingDirectory=${workingDir}` : "",
|
|
1558
2026
|
...envLines,
|
|
1559
2027
|
"Restart=always",
|
|
1560
2028
|
"RestartSec=2",
|
|
@@ -1575,8 +2043,9 @@ function registerStartup({ binaryPath, mcpBinaryPath, port, repoRoot, logger })
|
|
|
1575
2043
|
const taskName = "Docdex Daemon";
|
|
1576
2044
|
const joinedArgs = args.map((arg) => `"${arg}"`).join(" ");
|
|
1577
2045
|
const envParts = envPairs.map(([key, value]) => `set "${key}=${value}"`);
|
|
2046
|
+
const cdCommand = workingDir ? `cd /d "${workingDir}"` : null;
|
|
1578
2047
|
const taskArgs =
|
|
1579
|
-
`"cmd.exe" /c "${envParts.join(" && ")} && \"${binaryPath}\" ${joinedArgs}"`;
|
|
2048
|
+
`"cmd.exe" /c "${envParts.join(" && ")}${cdCommand ? ` && ${cdCommand}` : ""} && \"${binaryPath}\" ${joinedArgs}"`;
|
|
1580
2049
|
const create = spawnSync("schtasks", [
|
|
1581
2050
|
"/Create",
|
|
1582
2051
|
"/F",
|
|
@@ -1600,34 +2069,28 @@ function registerStartup({ binaryPath, mcpBinaryPath, port, repoRoot, logger })
|
|
|
1600
2069
|
return { ok: false, reason: "unsupported_platform" };
|
|
1601
2070
|
}
|
|
1602
2071
|
|
|
1603
|
-
function
|
|
1604
|
-
|
|
1605
|
-
const extraEnv = buildDaemonEnv({ mcpBinaryPath });
|
|
1606
|
-
const child = spawn(
|
|
2072
|
+
async function startDaemonWithHealthCheck({ binaryPath, port, host, logger }) {
|
|
2073
|
+
const startup = registerStartup({
|
|
1607
2074
|
binaryPath,
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
}
|
|
1628
|
-
);
|
|
1629
|
-
child.unref();
|
|
1630
|
-
return true;
|
|
2075
|
+
port,
|
|
2076
|
+
repoRoot: daemonRootPath(),
|
|
2077
|
+
logger
|
|
2078
|
+
});
|
|
2079
|
+
if (!startup.ok) {
|
|
2080
|
+
logger?.warn?.(`[docdex] daemon service registration failed (${startup.reason || "unknown"}).`);
|
|
2081
|
+
return { ok: false, reason: "startup_failed" };
|
|
2082
|
+
}
|
|
2083
|
+
startDaemonService({ logger });
|
|
2084
|
+
const healthy = await waitForDaemonHealthy({ host, port });
|
|
2085
|
+
if (healthy) {
|
|
2086
|
+
return { ok: true, reason: "healthy" };
|
|
2087
|
+
}
|
|
2088
|
+
logger?.warn?.(`[docdex] daemon failed health check on ${host}:${port}`);
|
|
2089
|
+
stopDaemonService({ logger });
|
|
2090
|
+
stopDaemonFromLock({ logger });
|
|
2091
|
+
stopDaemonByName({ logger });
|
|
2092
|
+
clearDaemonLocks();
|
|
2093
|
+
return { ok: false, reason: "health_failed" };
|
|
1631
2094
|
}
|
|
1632
2095
|
|
|
1633
2096
|
function recordStartupFailure(details) {
|
|
@@ -1758,16 +2221,34 @@ async function runPostInstallSetup({ binaryPath, logger } = {}) {
|
|
|
1758
2221
|
if (fs.existsSync(configPath)) {
|
|
1759
2222
|
existingConfig = fs.readFileSync(configPath, "utf8");
|
|
1760
2223
|
}
|
|
1761
|
-
const
|
|
1762
|
-
|
|
1763
|
-
if (
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
port
|
|
2224
|
+
const port = DEFAULT_DAEMON_PORT;
|
|
2225
|
+
const available = await isPortAvailable(port, DEFAULT_HOST);
|
|
2226
|
+
if (!available) {
|
|
2227
|
+
log.error?.(
|
|
2228
|
+
`[docdex] ${DEFAULT_HOST}:${port} is already in use; docdex requires a fixed port. Stop the process using this port and re-run the install.`
|
|
2229
|
+
);
|
|
2230
|
+
throw new Error(`docdex requires ${DEFAULT_HOST}:${port}, but the port is already in use`);
|
|
1768
2231
|
}
|
|
1769
|
-
|
|
1770
|
-
|
|
2232
|
+
|
|
2233
|
+
const daemonRoot = ensureDaemonRoot();
|
|
2234
|
+
const resolvedBinary = resolveBinaryPath({ binaryPath });
|
|
2235
|
+
const startupBinaries = resolveStartupBinaryPaths({
|
|
2236
|
+
binaryPath: resolvedBinary,
|
|
2237
|
+
logger: log
|
|
2238
|
+
});
|
|
2239
|
+
stopDaemonService({ logger: log });
|
|
2240
|
+
stopDaemonFromLock({ logger: log });
|
|
2241
|
+
stopDaemonByName({ logger: log });
|
|
2242
|
+
clearDaemonLocks();
|
|
2243
|
+
const result = await startDaemonWithHealthCheck({
|
|
2244
|
+
binaryPath: startupBinaries.binaryPath,
|
|
2245
|
+
port,
|
|
2246
|
+
host: DEFAULT_HOST,
|
|
2247
|
+
logger: log
|
|
2248
|
+
});
|
|
2249
|
+
if (!result.ok) {
|
|
2250
|
+
log.warn?.(`[docdex] daemon failed to start on ${DEFAULT_HOST}:${port}.`);
|
|
2251
|
+
throw new Error("docdex daemon failed to start");
|
|
1771
2252
|
}
|
|
1772
2253
|
|
|
1773
2254
|
const httpBindAddr = `${DEFAULT_HOST}:${port}`;
|
|
@@ -1800,29 +2281,8 @@ async function runPostInstallSetup({ binaryPath, logger } = {}) {
|
|
|
1800
2281
|
}
|
|
1801
2282
|
upsertCodexConfig(paths.codex, codexUrl);
|
|
1802
2283
|
applyAgentInstructions({ logger: log });
|
|
1803
|
-
|
|
1804
|
-
const
|
|
1805
|
-
const resolvedBinary = resolveBinaryPath({ binaryPath });
|
|
1806
|
-
const resolvedMcpBinary = resolveMcpBinaryPath(resolvedBinary);
|
|
1807
|
-
const startup = registerStartup({
|
|
1808
|
-
binaryPath: resolvedBinary,
|
|
1809
|
-
mcpBinaryPath: resolvedMcpBinary,
|
|
1810
|
-
port,
|
|
1811
|
-
repoRoot: daemonRoot,
|
|
1812
|
-
logger: log
|
|
1813
|
-
});
|
|
1814
|
-
if (!startup.ok) {
|
|
1815
|
-
if (!startupFailureReported()) {
|
|
1816
|
-
log.warn?.("[docdex] startup registration failed; run the daemon manually:");
|
|
1817
|
-
log.warn?.(`[docdex] ${resolvedBinary || "docdexd"} daemon --repo ${daemonRoot} --host ${DEFAULT_HOST} --port ${port}`);
|
|
1818
|
-
recordStartupFailure({ reason: startup.reason, port, repoRoot: daemonRoot });
|
|
1819
|
-
}
|
|
1820
|
-
} else {
|
|
1821
|
-
clearStartupFailure();
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
startDaemonNow({ binaryPath: resolvedBinary, mcpBinaryPath: resolvedMcpBinary, port, repoRoot: daemonRoot });
|
|
1825
|
-
const setupLaunch = launchSetupWizard({ binaryPath: resolvedBinary, logger: log });
|
|
2284
|
+
clearStartupFailure();
|
|
2285
|
+
const setupLaunch = launchSetupWizard({ binaryPath: startupBinaries.binaryPath, logger: log });
|
|
1826
2286
|
if (!setupLaunch.ok && setupLaunch.reason !== "skipped") {
|
|
1827
2287
|
log.warn?.("[docdex] setup wizard did not launch. Run `docdex setup`.");
|
|
1828
2288
|
recordSetupPending({ reason: setupLaunch.reason, port, repoRoot: daemonRoot });
|
|
@@ -1837,7 +2297,6 @@ module.exports = {
|
|
|
1837
2297
|
upsertMcpServerJson,
|
|
1838
2298
|
upsertZedConfig,
|
|
1839
2299
|
upsertCodexConfig,
|
|
1840
|
-
pickAvailablePort,
|
|
1841
2300
|
configUrlForPort,
|
|
1842
2301
|
configStreamableUrlForPort,
|
|
1843
2302
|
parseEnvBool,
|