@adhdev/daemon-core 0.7.6 → 0.7.7
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/index.d.mts +158 -38
- package/dist/index.d.ts +158 -38
- package/dist/index.js +3965 -2521
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3455 -2011
- package/dist/index.mjs.map +1 -1
- package/dist/{normalize-tKg8IiDk.d.mts → normalize-auJAPmKy.d.mts} +669 -629
- package/dist/{normalize-tKg8IiDk.d.ts → normalize-auJAPmKy.d.ts} +669 -629
- package/dist/status/normalize.d.mts +1 -1
- package/dist/status/normalize.d.ts +1 -1
- package/package.json +5 -1
- package/src/boot/daemon-lifecycle.ts +7 -4
- package/src/cli-adapter-types.ts +2 -0
- package/src/cli-adapters/provider-cli-adapter.ts +148 -11
- package/src/cli-adapters/pty-transport.ts +100 -0
- package/src/cli-adapters/session-host-transport.ts +392 -0
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +126 -0
- package/src/cli-adapters/terminal-backends/types.ts +17 -0
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +87 -0
- package/src/cli-adapters/terminal-screen.ts +40 -53
- package/src/commands/cli-manager.ts +184 -55
- package/src/config/config.d.ts +116 -0
- package/src/config/workspace-activity.d.ts +22 -0
- package/src/config/workspaces.d.ts +84 -0
- package/src/daemon/dev-auto-implement.ts +1087 -0
- package/src/daemon/dev-cdp-handlers.ts +1003 -0
- package/src/daemon/dev-cli-debug.ts +288 -0
- package/src/daemon/dev-server-types.ts +45 -0
- package/src/daemon/dev-server.ts +121 -1698
- package/src/index.ts +5 -1
- package/src/providers/cli-provider-instance.ts +13 -1
- package/src/providers/contracts.d.ts +408 -0
- package/src/providers/contracts.ts +9 -0
- package/src/providers/provider-instance-manager.ts +21 -0
- package/src/providers/provider-instance.d.ts +142 -0
- package/src/providers/provider-instance.ts +23 -1
- package/src/shared-types.d.ts +157 -0
- package/src/shared-types.ts +14 -0
- package/src/status/builders.ts +6 -0
- package/src/status/normalize.d.ts +14 -0
- package/src/types.d.ts +127 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevServer — CLI Debug Handlers
|
|
3
|
+
*
|
|
4
|
+
* Extracted from dev-server.ts for maintainability.
|
|
5
|
+
* All functions take a DevServerContext as their first argument.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type * as http from 'http';
|
|
9
|
+
import type { DevServerContext } from './dev-server-types.js';
|
|
10
|
+
|
|
11
|
+
// ─── Helpers ──────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
function findCliTarget(ctx: DevServerContext, type?: string, instanceId?: string): any | null {
|
|
14
|
+
if (!ctx.instanceManager) return null;
|
|
15
|
+
const cliStates = ctx.instanceManager
|
|
16
|
+
.collectAllStates()
|
|
17
|
+
.filter(s => s.category === 'cli' || s.category === 'acp');
|
|
18
|
+
if (instanceId) return cliStates.find(s => s.instanceId === instanceId) || null;
|
|
19
|
+
if (!type) return cliStates[cliStates.length - 1] || null;
|
|
20
|
+
const matches = cliStates.filter(s => s.type === type);
|
|
21
|
+
return matches[matches.length - 1] || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ─── Handlers ─────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/** GET /api/cli/status — list all running CLI/ACP instances with state */
|
|
27
|
+
export async function handleCliStatus(ctx: DevServerContext, _req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
28
|
+
if (!ctx.instanceManager) {
|
|
29
|
+
ctx.json(res, 503, { error: 'InstanceManager not available (daemon not fully initialized)' });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const allStates = ctx.instanceManager.collectAllStates();
|
|
33
|
+
const cliStates = allStates.filter(s => s.category === 'cli' || s.category === 'acp');
|
|
34
|
+
const result = cliStates.map(s => ({
|
|
35
|
+
instanceId: s.instanceId,
|
|
36
|
+
type: s.type,
|
|
37
|
+
name: s.name,
|
|
38
|
+
category: s.category,
|
|
39
|
+
status: s.status,
|
|
40
|
+
mode: s.mode,
|
|
41
|
+
workspace: s.workspace,
|
|
42
|
+
messageCount: s.activeChat?.messages?.length || 0,
|
|
43
|
+
lastMessage: s.activeChat?.messages?.slice(-1)[0] || null,
|
|
44
|
+
activeModal: s.activeChat?.activeModal || null,
|
|
45
|
+
pendingEvents: s.pendingEvents || [],
|
|
46
|
+
currentModel: s.currentModel,
|
|
47
|
+
settings: s.settings,
|
|
48
|
+
}));
|
|
49
|
+
ctx.json(res, 200, { instances: result, count: result.length });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** POST /api/cli/launch — launch a CLI agent { type, workingDir?, args? } */
|
|
53
|
+
export async function handleCliLaunch(ctx: DevServerContext, req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
54
|
+
if (!ctx.cliManager) {
|
|
55
|
+
ctx.json(res, 503, { error: 'CliManager not available' });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const body = await ctx.readBody(req);
|
|
59
|
+
const { type, workingDir, args } = body;
|
|
60
|
+
if (!type) {
|
|
61
|
+
ctx.json(res, 400, { error: 'type required (e.g. claude-cli, gemini-cli)' });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await ctx.cliManager.startSession(type, workingDir || process.cwd(), args || []);
|
|
66
|
+
ctx.json(res, 200, { launched: true, type, workspace: workingDir || process.cwd() });
|
|
67
|
+
} catch (e: any) {
|
|
68
|
+
ctx.json(res, 500, { error: `Launch failed: ${e.message}` });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** POST /api/cli/send — send message to a running CLI { type, text } */
|
|
73
|
+
export async function handleCliSend(ctx: DevServerContext, req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
74
|
+
if (!ctx.instanceManager) {
|
|
75
|
+
ctx.json(res, 503, { error: 'InstanceManager not available' });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const body = await ctx.readBody(req);
|
|
79
|
+
const { type, text, instanceId } = body;
|
|
80
|
+
if (!text) {
|
|
81
|
+
ctx.json(res, 400, { error: 'text required' });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const target = findCliTarget(ctx, type, instanceId);
|
|
86
|
+
if (!target) {
|
|
87
|
+
ctx.json(res, 404, { error: `No running instance found for: ${type || instanceId}` });
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
ctx.instanceManager!.sendEvent(target.instanceId, 'send_message', { text });
|
|
93
|
+
ctx.json(res, 200, { sent: true, type: target.type, instanceId: target.instanceId });
|
|
94
|
+
} catch (e: any) {
|
|
95
|
+
ctx.json(res, 500, { error: `Send failed: ${e.message}` });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** POST /api/cli/stop — stop a running CLI { type } */
|
|
100
|
+
export async function handleCliStop(ctx: DevServerContext, req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
101
|
+
if (!ctx.instanceManager) {
|
|
102
|
+
ctx.json(res, 503, { error: 'InstanceManager not available' });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const body = await ctx.readBody(req);
|
|
106
|
+
const { type, instanceId } = body;
|
|
107
|
+
|
|
108
|
+
const target = findCliTarget(ctx, type, instanceId);
|
|
109
|
+
if (!target) {
|
|
110
|
+
ctx.json(res, 404, { error: `No running instance found for: ${type || instanceId}` });
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
ctx.instanceManager!.removeInstance(target.instanceId);
|
|
116
|
+
ctx.json(res, 200, { stopped: true, type: target.type, instanceId: target.instanceId });
|
|
117
|
+
} catch (e: any) {
|
|
118
|
+
ctx.json(res, 500, { error: `Stop failed: ${e.message}` });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** GET /api/cli/events — SSE stream of CLI status events */
|
|
123
|
+
export function handleCliSSE(ctx: DevServerContext, cliSSEClients: http.ServerResponse[], _req: http.IncomingMessage, res: http.ServerResponse): void {
|
|
124
|
+
res.writeHead(200, {
|
|
125
|
+
'Content-Type': 'text/event-stream',
|
|
126
|
+
'Cache-Control': 'no-cache',
|
|
127
|
+
'Connection': 'keep-alive',
|
|
128
|
+
'Access-Control-Allow-Origin': '*',
|
|
129
|
+
});
|
|
130
|
+
res.write('data: {"type":"connected"}\n\n');
|
|
131
|
+
cliSSEClients.push(res);
|
|
132
|
+
|
|
133
|
+
// Register event listener if first client + instanceManager available
|
|
134
|
+
if (cliSSEClients.length === 1 && ctx.instanceManager) {
|
|
135
|
+
ctx.instanceManager.onEvent((event) => {
|
|
136
|
+
ctx.sendCliSSE(event);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Send current state snapshot immediately
|
|
141
|
+
if (ctx.instanceManager) {
|
|
142
|
+
const allStates = ctx.instanceManager.collectAllStates();
|
|
143
|
+
const cliStates = allStates.filter(s => s.category === 'cli' || s.category === 'acp');
|
|
144
|
+
for (const s of cliStates) {
|
|
145
|
+
ctx.sendCliSSE({ event: 'snapshot', providerType: s.type, status: s.status, instanceId: s.instanceId });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_req.on('close', () => {
|
|
150
|
+
const idx = cliSSEClients.indexOf(res);
|
|
151
|
+
if (idx >= 0) cliSSEClients.splice(idx, 1);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** GET /api/cli/debug/:type — full internal debug state of a CLI adapter */
|
|
156
|
+
export async function handleCliDebug(ctx: DevServerContext, type: string, _req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
157
|
+
if (!ctx.instanceManager) {
|
|
158
|
+
ctx.json(res, 503, { error: 'InstanceManager not available' });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const target = findCliTarget(ctx, type);
|
|
163
|
+
if (!target) {
|
|
164
|
+
const allStates = ctx.instanceManager.collectAllStates();
|
|
165
|
+
ctx.json(res, 404, { error: `No running instance for: ${type}`, available: allStates.filter(s => s.category === 'cli' || s.category === 'acp').map(s => s.type) });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Get the ProviderInstance and access adapter debug state
|
|
170
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId) as any;
|
|
171
|
+
if (!instance) {
|
|
172
|
+
ctx.json(res, 404, { error: `Instance not found: ${target.instanceId}` });
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const adapter = instance.getAdapter?.() || instance.adapter;
|
|
178
|
+
if (adapter && typeof adapter.getDebugState === 'function') {
|
|
179
|
+
const debugState = adapter.getDebugState();
|
|
180
|
+
ctx.json(res, 200, {
|
|
181
|
+
instanceId: target.instanceId,
|
|
182
|
+
providerState: {
|
|
183
|
+
type: target.type,
|
|
184
|
+
name: target.name,
|
|
185
|
+
status: target.status,
|
|
186
|
+
mode: 'mode' in target ? target.mode : undefined,
|
|
187
|
+
},
|
|
188
|
+
debug: debugState,
|
|
189
|
+
});
|
|
190
|
+
} else {
|
|
191
|
+
// Fallback: return what we can from the state
|
|
192
|
+
ctx.json(res, 200, {
|
|
193
|
+
instanceId: target.instanceId,
|
|
194
|
+
providerState: target,
|
|
195
|
+
debug: null,
|
|
196
|
+
message: 'No debug state available (adapter.getDebugState not found)',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
} catch (e: any) {
|
|
200
|
+
ctx.json(res, 500, { error: `Debug state failed: ${e.message}` });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** POST /api/cli/resolve — resolve an approval modal { type, buttonIndex } */
|
|
205
|
+
export async function handleCliResolve(ctx: DevServerContext, req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
206
|
+
const body = await ctx.readBody(req);
|
|
207
|
+
const { type, buttonIndex, instanceId } = body;
|
|
208
|
+
if (buttonIndex === undefined || buttonIndex === null) {
|
|
209
|
+
ctx.json(res, 400, { error: 'buttonIndex required (0=Yes, 1=Always, 2=Deny)' });
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!ctx.cliManager) {
|
|
214
|
+
ctx.json(res, 503, { error: 'CliManager not available' });
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (!ctx.instanceManager) {
|
|
218
|
+
ctx.json(res, 503, { error: 'InstanceManager not available' });
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const target = findCliTarget(ctx, type, instanceId);
|
|
223
|
+
if (!target) {
|
|
224
|
+
ctx.json(res, 404, { error: `No running adapter for: ${type || instanceId}` });
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId) as any;
|
|
229
|
+
const adapter = instance?.getAdapter?.() || instance?.adapter;
|
|
230
|
+
if (!adapter) {
|
|
231
|
+
ctx.json(res, 404, { error: `Adapter not found for instance: ${target.instanceId}` });
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
if (typeof adapter.resolveModal === 'function') {
|
|
237
|
+
adapter.resolveModal(buttonIndex);
|
|
238
|
+
ctx.json(res, 200, { resolved: true, type: target.type, instanceId: target.instanceId, buttonIndex });
|
|
239
|
+
} else {
|
|
240
|
+
ctx.json(res, 400, { error: 'resolveModal not available on this adapter' });
|
|
241
|
+
}
|
|
242
|
+
} catch (e: any) {
|
|
243
|
+
ctx.json(res, 500, { error: `Resolve failed: ${e.message}` });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** POST /api/cli/raw — send raw keystrokes to PTY { type, keys } */
|
|
248
|
+
export async function handleCliRaw(ctx: DevServerContext, req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
249
|
+
const body = await ctx.readBody(req);
|
|
250
|
+
const { type, keys, instanceId } = body;
|
|
251
|
+
if (!keys) {
|
|
252
|
+
ctx.json(res, 400, { error: 'keys required (raw string to send to PTY)' });
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (!ctx.cliManager) {
|
|
257
|
+
ctx.json(res, 503, { error: 'CliManager not available' });
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (!ctx.instanceManager) {
|
|
261
|
+
ctx.json(res, 503, { error: 'InstanceManager not available' });
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const target = findCliTarget(ctx, type, instanceId);
|
|
266
|
+
if (!target) {
|
|
267
|
+
ctx.json(res, 404, { error: `No running adapter for: ${type || instanceId}` });
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const instance = ctx.instanceManager.getInstance(target.instanceId) as any;
|
|
272
|
+
const adapter = instance?.getAdapter?.() || instance?.adapter;
|
|
273
|
+
if (!adapter) {
|
|
274
|
+
ctx.json(res, 404, { error: `Adapter not found for instance: ${target.instanceId}` });
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
if (typeof adapter.writeRaw === 'function') {
|
|
280
|
+
adapter.writeRaw(keys);
|
|
281
|
+
ctx.json(res, 200, { sent: true, type: target.type, instanceId: target.instanceId, keysLength: keys.length });
|
|
282
|
+
} else {
|
|
283
|
+
ctx.json(res, 400, { error: 'writeRaw not available on this adapter' });
|
|
284
|
+
}
|
|
285
|
+
} catch (e: any) {
|
|
286
|
+
ctx.json(res, 500, { error: `Raw send failed: ${e.message}` });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types & context interface for DevServer handler modules.
|
|
3
|
+
*
|
|
4
|
+
* Each handler module (cdp, cli-debug, auto-implement) imports DevServerContext
|
|
5
|
+
* to access shared state and utilities without circular references.
|
|
6
|
+
*/
|
|
7
|
+
import type * as http from 'http';
|
|
8
|
+
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
9
|
+
import type { DaemonCdpManager } from '../cdp/manager.js';
|
|
10
|
+
import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
|
|
11
|
+
import type { DaemonCliManager } from '../commands/cli-manager.js';
|
|
12
|
+
import type { ProviderCategory } from '../providers/contracts.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Context passed from the main DevServer to handler modules.
|
|
16
|
+
* Provides access to shared dependencies and utility methods.
|
|
17
|
+
*/
|
|
18
|
+
export interface DevServerContext {
|
|
19
|
+
readonly providerLoader: ProviderLoader;
|
|
20
|
+
readonly cdpManagers: Map<string, DaemonCdpManager>;
|
|
21
|
+
readonly instanceManager: ProviderInstanceManager | null;
|
|
22
|
+
readonly cliManager: DaemonCliManager | null;
|
|
23
|
+
|
|
24
|
+
// Utilities
|
|
25
|
+
getCdp(ideType?: string): DaemonCdpManager | null;
|
|
26
|
+
json(res: http.ServerResponse, status: number, data: any): void;
|
|
27
|
+
readBody(req: http.IncomingMessage): Promise<any>;
|
|
28
|
+
log(msg: string): void;
|
|
29
|
+
|
|
30
|
+
// SSE utilities
|
|
31
|
+
autoImplSSEClients: http.ServerResponse[];
|
|
32
|
+
sendAutoImplSSE(msg: { event: string; data: any }): void;
|
|
33
|
+
autoImplStatus: { running: boolean; type: string | null; progress: any[] };
|
|
34
|
+
autoImplProcess: import('child_process').ChildProcess | null;
|
|
35
|
+
|
|
36
|
+
// CLI SSE
|
|
37
|
+
sendCliSSE(data: any): void;
|
|
38
|
+
|
|
39
|
+
// Provider directory resolution
|
|
40
|
+
findProviderDir(type: string): string | null;
|
|
41
|
+
getLatestScriptVersionDir(scriptsDir: string): string | null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Re-export for convenience */
|
|
45
|
+
export type { ProviderCategory };
|