@adhdev/daemon-core 0.9.77-rc.4 → 0.9.77-rc.41
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/boot/daemon-lifecycle.d.ts +3 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +4 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +14 -4
- package/dist/commands/mesh-coordinator.d.ts +10 -0
- package/dist/commands/router.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +424 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +421 -45
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events.d.ts +2 -7
- package/dist/mesh/mesh-work-queue.d.ts +35 -1
- package/dist/shared-types.d.ts +14 -0
- package/package.json +1 -1
- package/src/boot/daemon-lifecycle.ts +5 -0
- package/src/cli-adapters/provider-cli-adapter.ts +34 -4
- package/src/cli-adapters/provider-cli-shared.ts +14 -4
- package/src/commands/cli-manager.ts +0 -4
- package/src/commands/mesh-coordinator.ts +55 -7
- package/src/commands/router.ts +193 -4
- package/src/commands/stream-commands.ts +8 -1
- package/src/index.ts +2 -2
- package/src/mesh/mesh-events.ts +147 -20
- package/src/mesh/mesh-work-queue.ts +103 -6
- package/src/providers/cli-provider-instance.ts +2 -0
- package/src/shared-types.ts +14 -0
|
@@ -8,17 +8,12 @@ export interface PendingMeshCoordinatorEvent {
|
|
|
8
8
|
}
|
|
9
9
|
/** Drain and return all pending coordinator events, clearing the queue. */
|
|
10
10
|
export declare function drainPendingMeshCoordinatorEvents(): PendingMeshCoordinatorEvent[];
|
|
11
|
-
export declare function tryAssignQueueTask(components:
|
|
12
|
-
cliManager: any;
|
|
13
|
-
}, meshId: string, nodeId: string, sessionId: string, providerType: string): boolean;
|
|
11
|
+
export declare function tryAssignQueueTask(components: DaemonComponents, meshId: string, nodeId: string, sessionId: string, providerType: string): boolean;
|
|
14
12
|
/**
|
|
15
13
|
* Triggers a queue check for all nodes in the mesh.
|
|
16
14
|
* Called when a new task is enqueued, in case nodes are already idle.
|
|
17
15
|
*/
|
|
18
|
-
export declare function triggerMeshQueue(components:
|
|
19
|
-
instanceManager: any;
|
|
20
|
-
cliManager: any;
|
|
21
|
-
}, meshId: string): void;
|
|
16
|
+
export declare function triggerMeshQueue(components: DaemonComponents, meshId: string): void;
|
|
22
17
|
export declare function handleMeshForwardEvent(components: DaemonComponents, payload: Record<string, unknown>): {
|
|
23
18
|
success: boolean;
|
|
24
19
|
forwarded: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed';
|
|
1
|
+
export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
|
|
2
2
|
export interface MeshWorkQueueEntry {
|
|
3
3
|
id: string;
|
|
4
4
|
meshId: string;
|
|
@@ -6,10 +6,19 @@ export interface MeshWorkQueueEntry {
|
|
|
6
6
|
status: MeshTaskStatus;
|
|
7
7
|
/** If specified, only this node can claim the task (used by legacy mesh_send_task) */
|
|
8
8
|
targetNodeId?: string;
|
|
9
|
+
/** If specified, only this runtime session can claim the task */
|
|
10
|
+
targetSessionId?: string;
|
|
9
11
|
/** The node that actually claimed and is executing the task */
|
|
10
12
|
assignedNodeId?: string;
|
|
11
13
|
/** The session currently executing the task */
|
|
12
14
|
assignedSessionId?: string;
|
|
15
|
+
/** Human/operator reason for terminal cancellation. */
|
|
16
|
+
cancelReason?: string;
|
|
17
|
+
cancelledAt?: string;
|
|
18
|
+
/** Human/operator reason for manually requeueing a task. */
|
|
19
|
+
requeueReason?: string;
|
|
20
|
+
requeuedAt?: string;
|
|
21
|
+
requeueCount?: number;
|
|
13
22
|
createdAt: string;
|
|
14
23
|
updatedAt: string;
|
|
15
24
|
}
|
|
@@ -18,6 +27,7 @@ export interface MeshWorkQueueEntry {
|
|
|
18
27
|
*/
|
|
19
28
|
export declare function enqueueTask(meshId: string, message: string, opts?: {
|
|
20
29
|
targetNodeId?: string;
|
|
30
|
+
targetSessionId?: string;
|
|
21
31
|
}): MeshWorkQueueEntry;
|
|
22
32
|
/**
|
|
23
33
|
* Get all tasks in the queue, optionally filtered by status.
|
|
@@ -34,6 +44,23 @@ export declare function claimNextTask(meshId: string, nodeId: string, sessionId:
|
|
|
34
44
|
* Used when a session completes, fails, or stalls.
|
|
35
45
|
*/
|
|
36
46
|
export declare function updateTaskStatus(meshId: string, taskId: string, status: MeshTaskStatus): MeshWorkQueueEntry | null;
|
|
47
|
+
/**
|
|
48
|
+
* Mark a queue task as manually cancelled without deleting audit history.
|
|
49
|
+
*/
|
|
50
|
+
export declare function cancelTask(meshId: string, taskId: string, opts?: {
|
|
51
|
+
reason?: string;
|
|
52
|
+
}): MeshWorkQueueEntry | null;
|
|
53
|
+
/**
|
|
54
|
+
* Return a queue task to pending for retry. By default, dead session targeting
|
|
55
|
+
* and assigned ownership are cleared so stale assignments do not strand again.
|
|
56
|
+
*/
|
|
57
|
+
export declare function requeueTask(meshId: string, taskId: string, opts?: {
|
|
58
|
+
reason?: string;
|
|
59
|
+
targetNodeId?: string;
|
|
60
|
+
targetSessionId?: string;
|
|
61
|
+
clearTargetNode?: boolean;
|
|
62
|
+
clearTargetSession?: boolean;
|
|
63
|
+
}): MeshWorkQueueEntry | null;
|
|
37
64
|
/**
|
|
38
65
|
* Update the status of the task currently assigned to a specific session.
|
|
39
66
|
*/
|
|
@@ -43,6 +70,13 @@ export interface MeshWorkQueueStats {
|
|
|
43
70
|
assigned: number;
|
|
44
71
|
completed: number;
|
|
45
72
|
failed: number;
|
|
73
|
+
cancelled: number;
|
|
74
|
+
activeAssignments: Array<{
|
|
75
|
+
id: string;
|
|
76
|
+
nodeId?: string;
|
|
77
|
+
sessionId?: string;
|
|
78
|
+
message: string;
|
|
79
|
+
}>;
|
|
46
80
|
}
|
|
47
81
|
/**
|
|
48
82
|
* Return aggregate queue statistics for the given mesh.
|
package/dist/shared-types.d.ts
CHANGED
|
@@ -297,6 +297,13 @@ export interface SessionEntry {
|
|
|
297
297
|
assigned: number;
|
|
298
298
|
completed: number;
|
|
299
299
|
failed: number;
|
|
300
|
+
cancelled?: number;
|
|
301
|
+
activeAssignments?: Array<{
|
|
302
|
+
id: string;
|
|
303
|
+
nodeId?: string;
|
|
304
|
+
sessionId?: string;
|
|
305
|
+
message: string;
|
|
306
|
+
}>;
|
|
300
307
|
};
|
|
301
308
|
}
|
|
302
309
|
/**
|
|
@@ -341,6 +348,13 @@ export interface CompactSessionEntry {
|
|
|
341
348
|
assigned: number;
|
|
342
349
|
completed: number;
|
|
343
350
|
failed: number;
|
|
351
|
+
cancelled?: number;
|
|
352
|
+
activeAssignments?: Array<{
|
|
353
|
+
id: string;
|
|
354
|
+
nodeId?: string;
|
|
355
|
+
sessionId?: string;
|
|
356
|
+
message: string;
|
|
357
|
+
}>;
|
|
344
358
|
};
|
|
345
359
|
}
|
|
346
360
|
export type VersionUpdateReason = 'force_update_below' | 'major_minor_mismatch' | 'patch_mismatch' | 'daemon_ahead';
|
package/package.json
CHANGED
|
@@ -83,6 +83,9 @@ export interface DaemonInitConfig {
|
|
|
83
83
|
|
|
84
84
|
/** Fired before send_chat is dispatched — used for turn snapshot hooks */
|
|
85
85
|
onBeforeSendChat?: (params: { workspace: string; sessionId: string }) => void;
|
|
86
|
+
|
|
87
|
+
/** Relays a command to a remote mesh node daemon */
|
|
88
|
+
dispatchMeshCommand?: (daemonId: string, command: string, args: Record<string, unknown>) => Promise<any>;
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
// ─── Result ───
|
|
@@ -100,6 +103,7 @@ export interface DaemonComponents {
|
|
|
100
103
|
sessionRegistry: SessionRegistry;
|
|
101
104
|
detectedIdes: { value: IDEInfo[] };
|
|
102
105
|
refreshProviderAvailability: (providerType?: string) => Promise<void>;
|
|
106
|
+
dispatchMeshCommand?: (daemonId: string, command: string, args: Record<string, unknown>) => Promise<any>;
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
export interface DaemonDevSupportOptions {
|
|
@@ -331,6 +335,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
331
335
|
sessionRegistry,
|
|
332
336
|
detectedIdes: detectedIdesRef,
|
|
333
337
|
refreshProviderAvailability,
|
|
338
|
+
dispatchMeshCommand: config.dispatchMeshCommand,
|
|
334
339
|
};
|
|
335
340
|
|
|
336
341
|
// 11. Setup Mesh Event Forwarding
|
|
@@ -195,6 +195,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
195
195
|
|
|
196
196
|
// ─── CLI Scripts (script-based parsing) ───
|
|
197
197
|
private cliScripts: CliScripts;
|
|
198
|
+
/** Per-session opaque state object created by cliScripts.createState(), reset on stop. */
|
|
199
|
+
private scriptState: unknown = null;
|
|
198
200
|
private runtimeSettings: Record<string, any> = {};
|
|
199
201
|
/** Full accumulated rendered PTY transcript for parser/readback use */
|
|
200
202
|
private accumulatedBuffer: string = '';
|
|
@@ -223,6 +225,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
223
225
|
currentTurnScope: TurnParseScope | null;
|
|
224
226
|
recentOutputBuffer: string;
|
|
225
227
|
accumulatedBuffer: string;
|
|
228
|
+
accumulatedRawBufferKey: string;
|
|
226
229
|
screenText: string;
|
|
227
230
|
currentStatus: CliSessionStatus['status'];
|
|
228
231
|
activeModal: { message: string; buttons: string[] } | null;
|
|
@@ -297,14 +300,23 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
297
300
|
this.lastScreenSnapshotReadAt = Number.NEGATIVE_INFINITY;
|
|
298
301
|
}
|
|
299
302
|
|
|
303
|
+
private getAccumulatedRawBufferCacheKey(): string {
|
|
304
|
+
return this.accumulatedRawBuffer
|
|
305
|
+
.replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, '')
|
|
306
|
+
.replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, '')
|
|
307
|
+
.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '');
|
|
308
|
+
}
|
|
309
|
+
|
|
300
310
|
private getFreshParsedStatusCache(): any | null {
|
|
301
311
|
const cached = this.parsedStatusCache;
|
|
312
|
+
const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
|
|
302
313
|
if (
|
|
303
314
|
cached
|
|
304
315
|
&& cached.responseBuffer === this.responseBuffer
|
|
305
316
|
&& cached.currentTurnScope === this.currentTurnScope
|
|
306
317
|
&& cached.recentOutputBuffer === this.recentOutputBuffer
|
|
307
318
|
&& cached.accumulatedBuffer === this.accumulatedBuffer
|
|
319
|
+
&& cached.accumulatedRawBufferKey === accumulatedRawBufferKey
|
|
308
320
|
&& cached.screenText === this.lastScreenText
|
|
309
321
|
&& cached.currentStatus === this.currentStatus
|
|
310
322
|
&& cached.activeModal === this.activeModal
|
|
@@ -477,6 +489,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
477
489
|
this.cliScripts = scripts;
|
|
478
490
|
this.parsedStatusCache = null;
|
|
479
491
|
this.parseErrorMessage = null;
|
|
492
|
+
// Initialize per-session state: createState() is called once here and on script reload.
|
|
493
|
+
// The returned object lives until the PTY exits (scriptState = null on exit).
|
|
494
|
+
this.scriptState = typeof scripts.createState === 'function' ? scripts.createState() : null;
|
|
480
495
|
const scriptNames = listCliScriptNames(scripts);
|
|
481
496
|
LOG.info('CLI', `[${this.cliType}] CLI scripts injected: [${scriptNames.join(', ')}]`);
|
|
482
497
|
}
|
|
@@ -610,6 +625,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
610
625
|
this.ready = false;
|
|
611
626
|
this.startupParseGate = false;
|
|
612
627
|
this.spawnAt = 0;
|
|
628
|
+
this.scriptState = null;
|
|
613
629
|
this.onStatusChange?.();
|
|
614
630
|
});
|
|
615
631
|
|
|
@@ -1450,6 +1466,14 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1450
1466
|
|
|
1451
1467
|
// ─── Script Execution ──────────────────────────
|
|
1452
1468
|
|
|
1469
|
+
private invokeCliScript<T>(script: Function, input: any): T {
|
|
1470
|
+
const hasStateFactory = typeof this.cliScripts?.createState === 'function';
|
|
1471
|
+
const expectsStateArgument = hasStateFactory || this.scriptState !== null || script.length >= 2;
|
|
1472
|
+
return expectsStateArgument
|
|
1473
|
+
? script(this.scriptState, input)
|
|
1474
|
+
: script(input);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1453
1477
|
private runParseSession(): ParsedSession | null {
|
|
1454
1478
|
if (typeof this.cliScripts?.parseSession !== 'function') {
|
|
1455
1479
|
this.parseErrorMessage = `${this.cliType} parseSession unavailable`;
|
|
@@ -1470,7 +1494,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1470
1494
|
scope: this.currentTurnScope,
|
|
1471
1495
|
runtimeSettings: this.runtimeSettings,
|
|
1472
1496
|
});
|
|
1473
|
-
const session = this.
|
|
1497
|
+
const session = this.invokeCliScript<ParsedSession | null>(
|
|
1498
|
+
this.cliScripts.parseSession,
|
|
1499
|
+
{ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) },
|
|
1500
|
+
);
|
|
1474
1501
|
this.parseErrorMessage = null;
|
|
1475
1502
|
return session && typeof session === 'object' ? session : null;
|
|
1476
1503
|
} catch (e: any) {
|
|
@@ -1485,7 +1512,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1485
1512
|
if (!this.cliScripts?.detectStatus) return null;
|
|
1486
1513
|
try {
|
|
1487
1514
|
const screenText = this.terminalScreen.getText();
|
|
1488
|
-
const status = this.cliScripts.detectStatus
|
|
1515
|
+
const status = this.invokeCliScript<string | null>(this.cliScripts.detectStatus, {
|
|
1489
1516
|
tail: text.slice(-500),
|
|
1490
1517
|
screenText,
|
|
1491
1518
|
rawBuffer: this.accumulatedRawBuffer,
|
|
@@ -1505,7 +1532,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1505
1532
|
try {
|
|
1506
1533
|
const screenText = this.terminalScreen.getText();
|
|
1507
1534
|
const buffer = screenText || this.accumulatedBuffer;
|
|
1508
|
-
return this.cliScripts.parseApproval
|
|
1535
|
+
return this.invokeCliScript<{ message: string; buttons: string[] } | null>(this.cliScripts.parseApproval, {
|
|
1509
1536
|
buffer,
|
|
1510
1537
|
screenText,
|
|
1511
1538
|
rawBuffer: this.accumulatedRawBuffer,
|
|
@@ -1570,12 +1597,14 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1570
1597
|
const screenText = this.readTerminalScreenText();
|
|
1571
1598
|
const parseScreenText = this.getParseScreenText(screenText);
|
|
1572
1599
|
const cached = this.parsedStatusCache;
|
|
1600
|
+
const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
|
|
1573
1601
|
if (
|
|
1574
1602
|
cached
|
|
1575
1603
|
&& cached.responseBuffer === this.responseBuffer
|
|
1576
1604
|
&& cached.currentTurnScope === this.currentTurnScope
|
|
1577
1605
|
&& cached.recentOutputBuffer === this.recentOutputBuffer
|
|
1578
1606
|
&& cached.accumulatedBuffer === this.accumulatedBuffer
|
|
1607
|
+
&& cached.accumulatedRawBufferKey === accumulatedRawBufferKey
|
|
1579
1608
|
&& cached.screenText === parseScreenText
|
|
1580
1609
|
&& cached.currentStatus === this.currentStatus
|
|
1581
1610
|
&& cached.activeModal === this.activeModal
|
|
@@ -1615,6 +1644,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1615
1644
|
currentTurnScope: this.currentTurnScope,
|
|
1616
1645
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
1617
1646
|
accumulatedBuffer: this.accumulatedBuffer,
|
|
1647
|
+
accumulatedRawBufferKey,
|
|
1618
1648
|
screenText: parseScreenText,
|
|
1619
1649
|
currentStatus: this.currentStatus,
|
|
1620
1650
|
activeModal: this.activeModal,
|
|
@@ -1640,7 +1670,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1640
1670
|
scope: this.currentTurnScope,
|
|
1641
1671
|
runtimeSettings: this.runtimeSettings,
|
|
1642
1672
|
});
|
|
1643
|
-
return await Promise.resolve(fn({
|
|
1673
|
+
return await Promise.resolve(fn(this.scriptState, {
|
|
1644
1674
|
...input,
|
|
1645
1675
|
args: args && typeof args === 'object' ? { ...args } : {},
|
|
1646
1676
|
}));
|
|
@@ -48,11 +48,21 @@ export interface ParsedSession {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export interface CliScripts {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Optional state factory. Called once per CLI session start (or script reload).
|
|
53
|
+
* The returned object is passed as the first argument to detectStatus, parseApproval,
|
|
54
|
+
* and parseSession on every invocation, allowing scripts to maintain per-session state
|
|
55
|
+
* (e.g. last-seen status, approval fingerprints, stability counters).
|
|
56
|
+
*
|
|
57
|
+
* Scripts that don't define createState() receive null as the state argument,
|
|
58
|
+
* making this change fully backward compatible.
|
|
59
|
+
*/
|
|
60
|
+
createState?: () => unknown;
|
|
61
|
+
parseSession?: (state: unknown, input: CliScriptInput & { tail?: string; tailScreen?: CliScreenSnapshot }) => ParsedSession | null;
|
|
62
|
+
detectStatus?: (state: unknown, input: CliStatusInput) => string | null;
|
|
63
|
+
parseApproval?: (state: unknown, input: CliApprovalInput) => { message: string; buttons: string[] } | null;
|
|
54
64
|
resolveAction?: (data: any) => string;
|
|
55
|
-
[name: string]: ((input: any) => any) | undefined;
|
|
65
|
+
[name: string]: ((state: unknown, input: any) => any) | ((data: any) => any) | (() => unknown) | undefined;
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
export interface CliScreenLine {
|
|
@@ -177,10 +177,6 @@ export function buildCoordinatorDelegatedCliLaunchOptions(
|
|
|
177
177
|
const cliArgs = Array.isArray(input.cliArgs) ? [...input.cliArgs] : [];
|
|
178
178
|
const env: Record<string, string> = { ...(input.env || {}), ...COORDINATOR_DELEGATED_ENV_UNSETS };
|
|
179
179
|
|
|
180
|
-
if (cliType === 'hermes-cli' && !hasCliArg(cliArgs, '--ignore-user-config')) {
|
|
181
|
-
cliArgs.unshift('--ignore-user-config');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
180
|
if (cliType === 'claude-cli' && !hasCliArg(cliArgs, '--mcp-config')) {
|
|
185
181
|
cliArgs.unshift('--mcp-config', ensureEmptyDelegatedMcpConfig(input.workspace));
|
|
186
182
|
}
|
|
@@ -28,6 +28,15 @@ export type MeshCoordinatorSetup =
|
|
|
28
28
|
instructions: string
|
|
29
29
|
template: string
|
|
30
30
|
}
|
|
31
|
+
| {
|
|
32
|
+
/** Provider registers MCP via its own CLI command (e.g. `codex mcp add` / `gemini mcp add`). */
|
|
33
|
+
kind: 'cli_command'
|
|
34
|
+
serverName: string
|
|
35
|
+
/** The rendered shell command to execute before launching the coordinator session. */
|
|
36
|
+
command: string
|
|
37
|
+
requiresRestart: boolean
|
|
38
|
+
instructions: string
|
|
39
|
+
}
|
|
31
40
|
| {
|
|
32
41
|
kind: 'unsupported'
|
|
33
42
|
reason: string
|
|
@@ -41,6 +50,8 @@ export interface ResolveMeshCoordinatorSetupOptions {
|
|
|
41
50
|
adhdevMcpCommand?: string
|
|
42
51
|
adhdevMcpEntryPath?: string
|
|
43
52
|
nodeExecutable?: string
|
|
53
|
+
adhdevMcpTransport?: 'local' | 'ipc'
|
|
54
|
+
adhdevMcpPort?: number
|
|
44
55
|
}
|
|
45
56
|
|
|
46
57
|
const DEFAULT_SERVER_NAME = 'adhdev-mesh'
|
|
@@ -58,6 +69,8 @@ function resolveHermesMeshCoordinatorSetup(options: ResolveMeshCoordinatorSetupO
|
|
|
58
69
|
meshId: options.meshId,
|
|
59
70
|
nodeExecutable: options.nodeExecutable,
|
|
60
71
|
adhdevMcpEntryPath: options.adhdevMcpEntryPath,
|
|
72
|
+
adhdevMcpTransport: options.adhdevMcpTransport,
|
|
73
|
+
adhdevMcpPort: options.adhdevMcpPort,
|
|
61
74
|
})
|
|
62
75
|
if (!mcpServer) {
|
|
63
76
|
return {
|
|
@@ -130,6 +143,8 @@ export function resolveMeshCoordinatorSetup(options: ResolveMeshCoordinatorSetup
|
|
|
130
143
|
meshId,
|
|
131
144
|
nodeExecutable: options.nodeExecutable,
|
|
132
145
|
adhdevMcpEntryPath: options.adhdevMcpEntryPath,
|
|
146
|
+
adhdevMcpTransport: options.adhdevMcpTransport,
|
|
147
|
+
adhdevMcpPort: options.adhdevMcpPort,
|
|
133
148
|
})
|
|
134
149
|
if (!mcpServer) {
|
|
135
150
|
return {
|
|
@@ -152,6 +167,24 @@ export function resolveMeshCoordinatorSetup(options: ResolveMeshCoordinatorSetup
|
|
|
152
167
|
if (!instructions || !template?.trim()) {
|
|
153
168
|
return { kind: 'unsupported', reason: 'Provider manual MCP setup is missing instructions or template' }
|
|
154
169
|
}
|
|
170
|
+
const renderedTemplate = renderMeshCoordinatorTemplate(template, {
|
|
171
|
+
meshId,
|
|
172
|
+
workspace,
|
|
173
|
+
serverName,
|
|
174
|
+
adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND,
|
|
175
|
+
})
|
|
176
|
+
// Detect if the template is a runnable CLI command (single line, no YAML/JSON structure).
|
|
177
|
+
// If so, use cli_command kind so the daemon can execute it automatically.
|
|
178
|
+
const isCliCommand = !renderedTemplate.trim().includes('\n') && !renderedTemplate.trim().startsWith('{')
|
|
179
|
+
if (isCliCommand) {
|
|
180
|
+
return {
|
|
181
|
+
kind: 'cli_command',
|
|
182
|
+
serverName,
|
|
183
|
+
command: renderedTemplate.trim(),
|
|
184
|
+
requiresRestart: mcpConfig.requiresRestart === true,
|
|
185
|
+
instructions: instructions,
|
|
186
|
+
}
|
|
187
|
+
}
|
|
155
188
|
return {
|
|
156
189
|
kind: 'manual',
|
|
157
190
|
serverName,
|
|
@@ -159,12 +192,7 @@ export function resolveMeshCoordinatorSetup(options: ResolveMeshCoordinatorSetup
|
|
|
159
192
|
configPathCommand: mcpConfig.configPathCommand,
|
|
160
193
|
requiresRestart: mcpConfig.requiresRestart === true,
|
|
161
194
|
instructions,
|
|
162
|
-
template:
|
|
163
|
-
meshId,
|
|
164
|
-
workspace,
|
|
165
|
-
serverName,
|
|
166
|
-
adhdevMcpCommand: options.adhdevMcpCommand || DEFAULT_ADHDEV_MCP_COMMAND,
|
|
167
|
-
}),
|
|
195
|
+
template: renderedTemplate,
|
|
168
196
|
}
|
|
169
197
|
}
|
|
170
198
|
|
|
@@ -196,17 +224,37 @@ function resolveAdhdevMcpServerLaunch(options: {
|
|
|
196
224
|
meshId: string
|
|
197
225
|
nodeExecutable?: string
|
|
198
226
|
adhdevMcpEntryPath?: string
|
|
227
|
+
adhdevMcpTransport?: 'local' | 'ipc'
|
|
228
|
+
adhdevMcpPort?: number
|
|
199
229
|
}): MeshCoordinatorMcpServerLaunch | null {
|
|
200
230
|
const entryPath = resolveAdhdevMcpEntryPath(options.adhdevMcpEntryPath)
|
|
201
231
|
if (!entryPath) return null
|
|
202
232
|
const nodeExecutable = resolveMcpNodeExecutable(options.nodeExecutable)
|
|
203
233
|
if (!nodeExecutable) return null
|
|
234
|
+
const transport = resolveMcpTransport(options.adhdevMcpTransport)
|
|
235
|
+
const args = [entryPath, '--mode', transport, '--repo-mesh', options.meshId]
|
|
236
|
+
const port = resolveMcpPort(options.adhdevMcpPort)
|
|
237
|
+
if (port !== undefined) args.push('--port', String(port))
|
|
204
238
|
return {
|
|
205
239
|
command: nodeExecutable,
|
|
206
|
-
args
|
|
240
|
+
args,
|
|
207
241
|
}
|
|
208
242
|
}
|
|
209
243
|
|
|
244
|
+
function resolveMcpTransport(explicitTransport?: 'local' | 'ipc'): 'local' | 'ipc' {
|
|
245
|
+
if (explicitTransport === 'local' || explicitTransport === 'ipc') return explicitTransport
|
|
246
|
+
const envTransport = process.env.ADHDEV_COORDINATOR_MCP_TRANSPORT?.trim()
|
|
247
|
+
return envTransport === 'local' ? 'local' : 'ipc'
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function resolveMcpPort(explicitPort?: number): number | undefined {
|
|
251
|
+
if (typeof explicitPort === 'number' && Number.isInteger(explicitPort) && explicitPort > 0) return explicitPort
|
|
252
|
+
const raw = process.env.ADHDEV_COORDINATOR_MCP_PORT?.trim()
|
|
253
|
+
if (!raw) return undefined
|
|
254
|
+
const parsed = Number(raw)
|
|
255
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined
|
|
256
|
+
}
|
|
257
|
+
|
|
210
258
|
function resolveMcpNodeExecutable(explicitExecutable?: string): string | null {
|
|
211
259
|
const explicit = explicitExecutable?.trim()
|
|
212
260
|
if (explicit) return explicit
|