@adhdev/daemon-core 0.8.29 → 0.8.31
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/agent-stream/manager.d.ts +1 -0
- package/dist/agent-stream/provider-adapter.d.ts +1 -0
- package/dist/agent-stream/types.d.ts +3 -0
- package/dist/boot/daemon-lifecycle.d.ts +2 -1
- package/dist/cdp/manager.d.ts +2 -0
- package/dist/cli-adapter-types.d.ts +34 -5
- package/dist/cli-adapters/provider-cli-adapter.d.ts +4 -158
- package/dist/cli-adapters/provider-cli-config.d.ts +30 -0
- package/dist/cli-adapters/provider-cli-parse.d.ts +42 -0
- package/dist/cli-adapters/provider-cli-runtime.d.ts +29 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +158 -0
- package/dist/commands/handler.d.ts +4 -3
- package/dist/config/config.d.ts +4 -3
- package/dist/index.js +1033 -621
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1035 -624
- package/dist/index.mjs.map +1 -1
- package/dist/providers/acp-provider-instance.d.ts +1 -0
- package/dist/providers/approval-utils.d.ts +7 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -0
- package/dist/providers/contracts.d.ts +12 -1
- package/dist/providers/ide-provider-instance.d.ts +1 -0
- package/dist/providers/provider-loader.d.ts +3 -0
- package/dist/status/reporter.d.ts +2 -3
- package/dist/status/snapshot.d.ts +2 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +3 -1
- package/src/agent-stream/manager.ts +8 -2
- package/src/agent-stream/poller.ts +57 -6
- package/src/agent-stream/provider-adapter.ts +11 -7
- package/src/agent-stream/types.ts +3 -0
- package/src/boot/daemon-lifecycle.ts +7 -6
- package/src/cdp/initializer.ts +2 -2
- package/src/cdp/manager.ts +5 -0
- package/src/cdp/setup.ts +1 -1
- package/src/cli-adapter-types.ts +37 -5
- package/src/cli-adapters/provider-cli-adapter.ts +212 -795
- package/src/cli-adapters/provider-cli-config.ts +66 -0
- package/src/cli-adapters/provider-cli-parse.ts +202 -0
- package/src/cli-adapters/provider-cli-runtime.ts +142 -0
- package/src/cli-adapters/provider-cli-shared.ts +439 -0
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +1 -1
- package/src/commands/cdp-commands.ts +6 -1
- package/src/commands/chat-commands.ts +45 -29
- package/src/commands/cli-manager.ts +28 -9
- package/src/commands/handler.ts +14 -10
- package/src/commands/router.ts +23 -10
- package/src/commands/stream-commands.ts +11 -5
- package/src/config/config.ts +4 -10
- package/src/daemon/dev-auto-implement.ts +22 -18
- package/src/daemon/dev-cli-debug.ts +59 -16
- package/src/daemon/dev-server.ts +67 -43
- package/src/providers/acp-provider-instance.ts +18 -3
- package/src/providers/approval-utils.ts +66 -0
- package/src/providers/cli-provider-instance.ts +32 -6
- package/src/providers/contracts.d.ts +1 -0
- package/src/providers/contracts.ts +15 -2
- package/src/providers/extension-provider-instance.ts +1 -1
- package/src/providers/ide-provider-instance.ts +67 -41
- package/src/providers/provider-loader.ts +110 -55
- package/src/providers/version-archive.ts +23 -5
- package/src/status/reporter.ts +18 -14
- package/src/status/snapshot.ts +5 -4
|
@@ -10,6 +10,7 @@ import * as path from 'path';
|
|
|
10
10
|
import * as crypto from 'crypto';
|
|
11
11
|
import chalk from 'chalk';
|
|
12
12
|
import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
|
|
13
|
+
import type { CliProviderModule } from '../cli-adapters/provider-cli-adapter.js';
|
|
13
14
|
import { detectCLI } from '../detection/cli-detector.js';
|
|
14
15
|
import { loadConfig } from '../config/config.js';
|
|
15
16
|
import { loadState, saveState } from '../config/state-store.js';
|
|
@@ -24,6 +25,7 @@ import type { ProviderModule, ProviderResumeCapability } from '../providers/cont
|
|
|
24
25
|
import type { CliAdapter } from '../cli-adapter-types.js';
|
|
25
26
|
import type { PtyTransportFactory } from '../cli-adapters/pty-transport.js';
|
|
26
27
|
import type { SessionRegistry } from '../sessions/registry.js';
|
|
28
|
+
import type { ProviderInstance } from '../providers/provider-instance.js';
|
|
27
29
|
import { LOG } from '../logging/logger.js';
|
|
28
30
|
|
|
29
31
|
// ─── external dependency interface ──────────────────────────
|
|
@@ -67,9 +69,17 @@ export interface HostedCliRuntimeDescriptor {
|
|
|
67
69
|
providerSessionId?: string;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
?
|
|
72
|
-
|
|
72
|
+
type CliPresentationInstance = ProviderInstance & {
|
|
73
|
+
getPresentationMode?(): 'terminal' | 'chat';
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
type ChalkColorFn = (text: string) => string;
|
|
77
|
+
type ChalkLike = Partial<Record<'red' | 'green' | 'yellow' | 'cyan', ChalkColorFn>>;
|
|
78
|
+
|
|
79
|
+
const chalkModule = chalk as unknown as ChalkLike & { default?: ChalkLike };
|
|
80
|
+
const chalkApi: ChalkLike | null = typeof chalkModule.yellow === 'function'
|
|
81
|
+
? chalkModule
|
|
82
|
+
: chalkModule.default || null;
|
|
73
83
|
|
|
74
84
|
function colorize(color: 'red' | 'green' | 'yellow' | 'cyan', text: string): string {
|
|
75
85
|
const fn = chalkApi?.[color];
|
|
@@ -84,6 +94,10 @@ type CliSessionBinding = {
|
|
|
84
94
|
launchMode: CliLaunchMode;
|
|
85
95
|
};
|
|
86
96
|
|
|
97
|
+
type CliAdapterWithExtraArgs = CliAdapter & {
|
|
98
|
+
extraArgs?: string[];
|
|
99
|
+
};
|
|
100
|
+
|
|
87
101
|
function isUuid(value: string): boolean {
|
|
88
102
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
89
103
|
}
|
|
@@ -239,7 +253,7 @@ export class DaemonCliManager {
|
|
|
239
253
|
|
|
240
254
|
getSessionPresentationMode(sessionId: string): 'terminal' | 'chat' | null {
|
|
241
255
|
if (!sessionId) return null;
|
|
242
|
-
const instance = this.deps.getInstanceManager()?.getInstance(sessionId) as
|
|
256
|
+
const instance = this.deps.getInstanceManager()?.getInstance(sessionId) as CliPresentationInstance | undefined;
|
|
243
257
|
const mode = instance?.category === 'cli'
|
|
244
258
|
? instance.getPresentationMode?.()
|
|
245
259
|
: null;
|
|
@@ -321,7 +335,7 @@ export class DaemonCliManager {
|
|
|
321
335
|
providerSessionId,
|
|
322
336
|
attachExisting,
|
|
323
337
|
);
|
|
324
|
-
return new ProviderCliAdapter(resolvedProvider as
|
|
338
|
+
return new ProviderCliAdapter(resolvedProvider as CliProviderModule, workingDir, cliArgs, transportFactory);
|
|
325
339
|
}
|
|
326
340
|
|
|
327
341
|
throw new Error(`No CLI provider found for '${cliType}'. Create a provider.js in providers/cli/${cliType}/`);
|
|
@@ -408,7 +422,7 @@ export class DaemonCliManager {
|
|
|
408
422
|
throw new Error(`Failed to start ${provider.displayName || provider.name || cliType}: ${spawnErr?.message}`);
|
|
409
423
|
}
|
|
410
424
|
|
|
411
|
-
this.adapters.set(key, cliInstance.getAdapter()
|
|
425
|
+
this.adapters.set(key, cliInstance.getAdapter());
|
|
412
426
|
this.startCliExitMonitor(key, cliType);
|
|
413
427
|
}
|
|
414
428
|
|
|
@@ -475,6 +489,7 @@ export class DaemonCliManager {
|
|
|
475
489
|
// Register ACP entry in adapter map (getStatus queries from acpInstance in real-time)
|
|
476
490
|
this.adapters.set(key, {
|
|
477
491
|
cliType: normalizedType,
|
|
492
|
+
cliName: provider.name,
|
|
478
493
|
workingDir: resolvedDir,
|
|
479
494
|
_acpInstance: acpInstance,
|
|
480
495
|
spawn: async () => {},
|
|
@@ -488,9 +503,13 @@ export class DaemonCliManager {
|
|
|
488
503
|
activeModal: state.activeChat?.activeModal || null,
|
|
489
504
|
};
|
|
490
505
|
},
|
|
506
|
+
getPartialResponse: () => '',
|
|
507
|
+
cancel: () => { instanceManager.removeInstance(key); },
|
|
508
|
+
isProcessing: () => false,
|
|
509
|
+
isReady: () => true,
|
|
491
510
|
setOnStatusChange: () => {},
|
|
492
511
|
setOnPtyData: () => {},
|
|
493
|
-
}
|
|
512
|
+
});
|
|
494
513
|
|
|
495
514
|
console.log(colorize('green', ` ✓ ACP agent started: ${provider.name} in ${resolvedDir}`));
|
|
496
515
|
|
|
@@ -893,7 +912,7 @@ export class DaemonCliManager {
|
|
|
893
912
|
const dir = rdir.path;
|
|
894
913
|
if (!cliType) throw new Error('cliType required');
|
|
895
914
|
const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
|
|
896
|
-
const prevCliArgs = found ? (found.adapter as
|
|
915
|
+
const prevCliArgs = found ? (found.adapter as CliAdapterWithExtraArgs).extraArgs : undefined;
|
|
897
916
|
if (found) await this.stopSession(found.key);
|
|
898
917
|
await this.startSession(cliType, dir, args?.cliArgs || prevCliArgs, args?.initialModel);
|
|
899
918
|
return { success: true, restarted: true };
|
|
@@ -916,7 +935,7 @@ export class DaemonCliManager {
|
|
|
916
935
|
await adapter.sendMessage(message);
|
|
917
936
|
return { success: true, status: 'generating' };
|
|
918
937
|
} else if (action === 'clear_history') {
|
|
919
|
-
if (typeof
|
|
938
|
+
if (typeof adapter.clearHistory === 'function') adapter.clearHistory();
|
|
920
939
|
return { success: true, cleared: true };
|
|
921
940
|
} else if (action === 'stop') {
|
|
922
941
|
await this.stopSession(key);
|
package/src/commands/handler.ts
CHANGED
|
@@ -16,8 +16,9 @@ import { CdpDomHandlers } from '../cdp/devtools.js';
|
|
|
16
16
|
import { findCdpManager } from '../status/builders.js';
|
|
17
17
|
import { ProviderLoader } from '../providers/provider-loader.js';
|
|
18
18
|
import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
|
|
19
|
-
import type { ProviderModule } from '../providers/contracts.js';
|
|
19
|
+
import type { ProviderModule, ProviderScripts } from '../providers/contracts.js';
|
|
20
20
|
import type { DaemonAgentStreamManager } from '../agent-stream/index.js';
|
|
21
|
+
import type { CliAdapter } from '../cli-adapter-types.js';
|
|
21
22
|
import { loadConfig } from '../config/config.js';
|
|
22
23
|
import { ChatHistoryWriter } from '../config/chat-history.js';
|
|
23
24
|
import type { SessionRegistry, SessionRuntimeTarget } from '../sessions/registry.js';
|
|
@@ -39,7 +40,7 @@ export interface CommandResult {
|
|
|
39
40
|
export interface CommandContext {
|
|
40
41
|
cdpManagers: Map<string, DaemonCdpManager>;
|
|
41
42
|
ideType: string;
|
|
42
|
-
adapters: Map<string,
|
|
43
|
+
adapters: Map<string, CliAdapter>;
|
|
43
44
|
providerLoader?: ProviderLoader;
|
|
44
45
|
/** ProviderInstanceManager — for runtime settings propagation */
|
|
45
46
|
instanceManager?: ProviderInstanceManager;
|
|
@@ -56,7 +57,7 @@ export interface CommandHelpers {
|
|
|
56
57
|
getProvider(overrideType?: string): ProviderModule | undefined;
|
|
57
58
|
getProviderScript(scriptName: string, params?: Record<string, string>, ideType?: string): string | null;
|
|
58
59
|
evaluateProviderScript(scriptName: string, params?: Record<string, string>, timeout?: number): Promise<{ result: any; category: string } | null>;
|
|
59
|
-
getCliAdapter(type?: string):
|
|
60
|
+
getCliAdapter(type?: string): CliAdapter | null;
|
|
60
61
|
readonly currentManagerKey: string | undefined;
|
|
61
62
|
readonly currentIdeType: string | undefined;
|
|
62
63
|
readonly currentProviderType: string | undefined;
|
|
@@ -66,6 +67,8 @@ export interface CommandHelpers {
|
|
|
66
67
|
readonly historyWriter: ChatHistoryWriter;
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
type LegacyStringScript = (params?: Record<string, unknown> | string) => string;
|
|
71
|
+
|
|
69
72
|
const COMMAND_DEBUG_LEVELS = new Set([
|
|
70
73
|
'pty_input',
|
|
71
74
|
'pty_resize',
|
|
@@ -214,15 +217,16 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
214
217
|
getProviderScript(scriptName: string, params?: Record<string, string>, ideType?: string): string | null {
|
|
215
218
|
const provider = this.getProvider(ideType);
|
|
216
219
|
if (provider?.scripts) {
|
|
217
|
-
const fn =
|
|
220
|
+
const fn = provider.scripts[scriptName];
|
|
218
221
|
if (typeof fn === 'function') {
|
|
222
|
+
const callScript = fn as LegacyStringScript;
|
|
219
223
|
if (params && Object.keys(params).length > 0) {
|
|
220
224
|
const firstVal = Object.values(params)[0];
|
|
221
225
|
if (scriptName === 'sendMessage' && typeof firstVal === 'string') {
|
|
222
|
-
const legacyScript =
|
|
226
|
+
const legacyScript = callScript(firstVal);
|
|
223
227
|
if (legacyScript) return legacyScript;
|
|
224
228
|
}
|
|
225
|
-
const script =
|
|
229
|
+
const script = callScript(params);
|
|
226
230
|
if (script) {
|
|
227
231
|
const likelyLegacyObjectLeak =
|
|
228
232
|
typeof script === 'string'
|
|
@@ -232,13 +236,13 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
232
236
|
}
|
|
233
237
|
|
|
234
238
|
if (firstVal !== undefined) {
|
|
235
|
-
const legacyScript =
|
|
239
|
+
const legacyScript = callScript(firstVal);
|
|
236
240
|
if (legacyScript) return legacyScript;
|
|
237
241
|
}
|
|
238
242
|
|
|
239
243
|
if (script) return script;
|
|
240
244
|
} else {
|
|
241
|
-
const script =
|
|
245
|
+
const script = callScript();
|
|
242
246
|
if (script) return script;
|
|
243
247
|
}
|
|
244
248
|
}
|
|
@@ -290,7 +294,7 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
290
294
|
}
|
|
291
295
|
|
|
292
296
|
/** CLI adapter search */
|
|
293
|
-
getCliAdapter(type?: string):
|
|
297
|
+
getCliAdapter(type?: string): CliAdapter | null {
|
|
294
298
|
const target = type || this._currentRoute.session?.sessionId || this._currentRoute.providerType || this._currentRoute.managerKey;
|
|
295
299
|
if (!target || !this._ctx.adapters) return null;
|
|
296
300
|
const session = this._ctx.sessionRegistry?.get(target);
|
|
@@ -313,7 +317,7 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
313
317
|
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
314
318
|
let session = targetSessionId ? this._ctx.sessionRegistry?.get(targetSessionId) : undefined;
|
|
315
319
|
if (targetSessionId && !session) {
|
|
316
|
-
reconcileIdeRuntimeSessions(this._ctx.instanceManager
|
|
320
|
+
reconcileIdeRuntimeSessions(this._ctx.instanceManager, this._ctx.sessionRegistry);
|
|
317
321
|
session = this._ctx.sessionRegistry?.get(targetSessionId);
|
|
318
322
|
}
|
|
319
323
|
const sessionLookupFailed = !!targetSessionId && !session;
|
package/src/commands/router.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { detectIDEs } from '../detection/ide-detector.js';
|
|
|
28
28
|
import { SessionRegistry } from '../sessions/registry.js';
|
|
29
29
|
import { LOG } from '../logging/logger.js';
|
|
30
30
|
import { logCommand } from '../logging/command-log.js';
|
|
31
|
+
import type { CommandLogEntry } from '../logging/command-log.js';
|
|
31
32
|
import { getRecentLogs, LOG_PATH } from '../logging/logger.js';
|
|
32
33
|
import { buildSessionEntries } from '../status/builders.js';
|
|
33
34
|
import { getSessionCompletionMarker } from '../status/snapshot.js';
|
|
@@ -86,6 +87,19 @@ const CHAT_COMMANDS = [
|
|
|
86
87
|
];
|
|
87
88
|
const READ_DEBUG_ENABLED = process.argv.includes('--dev') || process.env.ADHDEV_READ_DEBUG === '1';
|
|
88
89
|
|
|
90
|
+
function normalizeCommandSource(source: string): CommandLogEntry['source'] {
|
|
91
|
+
switch (source) {
|
|
92
|
+
case 'ws':
|
|
93
|
+
case 'p2p':
|
|
94
|
+
case 'ext':
|
|
95
|
+
case 'api':
|
|
96
|
+
case 'standalone':
|
|
97
|
+
return source;
|
|
98
|
+
default:
|
|
99
|
+
return 'unknown';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
89
103
|
function toHostedCliRuntimeDescriptor(record: any): HostedCliRuntimeDescriptor | null {
|
|
90
104
|
if (!record || typeof record !== 'object') return null;
|
|
91
105
|
const runtimeId = typeof record.sessionId === 'string' ? record.sessionId : '';
|
|
@@ -130,18 +144,19 @@ export class DaemonCommandRouter {
|
|
|
130
144
|
*/
|
|
131
145
|
async execute(cmd: string, args: any, source: string = 'unknown'): Promise<CommandRouterResult> {
|
|
132
146
|
const cmdStart = Date.now();
|
|
147
|
+
const logSource = normalizeCommandSource(source);
|
|
133
148
|
|
|
134
149
|
try {
|
|
135
150
|
// 1. Try daemon-level command
|
|
136
151
|
const daemonResult = await this.executeDaemonCommand(cmd, args);
|
|
137
152
|
if (daemonResult) {
|
|
138
|
-
logCommand({ ts: new Date().toISOString(), cmd, source:
|
|
153
|
+
logCommand({ ts: new Date().toISOString(), cmd, source: logSource, args, success: daemonResult.success, durationMs: Date.now() - cmdStart });
|
|
139
154
|
return daemonResult;
|
|
140
155
|
}
|
|
141
156
|
|
|
142
157
|
// 2. Delegate to DaemonCommandHandler
|
|
143
158
|
const handlerResult = await this.deps.commandHandler.handle(cmd, args);
|
|
144
|
-
logCommand({ ts: new Date().toISOString(), cmd, source:
|
|
159
|
+
logCommand({ ts: new Date().toISOString(), cmd, source: logSource, args, success: handlerResult.success, durationMs: Date.now() - cmdStart });
|
|
145
160
|
|
|
146
161
|
// 3. Post-chat command callback
|
|
147
162
|
if (CHAT_COMMANDS.includes(cmd) && this.deps.onPostChatCommand) {
|
|
@@ -150,7 +165,7 @@ export class DaemonCommandRouter {
|
|
|
150
165
|
|
|
151
166
|
return handlerResult;
|
|
152
167
|
} catch (e: any) {
|
|
153
|
-
logCommand({ ts: new Date().toISOString(), cmd, source:
|
|
168
|
+
logCommand({ ts: new Date().toISOString(), cmd, source: logSource, args, success: false, error: e.message, durationMs: Date.now() - cmdStart });
|
|
154
169
|
throw e;
|
|
155
170
|
}
|
|
156
171
|
}
|
|
@@ -417,7 +432,7 @@ export class DaemonCommandRouter {
|
|
|
417
432
|
? this.deps.getCdpLogFn(result.ideId)
|
|
418
433
|
: LOG.forComponent(`CDP:${result.ideId}`).asLogFn();
|
|
419
434
|
const provider = this.deps.providerLoader.getMeta(result.ideId);
|
|
420
|
-
const manager = new DaemonCdpManager(result.port, logFn, undefined,
|
|
435
|
+
const manager = new DaemonCdpManager(result.port, logFn, undefined, provider?.targetFilter);
|
|
421
436
|
const connected = await manager.connect();
|
|
422
437
|
if (connected) {
|
|
423
438
|
// Register active extension providers for this IDE in CDP manager
|
|
@@ -457,7 +472,7 @@ export class DaemonCommandRouter {
|
|
|
457
472
|
}));
|
|
458
473
|
} catch { /* ignore activity persist errors */ }
|
|
459
474
|
}
|
|
460
|
-
return {
|
|
475
|
+
return { ...result };
|
|
461
476
|
}
|
|
462
477
|
|
|
463
478
|
// ─── Detect IDEs ───
|
|
@@ -485,7 +500,7 @@ export class DaemonCommandRouter {
|
|
|
485
500
|
const prevSeenAt = currentState.sessionReads?.[sessionId] || 0;
|
|
486
501
|
const sessionEntries = buildSessionEntries(
|
|
487
502
|
this.deps.instanceManager.collectAllStates(),
|
|
488
|
-
this.deps.cdpManagers
|
|
503
|
+
this.deps.cdpManagers,
|
|
489
504
|
);
|
|
490
505
|
const targetSession = sessionEntries.find((entry) => entry.id === sessionId);
|
|
491
506
|
const completionMarker = targetSession ? getSessionCompletionMarker(targetSession) : '';
|
|
@@ -606,8 +621,7 @@ export class DaemonCommandRouter {
|
|
|
606
621
|
}
|
|
607
622
|
}
|
|
608
623
|
for (const instanceKey of keysToRemove) {
|
|
609
|
-
|
|
610
|
-
if (ideInstance) {
|
|
624
|
+
if (this.deps.instanceManager.getInstance(instanceKey)) {
|
|
611
625
|
this.deps.instanceManager.removeInstance(instanceKey);
|
|
612
626
|
LOG.info('StopIDE', `Instance removed: ${instanceKey}`);
|
|
613
627
|
}
|
|
@@ -615,8 +629,7 @@ export class DaemonCommandRouter {
|
|
|
615
629
|
// Fallback: single instance key
|
|
616
630
|
if (keysToRemove.length === 0) {
|
|
617
631
|
const instanceKey = `ide:${ideType}`;
|
|
618
|
-
|
|
619
|
-
if (ideInstance) {
|
|
632
|
+
if (this.deps.instanceManager.getInstance(instanceKey)) {
|
|
620
633
|
this.deps.instanceManager.removeInstance(instanceKey);
|
|
621
634
|
LOG.info('StopIDE', `Instance removed: ${instanceKey}`);
|
|
622
635
|
}
|
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
import type { CommandResult, CommandHelpers } from './handler.js';
|
|
7
7
|
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
8
|
+
import type { ProviderInstance } from '../providers/provider-instance.js';
|
|
8
9
|
import { LOG } from '../logging/logger.js';
|
|
9
10
|
|
|
11
|
+
interface CliPresentationInstance extends ProviderInstance {
|
|
12
|
+
getPresentationMode?(): 'terminal' | 'chat';
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
function getCliPresentationMode(h: CommandHelpers, targetSessionId?: string): 'terminal' | 'chat' | null {
|
|
11
16
|
if (!targetSessionId) return null;
|
|
12
|
-
const instance = h.ctx.instanceManager?.getInstance(targetSessionId) as
|
|
17
|
+
const instance = h.ctx.instanceManager?.getInstance(targetSessionId) as CliPresentationInstance | undefined;
|
|
13
18
|
if (instance?.category !== 'cli') return null;
|
|
14
19
|
const mode = instance.getPresentationMode?.();
|
|
15
20
|
return mode === 'chat' || mode === 'terminal' ? mode : null;
|
|
@@ -49,11 +54,12 @@ export function handlePtyResize(h: CommandHelpers, args: any): CommandResult {
|
|
|
49
54
|
if (!adapter || typeof adapter.resize !== 'function') {
|
|
50
55
|
return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || 'unknown'}` };
|
|
51
56
|
}
|
|
57
|
+
const resize = adapter.resize;
|
|
52
58
|
if (force) {
|
|
53
|
-
|
|
54
|
-
setTimeout(() =>
|
|
59
|
+
resize(cols - 1, rows);
|
|
60
|
+
setTimeout(() => resize(cols, rows), 50);
|
|
55
61
|
} else {
|
|
56
|
-
|
|
62
|
+
resize(cols, rows);
|
|
57
63
|
}
|
|
58
64
|
return { success: true };
|
|
59
65
|
}
|
|
@@ -119,7 +125,7 @@ function parseScriptResult(result: unknown): { success: boolean; payload: any }
|
|
|
119
125
|
return { success: true, payload: { result } };
|
|
120
126
|
}
|
|
121
127
|
}
|
|
122
|
-
if (result && typeof result === 'object' &&
|
|
128
|
+
if (result && typeof result === 'object' && 'success' in result && result.success === false) {
|
|
123
129
|
return { success: false, payload: result };
|
|
124
130
|
}
|
|
125
131
|
return { success: true, payload: result };
|
package/src/config/config.ts
CHANGED
|
@@ -57,10 +57,11 @@ export interface ADHDevConfig {
|
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* Server-side D1 `machines.id` — the row ID assigned when daemon registers via
|
|
60
|
-
* `POST /cli/complete`.
|
|
60
|
+
* `POST /cli/complete`. This remains useful for account-side machine actions
|
|
61
|
+
* that target the registered machine row directly (for example cloud rename).
|
|
61
62
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
63
|
+
* Machine auth itself uses `machineSecret` (adm_) and no longer falls back
|
|
64
|
+
* to `registeredMachineId`.
|
|
64
65
|
*/
|
|
65
66
|
registeredMachineId?: string;
|
|
66
67
|
|
|
@@ -174,17 +175,10 @@ function ensureMachineId(config: ADHDevConfig): { config: ADHDevConfig; changed:
|
|
|
174
175
|
return { config, changed: false };
|
|
175
176
|
}
|
|
176
177
|
|
|
177
|
-
// TODO(2026-05-01): Remove this legacy bridge after cloud clients have had
|
|
178
|
-
// time to persist registeredMachineId from the upgraded setup/login flow.
|
|
179
|
-
const legacyRegisteredMachineId = (!config.registeredMachineId && config.machineSecret && config.machineId)
|
|
180
|
-
? config.machineId
|
|
181
|
-
: config.registeredMachineId;
|
|
182
|
-
|
|
183
178
|
return {
|
|
184
179
|
config: {
|
|
185
180
|
...config,
|
|
186
181
|
machineId: generateMachineId(),
|
|
187
|
-
registeredMachineId: legacyRegisteredMachineId,
|
|
188
182
|
},
|
|
189
183
|
changed: true,
|
|
190
184
|
};
|
|
@@ -10,6 +10,7 @@ import * as fs from 'fs';
|
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import * as os from 'os';
|
|
12
12
|
import type * as http from 'http';
|
|
13
|
+
import type { ChildProcess } from 'child_process';
|
|
13
14
|
import type { DevServerContext, ProviderCategory } from './dev-server-types.js';
|
|
14
15
|
import { DEV_SERVER_PORT } from './dev-server.js';
|
|
15
16
|
import { LOG } from '../logging/logger.js';
|
|
@@ -33,8 +34,8 @@ type CliExerciseVerification = {
|
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
function getAutoImplPid(ctx: DevServerContext): number | null {
|
|
36
|
-
const
|
|
37
|
-
return
|
|
37
|
+
const pid = ctx.autoImplProcess?.pid;
|
|
38
|
+
return typeof pid === 'number' && pid > 0 ? pid : null;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
function isPidAlive(pid: number): boolean {
|
|
@@ -57,6 +58,15 @@ function clearStaleAutoImplState(ctx: DevServerContext, reason: string): void {
|
|
|
57
58
|
ctx.autoImplStatus.running = false;
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
function tryKillAutoImplProcess(processRef: ChildProcess | null, signal: NodeJS.Signals): void {
|
|
62
|
+
if (!processRef) return;
|
|
63
|
+
try {
|
|
64
|
+
processRef.kill(signal);
|
|
65
|
+
} catch {
|
|
66
|
+
// ignore
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
export function getDefaultAutoImplReference(ctx: DevServerContext, category: string, type: string): string {
|
|
61
71
|
if (category === 'cli') {
|
|
62
72
|
return type === 'codex-cli' ? 'claude-cli' : 'codex-cli';
|
|
@@ -283,14 +293,14 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
283
293
|
|
|
284
294
|
// 5. Determine agent command from provider spawn config
|
|
285
295
|
const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
|
|
286
|
-
const spawn =
|
|
296
|
+
const spawn = agentProvider?.spawn;
|
|
287
297
|
if (!spawn?.command) {
|
|
288
298
|
try { fs.unlinkSync(promptFile); } catch { /* ignore */ }
|
|
289
299
|
ctx.json(res, 400, { error: `Agent '${agent}' has no spawn config. Select a CLI provider with a spawn configuration.` });
|
|
290
300
|
return;
|
|
291
301
|
}
|
|
292
302
|
|
|
293
|
-
const agentCategory =
|
|
303
|
+
const agentCategory = agentProvider?.category;
|
|
294
304
|
|
|
295
305
|
// ─── ACP Agent: use ACP SDK (JSON-RPC protocol) ───
|
|
296
306
|
if (agentCategory === 'acp') {
|
|
@@ -521,10 +531,12 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
521
531
|
let autoStopIssued = false;
|
|
522
532
|
|
|
523
533
|
try {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
534
|
+
if (agentProvider?.category === 'cli') {
|
|
535
|
+
const { normalizeCliProviderForRuntime } = await import('../cli-adapters/provider-cli-adapter.js');
|
|
536
|
+
const normalized = normalizeCliProviderForRuntime(agentProvider);
|
|
537
|
+
approvalPatterns = normalized.patterns.approval;
|
|
538
|
+
approvalKeys = agentProvider.approvalKeys || { 0: 'y\r', 1: 'a\r' };
|
|
539
|
+
}
|
|
528
540
|
} catch (err: any) {
|
|
529
541
|
ctx.log(`Failed to load approval patterns: ${err.message}`);
|
|
530
542
|
}
|
|
@@ -547,11 +559,7 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
547
559
|
sendAutoImplSSE(ctx, { event: 'output', data: { chunk: `\n[🤖 ADHDev Pipeline] Completion token detected. Proceeding...\n`, stream: 'stdout' } });
|
|
548
560
|
approvalBuffer = '';
|
|
549
561
|
|
|
550
|
-
|
|
551
|
-
(ctx.autoImplProcess as any).kill('SIGINT');
|
|
552
|
-
} catch {
|
|
553
|
-
// ignore
|
|
554
|
-
}
|
|
562
|
+
tryKillAutoImplProcess(ctx.autoImplProcess, 'SIGINT');
|
|
555
563
|
return;
|
|
556
564
|
}
|
|
557
565
|
|
|
@@ -592,11 +600,7 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
592
600
|
stream: 'stdout',
|
|
593
601
|
},
|
|
594
602
|
});
|
|
595
|
-
|
|
596
|
-
(ctx.autoImplProcess as any).kill('SIGINT');
|
|
597
|
-
} catch {
|
|
598
|
-
// ignore
|
|
599
|
-
}
|
|
603
|
+
tryKillAutoImplProcess(ctx.autoImplProcess, 'SIGINT');
|
|
600
604
|
}, 30000);
|
|
601
605
|
};
|
|
602
606
|
|
|
@@ -9,6 +9,8 @@ import * as fs from 'fs';
|
|
|
9
9
|
import * as path from 'path';
|
|
10
10
|
import type * as http from 'http';
|
|
11
11
|
import type { DevServerContext } from './dev-server-types.js';
|
|
12
|
+
import type { CliAdapter } from '../cli-adapter-types.js';
|
|
13
|
+
import type { AcpProviderState, CliProviderState, ProviderInstance, ProviderState } from '../providers/provider-instance.js';
|
|
12
14
|
|
|
13
15
|
// ─── Helpers ──────────────────────────────────────
|
|
14
16
|
|
|
@@ -56,6 +58,36 @@ type CliExerciseFixture = {
|
|
|
56
58
|
notes?: string;
|
|
57
59
|
};
|
|
58
60
|
|
|
61
|
+
type CliTargetState = CliProviderState | AcpProviderState;
|
|
62
|
+
type CliDebugState = {
|
|
63
|
+
status?: string;
|
|
64
|
+
activeModal?: { message?: string; buttons?: string[] } | null;
|
|
65
|
+
startupParseGate?: boolean;
|
|
66
|
+
ready?: boolean;
|
|
67
|
+
currentTurnScope?: unknown;
|
|
68
|
+
providerResolution?: Record<string, any> | null;
|
|
69
|
+
messages?: Array<{ role?: string; content?: string }>;
|
|
70
|
+
partialResponse?: string;
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
};
|
|
73
|
+
type CliTraceState = {
|
|
74
|
+
entryCount?: number;
|
|
75
|
+
activeModal?: { message?: string; buttons?: string[] } | null;
|
|
76
|
+
entries?: Array<{ type?: string; [key: string]: unknown }>;
|
|
77
|
+
messages?: Array<{ role?: string; content?: string }>;
|
|
78
|
+
responseBuffer?: string;
|
|
79
|
+
[key: string]: unknown;
|
|
80
|
+
};
|
|
81
|
+
type CliDebugAdapter = CliAdapter & {
|
|
82
|
+
getDebugState?: () => CliDebugState | null;
|
|
83
|
+
getTraceState?: (limit?: number) => CliTraceState | null;
|
|
84
|
+
getProviderResolutionMeta?: () => Record<string, any> | null;
|
|
85
|
+
};
|
|
86
|
+
type CliAdapterBackedInstance = ProviderInstance & {
|
|
87
|
+
getAdapter?: () => CliDebugAdapter;
|
|
88
|
+
adapter?: CliDebugAdapter;
|
|
89
|
+
};
|
|
90
|
+
|
|
59
91
|
function slugifyFixtureName(value: string): string {
|
|
60
92
|
const normalized = String(value || '')
|
|
61
93
|
.trim()
|
|
@@ -223,7 +255,18 @@ export function validateCliFixtureResult(result: any, assertions: CliFixtureAsse
|
|
|
223
255
|
return failures;
|
|
224
256
|
}
|
|
225
257
|
|
|
226
|
-
function
|
|
258
|
+
function isCliTargetState(state: ProviderState): state is CliTargetState {
|
|
259
|
+
return state.category === 'cli' || state.category === 'acp';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function getCliAdapterFromInstance(instance: ProviderInstance | undefined): CliDebugAdapter | null {
|
|
263
|
+
if (!instance) return null;
|
|
264
|
+
const candidate = instance as CliAdapterBackedInstance;
|
|
265
|
+
if (typeof candidate.getAdapter === 'function') return candidate.getAdapter();
|
|
266
|
+
return candidate.adapter || null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function getCliProviderResolutionMeta(ctx: DevServerContext, type: string, adapter?: CliDebugAdapter | null): Record<string, any> | null {
|
|
227
270
|
const adapterMeta = typeof adapter?.getProviderResolutionMeta === 'function'
|
|
228
271
|
? adapter.getProviderResolutionMeta()
|
|
229
272
|
: (adapter?.getDebugState?.()?.providerResolution || null);
|
|
@@ -241,11 +284,11 @@ function getCliProviderResolutionMeta(ctx: DevServerContext, type: string, adapt
|
|
|
241
284
|
};
|
|
242
285
|
}
|
|
243
286
|
|
|
244
|
-
function findCliTarget(ctx: DevServerContext, type?: string, instanceId?: string):
|
|
287
|
+
function findCliTarget(ctx: DevServerContext, type?: string, instanceId?: string): CliTargetState | null {
|
|
245
288
|
if (!ctx.instanceManager) return null;
|
|
246
289
|
const cliStates = ctx.instanceManager
|
|
247
290
|
.collectAllStates()
|
|
248
|
-
.filter(
|
|
291
|
+
.filter(isCliTargetState);
|
|
249
292
|
if (instanceId) return cliStates.find(s => s.instanceId === instanceId) || null;
|
|
250
293
|
if (!type) return cliStates[cliStates.length - 1] || null;
|
|
251
294
|
const matches = cliStates.filter(s => s.type === type);
|
|
@@ -253,16 +296,16 @@ function findCliTarget(ctx: DevServerContext, type?: string, instanceId?: string
|
|
|
253
296
|
}
|
|
254
297
|
|
|
255
298
|
function getCliTargetBundle(ctx: DevServerContext, type?: string, instanceId?: string): {
|
|
256
|
-
target:
|
|
257
|
-
instance:
|
|
258
|
-
adapter:
|
|
299
|
+
target: CliTargetState;
|
|
300
|
+
instance: ProviderInstance;
|
|
301
|
+
adapter: CliDebugAdapter;
|
|
259
302
|
} | null {
|
|
260
303
|
if (!ctx.instanceManager) return null;
|
|
261
304
|
const target = findCliTarget(ctx, type, instanceId);
|
|
262
305
|
if (!target) return null;
|
|
263
|
-
const instance = ctx.instanceManager.getInstance(target.instanceId)
|
|
306
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId);
|
|
264
307
|
if (!instance) return null;
|
|
265
|
-
const adapter = instance
|
|
308
|
+
const adapter = getCliAdapterFromInstance(instance);
|
|
266
309
|
if (!adapter) return null;
|
|
267
310
|
return { target, instance, adapter };
|
|
268
311
|
}
|
|
@@ -838,14 +881,14 @@ export async function handleCliDebug(ctx: DevServerContext, type: string, _req:
|
|
|
838
881
|
}
|
|
839
882
|
|
|
840
883
|
// Get the ProviderInstance and access adapter debug state
|
|
841
|
-
const instance = ctx.instanceManager.getInstance(target.instanceId)
|
|
884
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId);
|
|
842
885
|
if (!instance) {
|
|
843
886
|
ctx.json(res, 404, { error: `Instance not found: ${target.instanceId}` });
|
|
844
887
|
return;
|
|
845
888
|
}
|
|
846
889
|
|
|
847
890
|
try {
|
|
848
|
-
const adapter = instance
|
|
891
|
+
const adapter = getCliAdapterFromInstance(instance);
|
|
849
892
|
if (adapter && typeof adapter.getDebugState === 'function') {
|
|
850
893
|
const debugState = adapter.getDebugState();
|
|
851
894
|
ctx.json(res, 200, {
|
|
@@ -891,14 +934,14 @@ export async function handleCliTrace(ctx: DevServerContext, type: string, req: h
|
|
|
891
934
|
return;
|
|
892
935
|
}
|
|
893
936
|
|
|
894
|
-
const instance = ctx.instanceManager.getInstance(target.instanceId)
|
|
937
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId);
|
|
895
938
|
if (!instance) {
|
|
896
939
|
ctx.json(res, 404, { error: `Instance not found: ${target.instanceId}` });
|
|
897
940
|
return;
|
|
898
941
|
}
|
|
899
942
|
|
|
900
943
|
try {
|
|
901
|
-
const adapter = instance
|
|
944
|
+
const adapter = getCliAdapterFromInstance(instance);
|
|
902
945
|
const url = new URL(req.url || '/', 'http://127.0.0.1');
|
|
903
946
|
const limit = parseInt(url.searchParams.get('limit') || '120', 10);
|
|
904
947
|
if (adapter && typeof adapter.getTraceState === 'function') {
|
|
@@ -1101,8 +1144,8 @@ export async function handleCliResolve(ctx: DevServerContext, req: http.Incoming
|
|
|
1101
1144
|
return;
|
|
1102
1145
|
}
|
|
1103
1146
|
|
|
1104
|
-
const instance = ctx.instanceManager.getInstance(target.instanceId)
|
|
1105
|
-
const adapter = instance
|
|
1147
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId);
|
|
1148
|
+
const adapter = getCliAdapterFromInstance(instance);
|
|
1106
1149
|
if (!adapter) {
|
|
1107
1150
|
ctx.json(res, 404, { error: `Adapter not found for instance: ${target.instanceId}` });
|
|
1108
1151
|
return;
|
|
@@ -1144,8 +1187,8 @@ export async function handleCliRaw(ctx: DevServerContext, req: http.IncomingMess
|
|
|
1144
1187
|
return;
|
|
1145
1188
|
}
|
|
1146
1189
|
|
|
1147
|
-
const instance = ctx.instanceManager.getInstance(target.instanceId)
|
|
1148
|
-
const adapter = instance
|
|
1190
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId);
|
|
1191
|
+
const adapter = getCliAdapterFromInstance(instance);
|
|
1149
1192
|
if (!adapter) {
|
|
1150
1193
|
ctx.json(res, 404, { error: `Adapter not found for instance: ${target.instanceId}` });
|
|
1151
1194
|
return;
|