@tobilu/qmd 2.5.1 → 2.5.2
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 +7 -0
- package/bin/qmd +105 -62
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.5.2] - 2026-05-22
|
|
6
|
+
|
|
7
|
+
### Fixes
|
|
8
|
+
|
|
9
|
+
- Launcher: Rewrite `bin/qmd` as a Node-based shebang polyglot to fix global npm installation execution failures on Windows (#668 / #452), while supporting seamless fallback to Bun in Node-less environments.
|
|
10
|
+
|
|
11
|
+
|
|
5
12
|
## [2.5.1] - 2026-05-20
|
|
6
13
|
|
|
7
14
|
### Changes
|
package/bin/qmd
CHANGED
|
@@ -1,68 +1,111 @@
|
|
|
1
|
-
#!/bin/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*) SOURCE="$SOURCE_DIR/$TARGET" ;;
|
|
11
|
-
esac
|
|
12
|
-
done
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// 2>/dev/null; if command -v node >/dev/null 2>&1; then exec node "$0" "$@"; else exec bun "$0" "$@"; fi
|
|
3
|
+
// Cross-platform launcher for qmd.
|
|
4
|
+
//
|
|
5
|
+
// Previously this was a POSIX shell script with `#!/bin/sh`, which meant npm
|
|
6
|
+
// on Windows generated shims that tried to route through `/bin/sh` — a path
|
|
7
|
+
// that doesn't exist on Windows, so `qmd` failed immediately after a global
|
|
8
|
+
// install. Rewriting the launcher in Node.js lets npm generate native
|
|
9
|
+
// cmd/ps1/sh shims that invoke `node` directly on every platform.
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
12
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
13
|
+
import { dirname, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export GGML_LOG_LEVEL="${GGML_LOG_LEVEL:-error}"
|
|
25
|
-
export GGML_BACKEND_SILENT="${GGML_BACKEND_SILENT:-1}"
|
|
26
|
-
fi
|
|
16
|
+
// Resolve symlinks so global installs (npm link / npm install -g) can find
|
|
17
|
+
// the actual package directory instead of the global bin directory.
|
|
18
|
+
const self = realpathSync(fileURLToPath(import.meta.url));
|
|
19
|
+
const pkgDir = resolve(dirname(self), "..");
|
|
20
|
+
const jsEntry = resolve(pkgDir, "dist/cli/qmd.js");
|
|
21
|
+
const tsEntry = resolve(pkgDir, "src/cli/qmd.ts");
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
// MCP stdio reserves stdout exclusively for JSON-RPC frames. node-llama-cpp
|
|
24
|
+
// / llama.cpp / ggml can write native logs directly to stdout before JS-level
|
|
25
|
+
// log handlers are attached, so seed the native quiet env before Node/Bun imports
|
|
26
|
+
// the CLI and its LLM modules. Preserve explicit user values when provided.
|
|
27
|
+
if (process.argv[2] === "mcp") {
|
|
28
|
+
process.env.LLAMA_LOG_LEVEL = process.env.LLAMA_LOG_LEVEL || "error";
|
|
29
|
+
process.env.GGML_LOG_LEVEL = process.env.GGML_LOG_LEVEL || "error";
|
|
30
|
+
process.env.GGML_BACKEND_SILENT = process.env.GGML_BACKEND_SILENT || "1";
|
|
31
|
+
}
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
fi
|
|
40
|
-
fi
|
|
41
|
-
if [ -f "$DIR/node_modules/tsx/dist/cli.mjs" ]; then
|
|
42
|
-
exec node "$DIR/node_modules/tsx/dist/cli.mjs" "$TS" "$@"
|
|
43
|
-
fi
|
|
44
|
-
fi
|
|
33
|
+
function hasBun() {
|
|
34
|
+
try {
|
|
35
|
+
const res = spawnSync("bun", ["--version"], { stdio: "ignore", shell: process.platform === "win32" });
|
|
36
|
+
return res.status === 0;
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
// In published packages, bin/qmd must run dist/. In a git checkout, however,
|
|
43
|
+
// dist/ is often ignored and can be stale after git reset or branch switches.
|
|
44
|
+
// Prefer source mode only for checkouts so ./bin/qmd reflects the checked-out
|
|
45
|
+
// source without changing packaged/runtime behavior.
|
|
46
|
+
let useSourceMode = false;
|
|
47
|
+
let sourceRunner = null;
|
|
48
|
+
let sourceArgs = [];
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
50
|
+
if (existsSync(resolve(pkgDir, ".git")) && existsSync(tsEntry)) {
|
|
51
|
+
if (existsSync(resolve(pkgDir, "bun.lock")) || existsSync(resolve(pkgDir, "bun.lockb"))) {
|
|
52
|
+
if (hasBun()) {
|
|
53
|
+
useSourceMode = true;
|
|
54
|
+
sourceRunner = "bun";
|
|
55
|
+
sourceArgs = [tsEntry, ...process.argv.slice(2)];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!useSourceMode && existsSync(resolve(pkgDir, "node_modules/tsx/dist/cli.mjs"))) {
|
|
59
|
+
useSourceMode = true;
|
|
60
|
+
sourceRunner = "node";
|
|
61
|
+
sourceArgs = [resolve(pkgDir, "node_modules/tsx/dist/cli.mjs"), tsEntry, ...process.argv.slice(2)];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!useSourceMode && !existsSync(jsEntry)) {
|
|
66
|
+
console.error(`qmd is not built: missing ${jsEntry}`);
|
|
67
|
+
console.error("Run: bun install && bun run build");
|
|
68
|
+
console.error("Or: npm install && npm run build");
|
|
69
|
+
console.error("After building, run: qmd doctor");
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Detect the package manager that installed dependencies by checking lockfiles.
|
|
74
|
+
// $BUN_INSTALL is intentionally NOT checked — it only indicates that bun exists
|
|
75
|
+
// on the system, not that it was used to install this package (see #361).
|
|
76
|
+
//
|
|
77
|
+
// package-lock.json takes priority: if it exists, npm installed the native
|
|
78
|
+
// modules for Node. The repo ships bun.lock, so without this check, source
|
|
79
|
+
// builds that use npm would be incorrectly routed to bun, causing ABI
|
|
80
|
+
// mismatches with better-sqlite3 / sqlite-vec (see #381).
|
|
81
|
+
let runnerName = "node";
|
|
82
|
+
if (existsSync(resolve(pkgDir, "package-lock.json"))) {
|
|
83
|
+
runnerName = "node";
|
|
84
|
+
} else if (existsSync(resolve(pkgDir, "bun.lock")) || existsSync(resolve(pkgDir, "bun.lockb"))) {
|
|
85
|
+
runnerName = "bun";
|
|
86
|
+
} else {
|
|
87
|
+
runnerName = "node";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const runner = useSourceMode ? sourceRunner : (runnerName === "node" ? "node" : "bun");
|
|
91
|
+
const args = useSourceMode ? sourceArgs : [jsEntry, ...process.argv.slice(2)];
|
|
92
|
+
const needsShell = (runner === "bun") && process.platform === "win32";
|
|
93
|
+
|
|
94
|
+
const child = spawn(runner, args, {
|
|
95
|
+
stdio: "inherit",
|
|
96
|
+
shell: needsShell,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
child.on("exit", (code, signal) => {
|
|
100
|
+
if (signal) {
|
|
101
|
+
process.kill(process.pid, signal);
|
|
102
|
+
} else {
|
|
103
|
+
process.exit(code ?? 0);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
child.on("error", (err) => {
|
|
108
|
+
const name = useSourceMode ? sourceRunner : runnerName;
|
|
109
|
+
console.error(`qmd: failed to launch ${name}: ${err.message}`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
});
|
package/package.json
CHANGED