@zhihand/mcp 0.34.0 → 0.35.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/bin/zhihand +4 -4
- package/dist/core/pair.js +12 -0
- package/dist/daemon/index.js +61 -25
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/bin/zhihand
CHANGED
|
@@ -605,7 +605,7 @@ switch (command) {
|
|
|
605
605
|
{ id: 17, phase: "Text+Keys", label: "Key combo (select all)", kind: "hid", platformAware: "select_all" },
|
|
606
606
|
{ id: 18, phase: "Navigation", label: "Press Home", kind: "hid", params: { action: "home" } },
|
|
607
607
|
{ id: 19, phase: "Navigation", label: "Press Back", kind: "hid", params: { action: "back" } },
|
|
608
|
-
{ id: 20, phase: "Navigation", label: "Open
|
|
608
|
+
{ id: 20, phase: "Navigation", label: "Open Settings", kind: "hid", platformAware: "open_settings" },
|
|
609
609
|
{ id: 21, phase: "Clipboard", label: "Clipboard set", kind: "hid", platformAware: "clipboard_set" },
|
|
610
610
|
{ id: 22, phase: "System Nav", label: "Notification shade", kind: "system", params: { action: "notification" } },
|
|
611
611
|
{ id: 23, phase: "System Nav", label: "Recent apps", kind: "system", params: { action: "recent" } },
|
|
@@ -758,10 +758,10 @@ switch (command) {
|
|
|
758
758
|
|
|
759
759
|
function resolvePlatformAwareParams(variant) {
|
|
760
760
|
const platform = getDevicePlatform();
|
|
761
|
-
if (variant === "
|
|
761
|
+
if (variant === "open_settings") {
|
|
762
762
|
return platform === "ios"
|
|
763
|
-
? { action: "open_app", bundleId: "com.
|
|
764
|
-
: { action: "open_app", appPackage: "com.
|
|
763
|
+
? { action: "open_app", bundleId: "com.apple.Preferences" }
|
|
764
|
+
: { action: "open_app", appPackage: "com.android.settings" };
|
|
765
765
|
}
|
|
766
766
|
if (variant === "clipboard_set") {
|
|
767
767
|
return { action: "clipboard", text: `zhihand_test_${Date.now()}` };
|
package/dist/core/pair.js
CHANGED
|
@@ -76,8 +76,20 @@ export async function ensurePluginIdentity(endpoint) {
|
|
|
76
76
|
plugin_secret: plugin.plugin_secret,
|
|
77
77
|
};
|
|
78
78
|
savePluginIdentity(identity);
|
|
79
|
+
// Notify running daemon to hot-reload identity (best-effort, daemon may not be running)
|
|
80
|
+
notifyDaemonReload();
|
|
79
81
|
return identity;
|
|
80
82
|
}
|
|
83
|
+
/** Best-effort POST to daemon's reload-identity endpoint. */
|
|
84
|
+
function notifyDaemonReload() {
|
|
85
|
+
const port = parseInt(process.env.ZHIHAND_PORT ?? "", 10) || 18686;
|
|
86
|
+
fetch(`http://127.0.0.1:${port}/internal/reload-identity`, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
signal: AbortSignal.timeout(3_000),
|
|
89
|
+
}).catch(() => {
|
|
90
|
+
// Daemon not running — that's fine, next start will pick up identity
|
|
91
|
+
});
|
|
92
|
+
}
|
|
81
93
|
/**
|
|
82
94
|
* Poll pairing session until claimed or expired.
|
|
83
95
|
*/
|
package/dist/daemon/index.js
CHANGED
|
@@ -64,6 +64,8 @@ function onPromptReceived(config, prompt) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
// ── Internal API ───────────────────────────────────────────
|
|
67
|
+
/** Set by startDaemon to allow identity hot-reload via /internal/reload-identity */
|
|
68
|
+
let reloadIdentityHandler = null;
|
|
67
69
|
function handleInternalAPI(req, res) {
|
|
68
70
|
const url = req.url ?? "";
|
|
69
71
|
if (url === "/internal/backend" && req.method === "POST") {
|
|
@@ -145,6 +147,18 @@ function handleInternalAPI(req, res) {
|
|
|
145
147
|
});
|
|
146
148
|
return true;
|
|
147
149
|
}
|
|
150
|
+
if (url === "/internal/reload-identity" && req.method === "POST") {
|
|
151
|
+
dbg(`[api] POST /internal/reload-identity`);
|
|
152
|
+
if (!reloadIdentityHandler) {
|
|
153
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
154
|
+
res.end(JSON.stringify({ error: "Daemon not ready" }));
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
const result = reloadIdentityHandler();
|
|
158
|
+
res.writeHead(result.ok ? 200 : 500, { "Content-Type": "application/json" });
|
|
159
|
+
res.end(JSON.stringify(result));
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
148
162
|
if (url === "/internal/status" && req.method === "GET") {
|
|
149
163
|
dbg(`[api] GET /internal/status`);
|
|
150
164
|
const effectiveModel = activeBackend ? (activeModel ?? DEFAULT_MODELS[activeBackend]) : null;
|
|
@@ -359,45 +373,67 @@ export async function startDaemon(options) {
|
|
|
359
373
|
httpServer.listen(port, "127.0.0.1", () => resolve());
|
|
360
374
|
});
|
|
361
375
|
writePid();
|
|
362
|
-
//
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
376
|
+
// ── Identity hot-reload support ──────────────────────────
|
|
377
|
+
let currentHeartbeatTarget = null;
|
|
378
|
+
let promptListener = null;
|
|
379
|
+
/** (Re)start heartbeat + prompt listener with current identity. Stops previous if running. */
|
|
380
|
+
function activateIdentity(identity) {
|
|
381
|
+
// Stop previous if running
|
|
382
|
+
if (promptListener)
|
|
383
|
+
promptListener.stop();
|
|
384
|
+
stopHeartbeatLoop();
|
|
385
|
+
currentHeartbeatTarget = {
|
|
386
|
+
controlPlaneEndpoint: resolveDefaultEndpoint(),
|
|
387
|
+
edgeId: identity.edge_id,
|
|
388
|
+
pluginSecret: identity.plugin_secret,
|
|
389
|
+
};
|
|
390
|
+
startHeartbeatLoop(currentHeartbeatTarget, log);
|
|
391
|
+
promptListener = new PromptListener({
|
|
392
|
+
controlPlaneEndpoint: resolveDefaultEndpoint(),
|
|
393
|
+
edgeId: identity.edge_id,
|
|
394
|
+
pluginSecret: identity.plugin_secret,
|
|
395
|
+
}, (prompt) => onPromptReceived(config, prompt), log, (reason) => {
|
|
396
|
+
log(`[fatal] ${reason}`);
|
|
397
|
+
process.exit(1);
|
|
398
|
+
});
|
|
399
|
+
promptListener.start();
|
|
400
|
+
log(`[identity] Active: edge_id=${identity.edge_id}, stable_identity=${identity.stable_identity}`);
|
|
367
401
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
402
|
+
// Wire up the reload-identity API handler
|
|
403
|
+
reloadIdentityHandler = () => {
|
|
404
|
+
const identity = loadPluginIdentity();
|
|
405
|
+
if (!identity) {
|
|
406
|
+
return { ok: false, error: "No identity.json found" };
|
|
407
|
+
}
|
|
408
|
+
activateIdentity(identity);
|
|
409
|
+
return { ok: true, edge_id: identity.edge_id };
|
|
373
410
|
};
|
|
374
|
-
//
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
382
|
-
log(`[fatal] ${reason}`);
|
|
383
|
-
process.exit(1);
|
|
384
|
-
});
|
|
385
|
-
promptListener.start();
|
|
411
|
+
// Try loading identity at startup (non-fatal if missing)
|
|
412
|
+
const initialIdentity = loadPluginIdentity();
|
|
413
|
+
if (initialIdentity) {
|
|
414
|
+
activateIdentity(initialIdentity);
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
log("[identity] No plugin identity found. Waiting for 'zhihand pair'...");
|
|
418
|
+
}
|
|
386
419
|
log(`ZhiHand daemon started.`);
|
|
387
420
|
log(` PID: ${process.pid}`);
|
|
388
421
|
log(` MCP: http://127.0.0.1:${port}/mcp`);
|
|
389
422
|
log(` Backend: ${activeBackend ?? "(none)"}`);
|
|
390
|
-
|
|
423
|
+
if (initialIdentity)
|
|
424
|
+
log(` Edge: ${initialIdentity.edge_id}`);
|
|
391
425
|
log(` Device: ${config.credentialId}`);
|
|
392
426
|
log(`Listening for prompts...`);
|
|
393
427
|
// Graceful shutdown
|
|
394
428
|
const shutdown = async () => {
|
|
395
429
|
log("\nShutting down...");
|
|
396
|
-
promptListener
|
|
430
|
+
if (promptListener)
|
|
431
|
+
promptListener.stop();
|
|
397
432
|
stopHeartbeatLoop();
|
|
398
433
|
clearInterval(sessionCleanupTimer);
|
|
399
434
|
await killActiveChild();
|
|
400
|
-
|
|
435
|
+
if (currentHeartbeatTarget)
|
|
436
|
+
await sendBrainOffline(currentHeartbeatTarget);
|
|
401
437
|
// Close all MCP sessions
|
|
402
438
|
for (const session of mcpSessions.values()) {
|
|
403
439
|
try {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
export declare const PACKAGE_VERSION = "0.
|
|
2
|
+
export declare const PACKAGE_VERSION = "0.35.0";
|
|
3
3
|
export declare function createServer(): McpServer;
|
|
4
4
|
export declare function startStdioServer(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { handlePair } from "./tools/pair.js";
|
|
|
8
8
|
import { resolveTargetDevice } from "./tools/resolve.js";
|
|
9
9
|
import { buildControlToolDescription, buildSystemToolDescription, buildScreenshotToolDescription, formatDeviceStatus, extractDynamic, } from "./core/device.js";
|
|
10
10
|
import { registry } from "./core/registry.js";
|
|
11
|
-
export const PACKAGE_VERSION = "0.
|
|
11
|
+
export const PACKAGE_VERSION = "0.35.0";
|
|
12
12
|
function errorResult(message) {
|
|
13
13
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
14
14
|
}
|