adhdev 0.9.76-rc.4 → 0.9.76-rc.6
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/cli/index.js +131 -49
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +121 -41
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/vendor/mcp-server/index.js +160 -20
- package/vendor/mcp-server/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adhdev",
|
|
3
|
-
"version": "0.9.76-rc.
|
|
3
|
+
"version": "0.9.76-rc.6",
|
|
4
4
|
"description": "ADHDev — Agent Dashboard Hub for Dev. Remote-control AI coding agents from anywhere.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"node": ">=18"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@adhdev/daemon-core": "0.9.76-rc.
|
|
50
|
+
"@adhdev/daemon-core": "0.9.76-rc.6",
|
|
51
51
|
"@adhdev/ghostty-vt-node": "*",
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
53
53
|
"@xterm/addon-serialize": "^0.14.0",
|
|
@@ -6,6 +6,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
9
13
|
var __copyProps = (to, from, except, desc) => {
|
|
10
14
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
15
|
for (let key of __getOwnPropNames(from))
|
|
@@ -22,6 +26,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
26
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
27
|
mod
|
|
24
28
|
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var index_exports = {};
|
|
33
|
+
__export(index_exports, {
|
|
34
|
+
parseArgs: () => parseArgs
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
25
37
|
|
|
26
38
|
// src/server.ts
|
|
27
39
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
@@ -234,6 +246,104 @@ var CloudTransport = class {
|
|
|
234
246
|
}
|
|
235
247
|
};
|
|
236
248
|
|
|
249
|
+
// src/transports/ipc.ts
|
|
250
|
+
var DEFAULT_IPC_PORT = 19222;
|
|
251
|
+
var DEFAULT_IPC_PATH = "/ipc";
|
|
252
|
+
var IpcTransport = class {
|
|
253
|
+
port;
|
|
254
|
+
path;
|
|
255
|
+
constructor(opts = {}) {
|
|
256
|
+
this.port = opts.port ?? DEFAULT_IPC_PORT;
|
|
257
|
+
this.path = opts.path || DEFAULT_IPC_PATH;
|
|
258
|
+
}
|
|
259
|
+
async ping() {
|
|
260
|
+
try {
|
|
261
|
+
const res = await fetch(`http://127.0.0.1:${this.port}/health`);
|
|
262
|
+
return res.ok;
|
|
263
|
+
} catch {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async getStatus() {
|
|
268
|
+
return this.command("get_status_metadata");
|
|
269
|
+
}
|
|
270
|
+
async command(type, args = {}) {
|
|
271
|
+
return this.sendIpcCommand(type, args);
|
|
272
|
+
}
|
|
273
|
+
async meshCommand(targetDaemonId, command, args = {}) {
|
|
274
|
+
return this.sendIpcCommand("mesh_relay_command", {
|
|
275
|
+
targetDaemonId,
|
|
276
|
+
command,
|
|
277
|
+
args
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
async sendIpcCommand(type, args) {
|
|
281
|
+
const WebSocketCtor = globalThis.WebSocket;
|
|
282
|
+
if (!WebSocketCtor) {
|
|
283
|
+
throw new Error("WebSocket is not available in this Node runtime; Node 20+ is required for daemon IPC mode");
|
|
284
|
+
}
|
|
285
|
+
return new Promise((resolve, reject) => {
|
|
286
|
+
const requestId = `mcp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
287
|
+
const ws = new WebSocketCtor(`ws://127.0.0.1:${this.port}${this.path}`);
|
|
288
|
+
let settled = false;
|
|
289
|
+
const finish = (fn) => {
|
|
290
|
+
if (settled) return;
|
|
291
|
+
settled = true;
|
|
292
|
+
clearTimeout(timeout);
|
|
293
|
+
try {
|
|
294
|
+
ws.close();
|
|
295
|
+
} catch {
|
|
296
|
+
}
|
|
297
|
+
fn();
|
|
298
|
+
};
|
|
299
|
+
const timeout = setTimeout(() => {
|
|
300
|
+
finish(() => reject(new Error(`Daemon IPC command '${type}' timed out after 15s`)));
|
|
301
|
+
}, 15e3);
|
|
302
|
+
const send = () => {
|
|
303
|
+
ws.send(JSON.stringify({
|
|
304
|
+
type: "ext:command",
|
|
305
|
+
payload: { command: type, args, requestId }
|
|
306
|
+
}));
|
|
307
|
+
};
|
|
308
|
+
ws.addEventListener("open", () => {
|
|
309
|
+
ws.send(JSON.stringify({
|
|
310
|
+
type: "ext:register",
|
|
311
|
+
payload: {
|
|
312
|
+
ideType: "mcp-server",
|
|
313
|
+
ideVersion: "1.0.0",
|
|
314
|
+
extensionVersion: "1.0.0",
|
|
315
|
+
instanceId: `mcp-server-${process.pid}`,
|
|
316
|
+
machineId: "mcp-server",
|
|
317
|
+
workspaceFolders: []
|
|
318
|
+
}
|
|
319
|
+
}));
|
|
320
|
+
});
|
|
321
|
+
ws.addEventListener("message", (event) => {
|
|
322
|
+
try {
|
|
323
|
+
const raw = typeof event.data === "string" ? event.data : String(event.data);
|
|
324
|
+
const msg = JSON.parse(raw);
|
|
325
|
+
if (msg?.type === "daemon:welcome") {
|
|
326
|
+
send();
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (msg?.type !== "ext:command_result") return;
|
|
330
|
+
if (msg?.payload?.requestId !== requestId) return;
|
|
331
|
+
const payload = msg.payload;
|
|
332
|
+
if (payload?.success === false) {
|
|
333
|
+
finish(() => reject(new Error(payload.error || `Daemon IPC command '${type}' failed`)));
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
finish(() => resolve(payload?.result ?? payload));
|
|
337
|
+
} catch {
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
ws.addEventListener("error", () => {
|
|
341
|
+
finish(() => reject(new Error(`Cannot connect to daemon IPC at ws://127.0.0.1:${this.port}${this.path}`)));
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
237
347
|
// src/transports/mode.ts
|
|
238
348
|
function isLocalTransport(transport) {
|
|
239
349
|
return typeof transport.command === "function";
|
|
@@ -1270,6 +1380,15 @@ function findNode(mesh, nodeId) {
|
|
|
1270
1380
|
if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
|
|
1271
1381
|
return node;
|
|
1272
1382
|
}
|
|
1383
|
+
async function commandForNode(ctx, node, command, args = {}) {
|
|
1384
|
+
if (ctx.transport instanceof IpcTransport && node.daemonId) {
|
|
1385
|
+
return ctx.transport.meshCommand(node.daemonId, command, args);
|
|
1386
|
+
}
|
|
1387
|
+
if (isLocalTransport(ctx.transport)) {
|
|
1388
|
+
return ctx.transport.command(command, args);
|
|
1389
|
+
}
|
|
1390
|
+
throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
|
|
1391
|
+
}
|
|
1273
1392
|
var MESH_STATUS_TOOL = {
|
|
1274
1393
|
name: "mesh_status",
|
|
1275
1394
|
description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions. Use this to decide which node to send work to.",
|
|
@@ -1387,7 +1506,7 @@ async function meshStatus(ctx) {
|
|
|
1387
1506
|
entry.isDirty = status?.isDirty;
|
|
1388
1507
|
entry.uncommittedChanges = status?.uncommittedChanges ?? 0;
|
|
1389
1508
|
} else if (isLocalTransport(transport)) {
|
|
1390
|
-
const statusResult = await
|
|
1509
|
+
const statusResult = await commandForNode(ctx, node, "git_status", { workspace: node.workspace });
|
|
1391
1510
|
const status = statusResult?.status ?? statusResult;
|
|
1392
1511
|
entry.health = status?.isGitRepo ? status?.isDirty ? "dirty" : "online" : "degraded";
|
|
1393
1512
|
entry.branch = status?.branch;
|
|
@@ -1433,9 +1552,10 @@ async function meshSendTask(ctx, args) {
|
|
|
1433
1552
|
return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
|
|
1434
1553
|
}
|
|
1435
1554
|
if (isLocalTransport(ctx.transport)) {
|
|
1436
|
-
await ctx
|
|
1555
|
+
await commandForNode(ctx, node, "send_chat", {
|
|
1437
1556
|
message: args.message,
|
|
1438
|
-
sessionId: args.session_id
|
|
1557
|
+
sessionId: args.session_id,
|
|
1558
|
+
targetSessionId: args.session_id
|
|
1439
1559
|
});
|
|
1440
1560
|
return JSON.stringify({ success: true, nodeId: args.node_id, sessionId: args.session_id });
|
|
1441
1561
|
} else {
|
|
@@ -1443,10 +1563,11 @@ async function meshSendTask(ctx, args) {
|
|
|
1443
1563
|
}
|
|
1444
1564
|
}
|
|
1445
1565
|
async function meshReadChat(ctx, args) {
|
|
1446
|
-
findNode(ctx.mesh, args.node_id);
|
|
1566
|
+
const node = findNode(ctx.mesh, args.node_id);
|
|
1447
1567
|
if (isLocalTransport(ctx.transport)) {
|
|
1448
|
-
const result = await ctx
|
|
1568
|
+
const result = await commandForNode(ctx, node, "read_chat", {
|
|
1449
1569
|
sessionId: args.session_id,
|
|
1570
|
+
targetSessionId: args.session_id,
|
|
1450
1571
|
tailLimit: args.tail ?? 10
|
|
1451
1572
|
});
|
|
1452
1573
|
return JSON.stringify(result, null, 2);
|
|
@@ -1457,7 +1578,7 @@ async function meshReadChat(ctx, args) {
|
|
|
1457
1578
|
async function meshLaunchSession(ctx, args) {
|
|
1458
1579
|
const node = findNode(ctx.mesh, args.node_id);
|
|
1459
1580
|
if (isLocalTransport(ctx.transport)) {
|
|
1460
|
-
const result = await ctx
|
|
1581
|
+
const result = await commandForNode(ctx, node, "launch_cli", {
|
|
1461
1582
|
cliType: args.type,
|
|
1462
1583
|
dir: node.workspace,
|
|
1463
1584
|
settings: {
|
|
@@ -1481,10 +1602,10 @@ async function meshGitStatus(ctx, args) {
|
|
|
1481
1602
|
diff: result?.diff ?? null
|
|
1482
1603
|
}, null, 2);
|
|
1483
1604
|
} else if (isLocalTransport(ctx.transport)) {
|
|
1484
|
-
const statusResult = await ctx
|
|
1605
|
+
const statusResult = await commandForNode(ctx, node, "git_status", {
|
|
1485
1606
|
workspace: node.workspace
|
|
1486
1607
|
});
|
|
1487
|
-
const diffResult = await ctx
|
|
1608
|
+
const diffResult = await commandForNode(ctx, node, "git_diff_summary", {
|
|
1488
1609
|
workspace: node.workspace
|
|
1489
1610
|
});
|
|
1490
1611
|
return JSON.stringify({
|
|
@@ -1503,7 +1624,7 @@ async function meshCheckpoint(ctx, args) {
|
|
|
1503
1624
|
return JSON.stringify({ error: `Node '${args.node_id}' is read-only \u2014 cannot checkpoint` });
|
|
1504
1625
|
}
|
|
1505
1626
|
if (isLocalTransport(ctx.transport)) {
|
|
1506
|
-
const result = await ctx
|
|
1627
|
+
const result = await commandForNode(ctx, node, "git_checkpoint", {
|
|
1507
1628
|
workspace: node.workspace,
|
|
1508
1629
|
message: args.message
|
|
1509
1630
|
});
|
|
@@ -1513,10 +1634,11 @@ async function meshCheckpoint(ctx, args) {
|
|
|
1513
1634
|
}
|
|
1514
1635
|
}
|
|
1515
1636
|
async function meshApprove(ctx, args) {
|
|
1516
|
-
findNode(ctx.mesh, args.node_id);
|
|
1637
|
+
const node = findNode(ctx.mesh, args.node_id);
|
|
1517
1638
|
if (isLocalTransport(ctx.transport)) {
|
|
1518
|
-
const result = await ctx
|
|
1639
|
+
const result = await commandForNode(ctx, node, "resolve_action", {
|
|
1519
1640
|
sessionId: args.session_id,
|
|
1641
|
+
targetSessionId: args.session_id,
|
|
1520
1642
|
action: args.action === "reject" ? "reject" : "approve"
|
|
1521
1643
|
});
|
|
1522
1644
|
return JSON.stringify(result, null, 2);
|
|
@@ -1527,10 +1649,10 @@ async function meshApprove(ctx, args) {
|
|
|
1527
1649
|
|
|
1528
1650
|
// src/server.ts
|
|
1529
1651
|
async function startMcpServer(opts) {
|
|
1530
|
-
const transport = opts.mode === "cloud" ? new CloudTransport({ apiKey: opts.apiKey, baseUrl: opts.baseUrl }) : new LocalTransport({ port: opts.port, password: opts.password });
|
|
1652
|
+
const transport = opts.mode === "cloud" ? new CloudTransport({ apiKey: opts.apiKey, baseUrl: opts.baseUrl }) : opts.mode === "ipc" ? new IpcTransport({ port: opts.port }) : new LocalTransport({ port: opts.port, password: opts.password });
|
|
1531
1653
|
const alive = await transport.ping();
|
|
1532
1654
|
if (!alive) {
|
|
1533
|
-
const hint = opts.mode === "local" ? `Make sure the standalone daemon is running (adhdev standalone or npx @adhdev/daemon-standalone).` : `Check your API key and network connectivity.`;
|
|
1655
|
+
const hint = opts.mode === "local" ? `Make sure the standalone daemon is running (adhdev standalone or npx @adhdev/daemon-standalone).` : opts.mode === "ipc" ? `Make sure the cloud daemon is running with local IPC enabled (adhdev daemon).` : `Check your API key and network connectivity.`;
|
|
1534
1656
|
process.stderr.write(`[adhdev-mcp] Cannot reach ${opts.mode} daemon. ${hint}
|
|
1535
1657
|
`);
|
|
1536
1658
|
process.exit(1);
|
|
@@ -1613,7 +1735,7 @@ async function startMcpServer(opts) {
|
|
|
1613
1735
|
`);
|
|
1614
1736
|
}
|
|
1615
1737
|
}
|
|
1616
|
-
if (!mesh && transport instanceof LocalTransport) {
|
|
1738
|
+
if (!mesh && (transport instanceof LocalTransport || transport instanceof IpcTransport)) {
|
|
1617
1739
|
try {
|
|
1618
1740
|
const result = await transport.command("get_mesh", { meshId: opts.meshId });
|
|
1619
1741
|
if (result?.success && result.mesh) {
|
|
@@ -1817,13 +1939,14 @@ async function startMcpServer(opts) {
|
|
|
1817
1939
|
}
|
|
1818
1940
|
|
|
1819
1941
|
// src/index.ts
|
|
1820
|
-
function parseArgs(argv) {
|
|
1942
|
+
function parseArgs(argv, env = process.env) {
|
|
1821
1943
|
const args = argv.slice(2);
|
|
1822
1944
|
let apiKey;
|
|
1823
1945
|
let baseUrl;
|
|
1824
1946
|
let port;
|
|
1825
1947
|
let password;
|
|
1826
1948
|
let meshId;
|
|
1949
|
+
let explicitMode;
|
|
1827
1950
|
for (let i = 0; i < args.length; i++) {
|
|
1828
1951
|
const arg = args[i];
|
|
1829
1952
|
if ((arg === "--api-key" || arg === "-k") && args[i + 1]) {
|
|
@@ -1832,6 +1955,12 @@ function parseArgs(argv) {
|
|
|
1832
1955
|
apiKey = arg.slice("--api-key=".length);
|
|
1833
1956
|
} else if (arg === "--base-url" && args[i + 1]) {
|
|
1834
1957
|
baseUrl = args[++i];
|
|
1958
|
+
} else if (arg === "--mode" && args[i + 1]) {
|
|
1959
|
+
const value = String(args[++i]).trim();
|
|
1960
|
+
if (value === "local" || value === "cloud" || value === "ipc") explicitMode = value;
|
|
1961
|
+
} else if (arg?.startsWith("--mode=")) {
|
|
1962
|
+
const value = arg.slice("--mode=".length).trim();
|
|
1963
|
+
if (value === "local" || value === "cloud" || value === "ipc") explicitMode = value;
|
|
1835
1964
|
} else if (arg === "--port" && args[i + 1]) {
|
|
1836
1965
|
port = Number(args[++i]);
|
|
1837
1966
|
} else if (arg?.startsWith("--port=")) {
|
|
@@ -1847,10 +1976,14 @@ function parseArgs(argv) {
|
|
|
1847
1976
|
process.exit(0);
|
|
1848
1977
|
}
|
|
1849
1978
|
}
|
|
1850
|
-
if (!apiKey &&
|
|
1851
|
-
if (!password &&
|
|
1852
|
-
if (!meshId &&
|
|
1853
|
-
|
|
1979
|
+
if (!apiKey && env.ADHDEV_API_KEY) apiKey = env.ADHDEV_API_KEY;
|
|
1980
|
+
if (!password && env.ADHDEV_PASSWORD) password = env.ADHDEV_PASSWORD;
|
|
1981
|
+
if (!meshId && env.ADHDEV_MESH_ID) meshId = env.ADHDEV_MESH_ID;
|
|
1982
|
+
if (!explicitMode && env.ADHDEV_MCP_TRANSPORT) {
|
|
1983
|
+
const value = env.ADHDEV_MCP_TRANSPORT.trim();
|
|
1984
|
+
if (value === "local" || value === "cloud" || value === "ipc") explicitMode = value;
|
|
1985
|
+
}
|
|
1986
|
+
const mode = explicitMode || (apiKey ? "cloud" : meshId && env.ADHDEV_INLINE_MESH ? "ipc" : "local");
|
|
1854
1987
|
return { mode, port, password, apiKey, baseUrl, meshId };
|
|
1855
1988
|
}
|
|
1856
1989
|
function printHelp() {
|
|
@@ -1860,10 +1993,12 @@ adhdev-mcp \u2014 ADHDev MCP Server
|
|
|
1860
1993
|
Usage:
|
|
1861
1994
|
adhdev-mcp Local mode (requires standalone daemon)
|
|
1862
1995
|
adhdev-mcp --api-key <key> Cloud mode (ADHDev cloud API)
|
|
1996
|
+
adhdev-mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
|
|
1863
1997
|
adhdev-mcp --repo-mesh <mesh_id> Mesh mode (coordinator-scoped tools)
|
|
1864
1998
|
|
|
1865
1999
|
Options:
|
|
1866
|
-
--
|
|
2000
|
+
--mode <mode> Transport: local, cloud, or ipc
|
|
2001
|
+
--port <n> Standalone or IPC daemon port (defaults: local 3847, ipc 19222)
|
|
1867
2002
|
--password <pass> Standalone daemon password (if set)
|
|
1868
2003
|
--api-key <key> ADHDev cloud API key (switches to cloud mode)
|
|
1869
2004
|
--base-url <url> Override cloud API base URL
|
|
@@ -1874,6 +2009,7 @@ Environment variables:
|
|
|
1874
2009
|
ADHDEV_API_KEY API key (cloud mode)
|
|
1875
2010
|
ADHDEV_PASSWORD Daemon password (local mode)
|
|
1876
2011
|
ADHDEV_MESH_ID Mesh ID (mesh mode)
|
|
2012
|
+
ADHDEV_MCP_TRANSPORT Transport: local, cloud, or ipc
|
|
1877
2013
|
|
|
1878
2014
|
Standard tools: list_daemons, list_sessions, launch_session, stop_session, check_pending, read_chat, send_chat, approve, git_status, git_log, git_diff, git_checkpoint, git_push, screenshot
|
|
1879
2015
|
Mesh tools: mesh_status, mesh_list_nodes, mesh_send_task, mesh_read_chat, mesh_launch_session, mesh_git_status, mesh_checkpoint, mesh_approve
|
|
@@ -1884,4 +2020,8 @@ startMcpServer(parseArgs(process.argv)).catch((err) => {
|
|
|
1884
2020
|
`);
|
|
1885
2021
|
process.exit(1);
|
|
1886
2022
|
});
|
|
2023
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2024
|
+
0 && (module.exports = {
|
|
2025
|
+
parseArgs
|
|
2026
|
+
});
|
|
1887
2027
|
//# sourceMappingURL=index.js.map
|