@zhihand/mcp 0.18.1 → 0.19.0
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/dist/core/sse.js +1 -1
- package/dist/daemon/dispatcher.js +93 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/core/sse.js
CHANGED
|
@@ -27,7 +27,7 @@ export function connectSSE(config) {
|
|
|
27
27
|
return; // Already connected
|
|
28
28
|
sseAbortController = new AbortController();
|
|
29
29
|
const { signal } = sseAbortController;
|
|
30
|
-
const url = `${config.controlPlaneEndpoint}/v1/credentials/${encodeURIComponent(config.credentialId)}/events?topic=commands`;
|
|
30
|
+
const url = `${config.controlPlaneEndpoint}/v1/credentials/${encodeURIComponent(config.credentialId)}/events/stream?topic=commands`;
|
|
31
31
|
(async () => {
|
|
32
32
|
while (!signal.aborted) {
|
|
33
33
|
try {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { spawn, execSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
2
3
|
import fsp from "node:fs/promises";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import os from "node:os";
|
|
@@ -12,6 +13,93 @@ const SESSION_STABILITY_DELAY = 2_000; // wait 2s after outcome before returning
|
|
|
12
13
|
// Resolve pty-wrap.py relative to this file (works from both src/ and dist/)
|
|
13
14
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
15
|
const PTY_WRAP_SCRIPT = path.resolve(__dirname, "../../scripts/pty-wrap.py");
|
|
16
|
+
// ── Executable Path Resolution ───────────────────────────────
|
|
17
|
+
/** Cache of resolved executable paths to avoid repeated lookups */
|
|
18
|
+
const executableCache = new Map();
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the full path of a CLI executable.
|
|
21
|
+
* Searches PATH first via `which`, then falls back to platform-specific known locations.
|
|
22
|
+
*/
|
|
23
|
+
function resolveExecutable(name, fallbackPaths) {
|
|
24
|
+
const cached = executableCache.get(name);
|
|
25
|
+
if (cached)
|
|
26
|
+
return cached;
|
|
27
|
+
// Try `which` first (works when the binary is in PATH)
|
|
28
|
+
try {
|
|
29
|
+
const resolved = execSync(`which ${name}`, { encoding: "utf8", timeout: 5000 }).trim();
|
|
30
|
+
if (resolved) {
|
|
31
|
+
executableCache.set(name, resolved);
|
|
32
|
+
return resolved;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Not in PATH, try fallback locations
|
|
37
|
+
}
|
|
38
|
+
// Try known platform-specific paths
|
|
39
|
+
for (const candidate of fallbackPaths) {
|
|
40
|
+
// Support glob-like patterns with * (e.g. version directories)
|
|
41
|
+
if (candidate.includes("*")) {
|
|
42
|
+
try {
|
|
43
|
+
const dir = path.dirname(candidate);
|
|
44
|
+
const pattern = path.basename(candidate);
|
|
45
|
+
// Walk one level of glob for version directories
|
|
46
|
+
const parentDir = path.dirname(dir);
|
|
47
|
+
const globSegment = path.basename(dir);
|
|
48
|
+
if (globSegment === "*") {
|
|
49
|
+
const entries = fs.readdirSync(parentDir, { withFileTypes: true });
|
|
50
|
+
// Sort descending to prefer latest version
|
|
51
|
+
const dirs = entries
|
|
52
|
+
.filter(e => e.isDirectory())
|
|
53
|
+
.map(e => e.name)
|
|
54
|
+
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
|
|
55
|
+
for (const d of dirs) {
|
|
56
|
+
const full = path.join(parentDir, d, pattern);
|
|
57
|
+
if (fs.existsSync(full)) {
|
|
58
|
+
executableCache.set(name, full);
|
|
59
|
+
return full;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Glob resolution failed, skip
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
if (fs.existsSync(candidate)) {
|
|
70
|
+
executableCache.set(name, candidate);
|
|
71
|
+
return candidate;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Last resort: return bare name and let spawn fail with a clear error
|
|
76
|
+
return name;
|
|
77
|
+
}
|
|
78
|
+
/** Resolve gemini executable path */
|
|
79
|
+
function resolveGemini() {
|
|
80
|
+
return resolveExecutable("gemini", [
|
|
81
|
+
"/opt/homebrew/bin/gemini", // macOS ARM (Homebrew)
|
|
82
|
+
"/usr/local/bin/gemini", // macOS Intel / Linux
|
|
83
|
+
path.join(os.homedir(), ".local/bin/gemini"), // pip --user install
|
|
84
|
+
path.join(os.homedir(), "bin/gemini"),
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
/** Resolve claude executable path */
|
|
88
|
+
function resolveClaude() {
|
|
89
|
+
const platform = process.platform;
|
|
90
|
+
const fallbacks = [];
|
|
91
|
+
if (platform === "darwin") {
|
|
92
|
+
// macOS: Claude Code installed via Claude desktop app
|
|
93
|
+
fallbacks.push(path.join(os.homedir(), "Library/Application Support/Claude/claude-code/*/claude.app/Contents/MacOS/claude"), "/usr/local/bin/claude", "/opt/homebrew/bin/claude");
|
|
94
|
+
}
|
|
95
|
+
else if (platform === "linux") {
|
|
96
|
+
fallbacks.push("/usr/local/bin/claude", path.join(os.homedir(), ".local/bin/claude"), "/snap/bin/claude");
|
|
97
|
+
}
|
|
98
|
+
else if (platform === "win32") {
|
|
99
|
+
fallbacks.push(path.join(process.env.LOCALAPPDATA ?? "", "Programs/Claude/claude.exe"), path.join(process.env.APPDATA ?? "", "npm/claude.cmd"));
|
|
100
|
+
}
|
|
101
|
+
return resolveExecutable("claude", fallbacks);
|
|
102
|
+
}
|
|
15
103
|
// Gemini session directories
|
|
16
104
|
const GEMINI_TMP_DIR = path.join(os.homedir(), ".gemini", "tmp");
|
|
17
105
|
let activeChild = null;
|
|
@@ -396,7 +484,8 @@ function dispatchGemini(prompt, startTime, log, model) {
|
|
|
396
484
|
COLORTERM: "truecolor",
|
|
397
485
|
};
|
|
398
486
|
// Wrap with PTY so gemini sees isatty()==true
|
|
399
|
-
const
|
|
487
|
+
const geminiPath = resolveGemini();
|
|
488
|
+
const child = spawn("python3", [PTY_WRAP_SCRIPT, geminiPath, ...cliArgs], {
|
|
400
489
|
env,
|
|
401
490
|
stdio: ["ignore", "pipe", "pipe"],
|
|
402
491
|
detached: false,
|
|
@@ -427,7 +516,8 @@ function dispatchCodex(prompt, startTime, model) {
|
|
|
427
516
|
}
|
|
428
517
|
// ── Claude Dispatch ────────────────────────────────────────
|
|
429
518
|
function dispatchClaude(prompt, startTime, model) {
|
|
430
|
-
const
|
|
519
|
+
const claudePath = resolveClaude();
|
|
520
|
+
const child = spawn(claudePath, ["-p", prompt, "--output-format", "json"], {
|
|
431
521
|
env: process.env,
|
|
432
522
|
stdio: ["ignore", "pipe", "pipe"],
|
|
433
523
|
detached: false,
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { controlSchema, screenshotSchema, pairSchema } from "./tools/schemas.js"
|
|
|
5
5
|
import { executeControl } from "./tools/control.js";
|
|
6
6
|
import { handleScreenshot } from "./tools/screenshot.js";
|
|
7
7
|
import { handlePair } from "./tools/pair.js";
|
|
8
|
-
const PACKAGE_VERSION = "0.
|
|
8
|
+
const PACKAGE_VERSION = "0.19.0";
|
|
9
9
|
export function createServer(deviceName) {
|
|
10
10
|
const server = new McpServer({
|
|
11
11
|
name: "zhihand",
|