docdex 0.2.6 → 0.2.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## 0.2.6
3
+ ## 0.2.7
4
4
  - Added glama support
5
5
 
6
6
  ## 0.1.10
@@ -6,6 +6,7 @@ const net = require("node:net");
6
6
  const os = require("node:os");
7
7
  const path = require("node:path");
8
8
  const readline = require("node:readline");
9
+ const tty = require("node:tty");
9
10
  const { spawn, spawnSync } = require("node:child_process");
10
11
 
11
12
  const { detectPlatformKey, UnsupportedPlatformError } = require("./platform");
@@ -17,6 +18,7 @@ const STARTUP_FAILURE_MARKER = "startup_registration_failed.json";
17
18
  const DEFAULT_OLLAMA_MODEL = "nomic-embed-text";
18
19
  const DEFAULT_OLLAMA_CHAT_MODEL = "phi3.5:3.8b";
19
20
  const DEFAULT_OLLAMA_CHAT_MODEL_SIZE_GIB = 2.2;
21
+ const SETUP_PENDING_MARKER = "setup_pending.json";
20
22
 
21
23
  function defaultConfigPath() {
22
24
  return path.join(os.homedir(), ".docdex", "config.toml");
@@ -30,6 +32,10 @@ function stateDir() {
30
32
  return path.join(os.homedir(), ".docdex", "state");
31
33
  }
32
34
 
35
+ function setupPendingPath() {
36
+ return path.join(stateDir(), SETUP_PENDING_MARKER);
37
+ }
38
+
33
39
  function configUrlForPort(port) {
34
40
  return `http://localhost:${port}/sse`;
35
41
  }
@@ -389,9 +395,11 @@ function canPromptWithTty(stdin, stdout) {
389
395
  try {
390
396
  const readFd = fs.openSync(inputPath, "r");
391
397
  const writeFd = fs.openSync(outputPath, "w");
398
+ const readable = tty.isatty(readFd);
399
+ const writable = tty.isatty(writeFd);
392
400
  fs.closeSync(readFd);
393
401
  fs.closeSync(writeFd);
394
- return true;
402
+ return readable && writable;
395
403
  } catch {
396
404
  return false;
397
405
  }
@@ -592,8 +600,15 @@ function resolvePromptStreams(stdin, stdout) {
592
600
  const inputPath = isWindows ? "CONIN$" : "/dev/tty";
593
601
  const outputPath = isWindows ? "CONOUT$" : "/dev/tty";
594
602
  try {
595
- const input = fs.createReadStream(inputPath, { autoClose: true });
596
- const output = fs.createWriteStream(outputPath, { autoClose: true });
603
+ const readFd = fs.openSync(inputPath, "r");
604
+ const writeFd = fs.openSync(outputPath, "w");
605
+ if (!tty.isatty(readFd) || !tty.isatty(writeFd)) {
606
+ fs.closeSync(readFd);
607
+ fs.closeSync(writeFd);
608
+ return { input: stdin, output: stdout, close: null };
609
+ }
610
+ const input = fs.createReadStream(inputPath, { fd: readFd, autoClose: true });
611
+ const output = fs.createWriteStream(outputPath, { fd: writeFd, autoClose: true });
597
612
  return {
598
613
  input,
599
614
  output,
@@ -610,8 +625,11 @@ function resolvePromptStreams(stdin, stdout) {
610
625
  function promptYesNo(question, { defaultYes = true, stdin = process.stdin, stdout = process.stdout } = {}) {
611
626
  return new Promise((resolve) => {
612
627
  const { input, output, close } = resolvePromptStreams(stdin, stdout);
613
- const rl = readline.createInterface({ input, output });
614
- rl.question(question, (answer) => {
628
+ const rl = readline.createInterface({ input, output, terminal: Boolean(output?.isTTY) });
629
+ if (output && typeof output.write === "function") {
630
+ output.write(`\n${question}`);
631
+ }
632
+ rl.question("", (answer) => {
615
633
  rl.close();
616
634
  if (typeof close === "function") close();
617
635
  const normalized = String(answer || "").trim().toLowerCase();
@@ -624,8 +642,11 @@ function promptYesNo(question, { defaultYes = true, stdin = process.stdin, stdou
624
642
  function promptInput(question, { stdin = process.stdin, stdout = process.stdout } = {}) {
625
643
  return new Promise((resolve) => {
626
644
  const { input, output, close } = resolvePromptStreams(stdin, stdout);
627
- const rl = readline.createInterface({ input, output });
628
- rl.question(question, (answer) => {
645
+ const rl = readline.createInterface({ input, output, terminal: Boolean(output?.isTTY) });
646
+ if (output && typeof output.write === "function") {
647
+ output.write(`\n${question}`);
648
+ }
649
+ rl.question("", (answer) => {
629
650
  rl.close();
630
651
  if (typeof close === "function") close();
631
652
  resolve(String(answer || "").trim());
@@ -901,6 +922,11 @@ function registerStartup({ binaryPath, port, repoRoot, logger }) {
901
922
  `<dict>\n` +
902
923
  ` <key>Label</key>\n` +
903
924
  ` <string>com.docdex.daemon</string>\n` +
925
+ ` <key>EnvironmentVariables</key>\n` +
926
+ ` <dict>\n` +
927
+ ` <key>DOCDEX_BROWSER_AUTO_INSTALL</key>\n` +
928
+ ` <string>0</string>\n` +
929
+ ` </dict>\n` +
904
930
  ` <key>ProgramArguments</key>\n` +
905
931
  ` <array>\n` +
906
932
  programArgs.map((arg) => ` <string>${arg}</string>\n`).join("") +
@@ -939,6 +965,7 @@ function registerStartup({ binaryPath, port, repoRoot, logger }) {
939
965
  "",
940
966
  "[Service]",
941
967
  `ExecStart=${binaryPath} ${args.join(" ")}`,
968
+ "Environment=DOCDEX_BROWSER_AUTO_INSTALL=0",
942
969
  "Restart=always",
943
970
  "RestartSec=2",
944
971
  "",
@@ -956,7 +983,9 @@ function registerStartup({ binaryPath, port, repoRoot, logger }) {
956
983
 
957
984
  if (process.platform === "win32") {
958
985
  const taskName = "Docdex Daemon";
959
- const taskArgs = `"${binaryPath}" ${args.map((arg) => `"${arg}"`).join(" ")}`;
986
+ const joinedArgs = args.map((arg) => `"${arg}"`).join(" ");
987
+ const taskArgs =
988
+ `"cmd.exe" /c "set DOCDEX_BROWSER_AUTO_INSTALL=0 && \"${binaryPath}\" ${joinedArgs}"`;
960
989
  const create = spawnSync("schtasks", [
961
990
  "/Create",
962
991
  "/F",
@@ -996,7 +1025,14 @@ function startDaemonNow({ binaryPath, port, repoRoot }) {
996
1025
  "warn",
997
1026
  "--secure-mode=false"
998
1027
  ],
999
- { stdio: "ignore", detached: true }
1028
+ {
1029
+ stdio: "ignore",
1030
+ detached: true,
1031
+ env: {
1032
+ ...process.env,
1033
+ DOCDEX_BROWSER_AUTO_INSTALL: "0"
1034
+ }
1035
+ }
1000
1036
  );
1001
1037
  child.unref();
1002
1038
  return true;
@@ -1008,6 +1044,12 @@ function recordStartupFailure(details) {
1008
1044
  fs.writeFileSync(markerPath, JSON.stringify(details, null, 2));
1009
1045
  }
1010
1046
 
1047
+ function recordSetupPending(details) {
1048
+ const markerPath = setupPendingPath();
1049
+ fs.mkdirSync(path.dirname(markerPath), { recursive: true });
1050
+ fs.writeFileSync(markerPath, JSON.stringify(details, null, 2));
1051
+ }
1052
+
1011
1053
  function clearStartupFailure() {
1012
1054
  const markerPath = path.join(stateDir(), STARTUP_FAILURE_MARKER);
1013
1055
  if (fs.existsSync(markerPath)) fs.unlinkSync(markerPath);
@@ -1017,6 +1059,58 @@ function startupFailureReported() {
1017
1059
  return fs.existsSync(path.join(stateDir(), STARTUP_FAILURE_MARKER));
1018
1060
  }
1019
1061
 
1062
+ function shouldSkipSetup(env = process.env) {
1063
+ return parseEnvBool(env.DOCDEX_SETUP_SKIP) === true;
1064
+ }
1065
+
1066
+ function launchSetupWizard({
1067
+ binaryPath,
1068
+ logger,
1069
+ env = process.env,
1070
+ stdin = process.stdin,
1071
+ stdout = process.stdout,
1072
+ spawnFn = spawn,
1073
+ spawnSyncFn = spawnSync,
1074
+ platform = process.platform,
1075
+ canPrompt = canPromptWithTty
1076
+ }) {
1077
+ if (!binaryPath) return { ok: false, reason: "missing_binary" };
1078
+ if (shouldSkipSetup(env)) return { ok: false, reason: "skipped" };
1079
+
1080
+ const args = ["setup"];
1081
+ if (platform === "linux") {
1082
+ if (!canPrompt(stdin, stdout)) {
1083
+ return { ok: false, reason: "non_interactive" };
1084
+ }
1085
+ const child = spawnFn(binaryPath, args, { stdio: "inherit" });
1086
+ if (child.pid) return { ok: true };
1087
+ return { ok: false, reason: "spawn_failed" };
1088
+ }
1089
+
1090
+ if (platform === "darwin") {
1091
+ const command = `${binaryPath} ${args.join(" ")}`;
1092
+ const osa = [
1093
+ "osascript",
1094
+ "-e",
1095
+ `tell application \"Terminal\" to do script \"${command.replace(/"/g, '\\"')}\"`
1096
+ ];
1097
+ const result = spawnSyncFn(osa[0], osa.slice(1));
1098
+ if (result.status === 0) return { ok: true };
1099
+ logger?.warn?.(`[docdex] osascript failed: ${result.stderr || "unknown error"}`);
1100
+ return { ok: false, reason: "terminal_launch_failed" };
1101
+ }
1102
+
1103
+ if (platform === "win32") {
1104
+ const quoted = `"${binaryPath}" ${args.map((arg) => `"${arg}"`).join(" ")}`;
1105
+ const result = spawnSyncFn("cmd", ["/c", "start", "", quoted]);
1106
+ if (result.status === 0) return { ok: true };
1107
+ logger?.warn?.(`[docdex] cmd start failed: ${result.stderr || "unknown error"}`);
1108
+ return { ok: false, reason: "terminal_launch_failed" };
1109
+ }
1110
+
1111
+ return { ok: false, reason: "unsupported_platform" };
1112
+ }
1113
+
1020
1114
  async function runPostInstallSetup({ binaryPath, logger } = {}) {
1021
1115
  const log = logger || console;
1022
1116
  const configPath = defaultConfigPath();
@@ -1063,8 +1157,11 @@ async function runPostInstallSetup({ binaryPath, logger } = {}) {
1063
1157
  }
1064
1158
 
1065
1159
  startDaemonNow({ binaryPath: resolvedBinary, port, repoRoot: daemonRoot });
1066
- await maybeInstallOllama({ logger: log });
1067
- await maybePromptOllamaModel({ logger: log, configPath });
1160
+ const setupLaunch = launchSetupWizard({ binaryPath: resolvedBinary, logger: log });
1161
+ if (!setupLaunch.ok && setupLaunch.reason !== "skipped") {
1162
+ log.warn?.("[docdex] setup wizard did not launch. Run `docdex setup`.");
1163
+ recordSetupPending({ reason: setupLaunch.reason, port, repoRoot: daemonRoot });
1164
+ }
1068
1165
  return { port, url, configPath };
1069
1166
  }
1070
1167
 
@@ -1086,5 +1183,7 @@ module.exports = {
1086
1183
  pullOllamaModel,
1087
1184
  listOllamaModels,
1088
1185
  hasInteractiveTty,
1089
- canPromptWithTty
1186
+ canPromptWithTty,
1187
+ shouldSkipSetup,
1188
+ launchSetupWizard
1090
1189
  };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "docdex",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
+ "mcpName": "io.github.bekirdag/docdex",
4
5
  "description": "Docdex CLI as an npm-installable binary wrapper.",
5
6
  "bin": {
6
7
  "docdex": "bin/docdex.js",