@adhdev/daemon-core 0.9.82-rc.9 → 0.9.82-rc.90
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-adapters/provider-cli-adapter.d.ts +2 -0
- package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/mesh-config.d.ts +66 -1
- package/dist/index.d.ts +13 -6
- package/dist/index.js +5395 -1197
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5359 -1183
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +18 -0
- package/dist/mesh/mesh-active-work.d.ts +60 -0
- package/dist/mesh/mesh-events.d.ts +29 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +38 -1
- package/dist/mesh/mesh-work-queue.d.ts +27 -5
- package/dist/mesh/refine-config.d.ts +176 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -1
- package/dist/repo-mesh-types.d.ts +46 -0
- package/dist/status/reporter.d.ts +2 -0
- package/package.json +3 -1
- package/src/boot/daemon-lifecycle.ts +1 -0
- package/src/cli-adapters/provider-cli-adapter.ts +91 -3
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +4 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +20 -10
- package/src/commands/chat-commands.ts +454 -15
- package/src/commands/cli-manager.ts +126 -0
- package/src/commands/handler.ts +8 -1
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +2687 -435
- package/src/config/chat-history.ts +9 -7
- package/src/config/mesh-config.ts +245 -1
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/index.ts +31 -5
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +176 -0
- package/src/mesh/coordinator-prompt.ts +30 -7
- package/src/mesh/mesh-active-work.ts +243 -0
- package/src/mesh/mesh-events.ts +400 -47
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +138 -1
- package/src/mesh/mesh-work-queue.ts +199 -137
- package/src/mesh/refine-config.ts +356 -0
- package/src/providers/chat-message-normalization.ts +3 -1
- package/src/providers/cli-provider-instance.ts +91 -13
- package/src/providers/ide-provider-instance.ts +17 -3
- package/src/providers/provider-loader.ts +10 -4
- package/src/providers/read-chat-contract.ts +1 -1
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +51 -0
- package/src/status/reporter.ts +15 -0
- package/src/system/host-memory.ts +29 -12
|
@@ -80,6 +80,114 @@ export interface CliManagerDeps {
|
|
|
80
80
|
|
|
81
81
|
type CommandResult = { success: boolean;[key: string]: unknown };
|
|
82
82
|
|
|
83
|
+
const BUSY_AGENT_STATUSES = new Set(['generating', 'running', 'streaming', 'starting', 'busy', 'waiting', 'waiting_approval', 'long_generating']);
|
|
84
|
+
const ZERO_MESSAGE_STARTING_SEND_WAIT_MS = 2_000;
|
|
85
|
+
|
|
86
|
+
function normalizeAgentStatus(value: unknown): string {
|
|
87
|
+
return typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function hasNonEmptyModalButtons(activeModal: unknown): boolean {
|
|
91
|
+
const buttons = (activeModal as any)?.buttons;
|
|
92
|
+
return Array.isArray(buttons) && buttons.some((button) => String(button || '').trim().length > 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function hasAdapterPendingResponse(adapter: any): boolean {
|
|
96
|
+
if (adapter?.isWaitingForResponse === true) return true;
|
|
97
|
+
if (adapter?.currentTurnScope) return true;
|
|
98
|
+
try {
|
|
99
|
+
if (typeof adapter?.isProcessing === 'function' && adapter.isProcessing()) return true;
|
|
100
|
+
} catch { /* defensive: send guard should not fail on diagnostics */ }
|
|
101
|
+
try {
|
|
102
|
+
const partial = typeof adapter?.getPartialResponse === 'function' ? adapter.getPartialResponse() : '';
|
|
103
|
+
if (typeof partial === 'string' && partial.trim()) return true;
|
|
104
|
+
} catch { /* defensive: missing partial means no pending evidence */ }
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function countMessages(value: unknown): number {
|
|
109
|
+
return Array.isArray(value) ? value.length : 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function hasFinalAssistantMessage(value: unknown): boolean {
|
|
113
|
+
const messages = Array.isArray(value) ? value : [];
|
|
114
|
+
const last = messages[messages.length - 1] as any;
|
|
115
|
+
if (!last || last.role !== 'assistant') return false;
|
|
116
|
+
if (last.bubbleState === 'streaming') return false;
|
|
117
|
+
if (last.meta?.streaming === true) return false;
|
|
118
|
+
return typeof last.content === 'string' && last.content.trim().length > 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function hasZeroMessageStartingLaunch(adapter: any): boolean {
|
|
122
|
+
const adapterStatus = adapter?.getStatus?.({ allowParse: false }) ?? adapter?.getStatus?.() ?? {};
|
|
123
|
+
const parsedStatus = typeof adapter?.getScriptParsedStatus === 'function'
|
|
124
|
+
? adapter.getScriptParsedStatus()
|
|
125
|
+
: {};
|
|
126
|
+
const adapterRawStatus = normalizeAgentStatus(adapterStatus?.status);
|
|
127
|
+
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
128
|
+
if (adapterRawStatus !== 'starting') return false;
|
|
129
|
+
if (parsedRawStatus && parsedRawStatus !== 'starting' && parsedRawStatus !== 'generating') return false;
|
|
130
|
+
if (hasNonEmptyModalButtons(adapterStatus?.activeModal ?? adapterStatus?.modal ?? parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
|
|
131
|
+
if (countMessages(adapterStatus?.messages) > 0 || countMessages(parsedStatus?.messages) > 0) return false;
|
|
132
|
+
return !hasAdapterPendingResponse(adapter);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function hasCompletedStartingLaunch(adapter: any): boolean {
|
|
136
|
+
const adapterStatus = adapter?.getStatus?.({ allowParse: false }) ?? adapter?.getStatus?.() ?? {};
|
|
137
|
+
const adapterRawStatus = normalizeAgentStatus(adapterStatus?.status);
|
|
138
|
+
if (adapterRawStatus !== 'starting') return false;
|
|
139
|
+
if (hasAdapterPendingResponse(adapter)) return false;
|
|
140
|
+
|
|
141
|
+
const parsedStatus = typeof adapter?.getScriptParsedStatus === 'function'
|
|
142
|
+
? adapter.getScriptParsedStatus()
|
|
143
|
+
: {};
|
|
144
|
+
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
145
|
+
if (parsedRawStatus !== 'idle') return false;
|
|
146
|
+
if (hasNonEmptyModalButtons(adapterStatus?.activeModal ?? adapterStatus?.modal ?? parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
|
|
147
|
+
return hasFinalAssistantMessage(parsedStatus?.messages);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function shouldSuppressStaleParsedBusyStatus(adapterStatus: string, parsedStatus: any, adapter: any): boolean {
|
|
151
|
+
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
152
|
+
if (!BUSY_AGENT_STATUSES.has(parsedRawStatus)) return false;
|
|
153
|
+
if (adapterStatus !== 'idle') return false;
|
|
154
|
+
if (hasNonEmptyModalButtons(parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
|
|
155
|
+
return !hasAdapterPendingResponse(adapter);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function getEffectiveAgentSendStatus(adapter: any): string {
|
|
159
|
+
const adapterStatus = normalizeAgentStatus(adapter?.getStatus?.({ allowParse: false })?.status ?? adapter?.getStatus?.()?.status);
|
|
160
|
+
if (adapterStatus === 'starting' && hasCompletedStartingLaunch(adapter)) return 'idle';
|
|
161
|
+
if (adapterStatus && adapterStatus !== 'idle') return adapterStatus;
|
|
162
|
+
if (adapterStatus !== 'idle') return adapterStatus;
|
|
163
|
+
|
|
164
|
+
if (typeof adapter?.getScriptParsedStatus !== 'function') return adapterStatus;
|
|
165
|
+
try {
|
|
166
|
+
const parsedStatus = adapter.getScriptParsedStatus();
|
|
167
|
+
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
168
|
+
if (BUSY_AGENT_STATUSES.has(parsedRawStatus) && !shouldSuppressStaleParsedBusyStatus(adapterStatus, parsedStatus, adapter)) {
|
|
169
|
+
return parsedRawStatus;
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
return adapterStatus;
|
|
173
|
+
}
|
|
174
|
+
return adapterStatus;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function waitForZeroMessageStartingLaunch(adapter: any): Promise<boolean> {
|
|
178
|
+
try {
|
|
179
|
+
if (!hasZeroMessageStartingLaunch(adapter)) return false;
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
await new Promise(resolve => setTimeout(resolve, ZERO_MESSAGE_STARTING_SEND_WAIT_MS));
|
|
184
|
+
try {
|
|
185
|
+
return hasZeroMessageStartingLaunch(adapter);
|
|
186
|
+
} catch {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
83
191
|
export interface CliTransportFactoryParams {
|
|
84
192
|
runtimeId: string;
|
|
85
193
|
providerType: string;
|
|
@@ -1073,6 +1181,24 @@ export class DaemonCliManager {
|
|
|
1073
1181
|
const { adapter, key } = found;
|
|
1074
1182
|
|
|
1075
1183
|
if (action === 'send_chat') {
|
|
1184
|
+
let currentStatus = getEffectiveAgentSendStatus(adapter);
|
|
1185
|
+
if (currentStatus === 'starting' && await waitForZeroMessageStartingLaunch(adapter)) {
|
|
1186
|
+
currentStatus = 'idle';
|
|
1187
|
+
} else if (currentStatus === 'starting') {
|
|
1188
|
+
currentStatus = getEffectiveAgentSendStatus(adapter);
|
|
1189
|
+
}
|
|
1190
|
+
if (BUSY_AGENT_STATUSES.has(currentStatus)) {
|
|
1191
|
+
return {
|
|
1192
|
+
success: false,
|
|
1193
|
+
code: 'agent_runtime_busy',
|
|
1194
|
+
reason: 'agent_runtime_busy',
|
|
1195
|
+
retryable: true,
|
|
1196
|
+
retryRecommended: true,
|
|
1197
|
+
status: currentStatus,
|
|
1198
|
+
targetSessionId: args?.targetSessionId,
|
|
1199
|
+
error: `CLI agent '${agentType}' is currently ${currentStatus}; retry after the current turn finishes.`,
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1076
1202
|
const input = normalizeInputEnvelope(args?.input ? { input: args.input } : args);
|
|
1077
1203
|
const provider = this.providerLoader.resolve(agentType) || this.providerLoader.getMeta(agentType);
|
|
1078
1204
|
if (provider?.category === 'acp') {
|
package/src/commands/handler.ts
CHANGED
|
@@ -408,12 +408,19 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
408
408
|
'invoke_provider_script',
|
|
409
409
|
]);
|
|
410
410
|
|
|
411
|
+
// read_chat and get_chat_debug_bundle can serve historical transcript data even
|
|
412
|
+
// when the live session record is gone (stopped/destroyed). Allow the fallback
|
|
413
|
+
// when the provider type is known and any session identity hint is present:
|
|
414
|
+
// an explicit providerSessionId/historySessionId, or the targetSessionId itself
|
|
415
|
+
// (which getHistorySessionId already uses as a fallback history key).
|
|
416
|
+
const isReadOrDebugCmd = cmd === 'read_chat' || cmd === 'get_chat_debug_bundle';
|
|
411
417
|
const allowsInactiveReadChatFallback =
|
|
412
|
-
|
|
418
|
+
isReadOrDebugCmd
|
|
413
419
|
&& !!this._currentRoute.providerType
|
|
414
420
|
&& (
|
|
415
421
|
(typeof args?.providerSessionId === 'string' && args.providerSessionId.trim().length > 0)
|
|
416
422
|
|| (typeof args?.historySessionId === 'string' && args.historySessionId.trim().length > 0)
|
|
423
|
+
|| (typeof args?.targetSessionId === 'string' && args.targetSessionId.trim().length > 0)
|
|
417
424
|
);
|
|
418
425
|
|
|
419
426
|
if (this._currentRoute.sessionLookupFailed && sessionScopedCommands.has(cmd) && !allowsInactiveReadChatFallback) {
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process'
|
|
2
1
|
import { createHash } from 'node:crypto'
|
|
3
|
-
import { existsSync, readdirSync, realpathSync } from 'node:fs'
|
|
4
|
-
import { createRequire } from 'node:module'
|
|
5
2
|
import * as os from 'node:os'
|
|
6
|
-
import {
|
|
3
|
+
import { isAbsolute, join, resolve } from 'node:path'
|
|
7
4
|
import type { ProviderModule, MeshCoordinatorMcpConfigFormat } from '../providers/contracts.js'
|
|
8
5
|
|
|
9
6
|
export interface MeshCoordinatorMcpServerLaunch {
|
|
@@ -55,7 +52,7 @@ export interface ResolveMeshCoordinatorSetupOptions {
|
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
const DEFAULT_SERVER_NAME = 'adhdev-mesh'
|
|
58
|
-
const DEFAULT_ADHDEV_MCP_COMMAND = 'adhdev
|
|
55
|
+
const DEFAULT_ADHDEV_MCP_COMMAND = 'adhdev'
|
|
59
56
|
const HERMES_CLI_TYPE = 'hermes-cli'
|
|
60
57
|
const HERMES_MCP_CONFIG_PATH = '~/.hermes/config.yaml'
|
|
61
58
|
|
|
@@ -67,8 +64,7 @@ function isHermesProvider(provider: ProviderModule | null | undefined, cliType?:
|
|
|
67
64
|
function resolveHermesMeshCoordinatorSetup(options: ResolveMeshCoordinatorSetupOptions): MeshCoordinatorSetup {
|
|
68
65
|
const mcpServer = resolveAdhdevMcpServerLaunch({
|
|
69
66
|
meshId: options.meshId,
|
|
70
|
-
|
|
71
|
-
adhdevMcpEntryPath: options.adhdevMcpEntryPath,
|
|
67
|
+
adhdevMcpCommand: options.adhdevMcpCommand,
|
|
72
68
|
adhdevMcpTransport: options.adhdevMcpTransport,
|
|
73
69
|
adhdevMcpPort: options.adhdevMcpPort,
|
|
74
70
|
})
|
|
@@ -100,7 +96,7 @@ export function createHermesManualMeshCoordinatorSetup(meshId: string, workspace
|
|
|
100
96
|
requiresRestart: true,
|
|
101
97
|
instructions: 'Hermes CLI does not auto-import repo-local .mcp.json. Add this MCP server to Hermes config under mcp_servers, then start a fresh Hermes session.',
|
|
102
98
|
template: renderMeshCoordinatorTemplate(
|
|
103
|
-
'mcp_servers:\n {{serverName}}:\n command: {{adhdevMcpCommand}}\n args:\n - --repo-mesh\n - {{meshId}}\n enabled: true\n',
|
|
99
|
+
'mcp_servers:\n {{serverName}}:\n command: {{adhdevMcpCommand}}\n args:\n - mcp\n - --mode\n - ipc\n - --repo-mesh\n - {{meshId}}\n enabled: true\n',
|
|
104
100
|
{
|
|
105
101
|
meshId,
|
|
106
102
|
workspace,
|
|
@@ -141,8 +137,7 @@ export function resolveMeshCoordinatorSetup(options: ResolveMeshCoordinatorSetup
|
|
|
141
137
|
}
|
|
142
138
|
const mcpServer = resolveAdhdevMcpServerLaunch({
|
|
143
139
|
meshId,
|
|
144
|
-
|
|
145
|
-
adhdevMcpEntryPath: options.adhdevMcpEntryPath,
|
|
140
|
+
adhdevMcpCommand: options.adhdevMcpCommand,
|
|
146
141
|
adhdevMcpTransport: options.adhdevMcpTransport,
|
|
147
142
|
adhdevMcpPort: options.adhdevMcpPort,
|
|
148
143
|
})
|
|
@@ -222,25 +217,25 @@ function resolveMcpConfigPath(configPath: string, workspace: string): string {
|
|
|
222
217
|
|
|
223
218
|
function resolveAdhdevMcpServerLaunch(options: {
|
|
224
219
|
meshId: string
|
|
225
|
-
|
|
226
|
-
adhdevMcpEntryPath?: string
|
|
220
|
+
adhdevMcpCommand?: string
|
|
227
221
|
adhdevMcpTransport?: 'local' | 'ipc'
|
|
228
222
|
adhdevMcpPort?: number
|
|
229
223
|
}): MeshCoordinatorMcpServerLaunch | null {
|
|
230
|
-
const
|
|
231
|
-
if (!entryPath) return null
|
|
232
|
-
const nodeExecutable = resolveMcpNodeExecutable(options.nodeExecutable)
|
|
233
|
-
if (!nodeExecutable) return null
|
|
224
|
+
const command = resolveAdhdevCommand(options.adhdevMcpCommand)
|
|
234
225
|
const transport = resolveMcpTransport(options.adhdevMcpTransport)
|
|
235
|
-
const args = [
|
|
226
|
+
const args = ['mcp', '--mode', transport, '--repo-mesh', options.meshId]
|
|
236
227
|
const port = resolveMcpPort(options.adhdevMcpPort)
|
|
237
228
|
if (port !== undefined) args.push('--port', String(port))
|
|
238
229
|
return {
|
|
239
|
-
command
|
|
230
|
+
command,
|
|
240
231
|
args,
|
|
241
232
|
}
|
|
242
233
|
}
|
|
243
234
|
|
|
235
|
+
function resolveAdhdevCommand(explicitCommand?: string): string {
|
|
236
|
+
return explicitCommand?.trim() || process.env.ADHDEV_COORDINATOR_MCP_COMMAND?.trim() || DEFAULT_ADHDEV_MCP_COMMAND
|
|
237
|
+
}
|
|
238
|
+
|
|
244
239
|
function resolveMcpTransport(explicitTransport?: 'local' | 'ipc'): 'local' | 'ipc' {
|
|
245
240
|
if (explicitTransport === 'local' || explicitTransport === 'ipc') return explicitTransport
|
|
246
241
|
const envTransport = process.env.ADHDEV_COORDINATOR_MCP_TRANSPORT?.trim()
|
|
@@ -254,128 +249,3 @@ function resolveMcpPort(explicitPort?: number): number | undefined {
|
|
|
254
249
|
const parsed = Number(raw)
|
|
255
250
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined
|
|
256
251
|
}
|
|
257
|
-
|
|
258
|
-
function resolveMcpNodeExecutable(explicitExecutable?: string): string | null {
|
|
259
|
-
const explicit = explicitExecutable?.trim()
|
|
260
|
-
if (explicit) return explicit
|
|
261
|
-
|
|
262
|
-
const candidates: string[] = []
|
|
263
|
-
const addCandidate = (candidate?: string | null) => {
|
|
264
|
-
const trimmed = candidate?.trim()
|
|
265
|
-
if (!trimmed) return
|
|
266
|
-
const normalized = normalizeExistingPath(trimmed) || trimmed
|
|
267
|
-
if (!candidates.includes(normalized)) candidates.push(normalized)
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
addCandidate(process.env.ADHDEV_MCP_NODE_EXECUTABLE)
|
|
271
|
-
addCandidate(process.env.ADHDEV_NODE_EXECUTABLE)
|
|
272
|
-
addCandidate(process.env.npm_node_execpath)
|
|
273
|
-
addNodeCandidatesFromPath(process.env.PATH, addCandidate)
|
|
274
|
-
addNodeCandidatesFromNvm(os.homedir(), addCandidate)
|
|
275
|
-
addCandidate('/opt/homebrew/bin/node')
|
|
276
|
-
addCandidate('/usr/local/bin/node')
|
|
277
|
-
addCandidate('/usr/bin/node')
|
|
278
|
-
addCandidate(process.execPath)
|
|
279
|
-
|
|
280
|
-
for (const candidate of candidates) {
|
|
281
|
-
if (nodeRuntimeSupportsWebSocket(candidate)) return candidate
|
|
282
|
-
}
|
|
283
|
-
return null
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function addNodeCandidatesFromPath(pathValue: string | undefined, addCandidate: (candidate?: string | null) => void) {
|
|
287
|
-
for (const entry of (pathValue || '').split(':')) {
|
|
288
|
-
const dir = entry.trim()
|
|
289
|
-
if (!dir) continue
|
|
290
|
-
addCandidate(join(dir, 'node'))
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function addNodeCandidatesFromNvm(homeDir: string, addCandidate: (candidate?: string | null) => void) {
|
|
295
|
-
const versionsDir = join(homeDir, '.nvm', 'versions', 'node')
|
|
296
|
-
try {
|
|
297
|
-
const versionDirs = readdirSync(versionsDir, { withFileTypes: true })
|
|
298
|
-
.filter((entry) => entry.isDirectory())
|
|
299
|
-
.map((entry) => entry.name)
|
|
300
|
-
.sort(compareNodeVersionNamesDescending)
|
|
301
|
-
for (const versionDir of versionDirs) {
|
|
302
|
-
addCandidate(join(versionsDir, versionDir, 'bin', 'node'))
|
|
303
|
-
}
|
|
304
|
-
} catch {
|
|
305
|
-
// nvm is optional; PATH and process.execPath candidates still cover normal installs.
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function compareNodeVersionNamesDescending(a: string, b: string): number {
|
|
310
|
-
const parse = (value: string) => value.replace(/^v/, '').split('.').map((part) => Number.parseInt(part, 10) || 0)
|
|
311
|
-
const left = parse(a)
|
|
312
|
-
const right = parse(b)
|
|
313
|
-
for (let i = 0; i < Math.max(left.length, right.length); i++) {
|
|
314
|
-
const diff = (right[i] || 0) - (left[i] || 0)
|
|
315
|
-
if (diff !== 0) return diff
|
|
316
|
-
}
|
|
317
|
-
return b.localeCompare(a)
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function nodeRuntimeSupportsWebSocket(nodeExecutable: string): boolean {
|
|
321
|
-
try {
|
|
322
|
-
execFileSync(nodeExecutable, ['-e', "process.exit(typeof WebSocket === 'function' ? 0 : 42)"], {
|
|
323
|
-
stdio: 'ignore',
|
|
324
|
-
timeout: 3000,
|
|
325
|
-
})
|
|
326
|
-
return true
|
|
327
|
-
} catch {
|
|
328
|
-
return false
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function resolveAdhdevMcpEntryPath(explicitPath?: string): string | null {
|
|
333
|
-
const explicit = explicitPath?.trim()
|
|
334
|
-
if (explicit) return normalizeExistingPath(explicit) || explicit
|
|
335
|
-
|
|
336
|
-
const envPath = process.env.ADHDEV_MCP_SERVER_PATH?.trim()
|
|
337
|
-
if (envPath) return normalizeExistingPath(envPath) || envPath
|
|
338
|
-
|
|
339
|
-
const candidates: string[] = []
|
|
340
|
-
const addCandidate = (candidate: string) => {
|
|
341
|
-
if (!candidates.includes(candidate)) candidates.push(candidate)
|
|
342
|
-
}
|
|
343
|
-
const addPackagedCandidates = (baseFile?: string) => {
|
|
344
|
-
if (!baseFile) return
|
|
345
|
-
const realBase = normalizeExistingPath(baseFile) || baseFile
|
|
346
|
-
const dir = dirname(realBase)
|
|
347
|
-
addCandidate(resolve(dir, '../vendor/mcp-server/index.js'))
|
|
348
|
-
addCandidate(resolve(dir, '../../vendor/mcp-server/index.js'))
|
|
349
|
-
addCandidate(resolve(dir, '../../../vendor/mcp-server/index.js'))
|
|
350
|
-
// Source checkout/dev mode does not vendor the MCP server into daemon-standalone.
|
|
351
|
-
// Resolve the sibling workspace build directly so Repo Mesh auto-import still
|
|
352
|
-
// writes an absolute Node entrypoint instead of falling back to a PATH bin shim.
|
|
353
|
-
addCandidate(resolve(dir, '../../mcp-server/dist/index.js'))
|
|
354
|
-
addCandidate(resolve(dir, '../../../mcp-server/dist/index.js'))
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
addPackagedCandidates(process.argv[1])
|
|
358
|
-
|
|
359
|
-
for (const candidate of candidates) {
|
|
360
|
-
const normalized = normalizeExistingPath(candidate)
|
|
361
|
-
if (normalized) return normalized
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
try {
|
|
365
|
-
const requireBase = process.argv[1] ? (normalizeExistingPath(process.argv[1]) || process.argv[1]) : join(process.cwd(), 'adhdev-daemon.js')
|
|
366
|
-
const req = createRequire(requireBase)
|
|
367
|
-
const resolvedModule = req.resolve('@adhdev/mcp-server')
|
|
368
|
-
return normalizeExistingPath(resolvedModule) || resolvedModule
|
|
369
|
-
} catch {
|
|
370
|
-
return null
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function normalizeExistingPath(filePath: string): string | null {
|
|
375
|
-
try {
|
|
376
|
-
if (!existsSync(filePath)) return null
|
|
377
|
-
return realpathSync.native(filePath)
|
|
378
|
-
} catch {
|
|
379
|
-
return null
|
|
380
|
-
}
|
|
381
|
-
}
|