@adhdev/daemon-core 0.7.5 → 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 +164 -38
- package/dist/index.d.ts +164 -38
- package/dist/index.js +4051 -2547
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3696 -2192
- 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/agent-stream/forward.ts +6 -0
- 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/extension-provider-instance.ts +50 -10
- package/src/providers/provider-instance-manager.ts +48 -10
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { x as ManagedStatus, Q as isManagedStatusWaiting, T as isManagedStatusWorking, Y as normalizeActiveChatData, Z as normalizeManagedStatus } from '../normalize-auJAPmKy.mjs';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { x as ManagedStatus, Q as isManagedStatusWaiting, T as isManagedStatusWorking, Y as normalizeActiveChatData, Z as normalizeManagedStatus } from '../normalize-auJAPmKy.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"license": "AGPL-3.0-or-later",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
41
|
+
"@adhdev/session-host-core": "*",
|
|
41
42
|
"@xterm/xterm": "^6.0.0",
|
|
42
43
|
"chalk": "^5.3.0",
|
|
43
44
|
"chokidar": "^5.0.0",
|
|
@@ -45,6 +46,9 @@
|
|
|
45
46
|
"node-pty": "^1.1.0",
|
|
46
47
|
"ws": "^8.19.0"
|
|
47
48
|
},
|
|
49
|
+
"optionalDependencies": {
|
|
50
|
+
"@adhdev/ghostty-vt-node": "*"
|
|
51
|
+
},
|
|
48
52
|
"devDependencies": {
|
|
49
53
|
"@types/node": "^22.0.0",
|
|
50
54
|
"@types/ws": "^8.18.1",
|
|
@@ -24,6 +24,12 @@ export function forwardAgentStreamsToIdeInstance(
|
|
|
24
24
|
activeModal: stream.activeModal || null,
|
|
25
25
|
model: stream.model || undefined,
|
|
26
26
|
mode: stream.mode || undefined,
|
|
27
|
+
sessionId: stream.sessionId || stream.instanceId || undefined,
|
|
28
|
+
title: stream.title || stream.agentName || undefined,
|
|
29
|
+
agentType: stream.agentType || undefined,
|
|
30
|
+
agentName: stream.agentName || undefined,
|
|
31
|
+
extensionId: stream.extensionId || undefined,
|
|
32
|
+
inputContent: stream.inputContent || '',
|
|
27
33
|
});
|
|
28
34
|
}
|
|
29
35
|
}
|
|
@@ -312,13 +312,16 @@ export async function shutdownDaemonComponents(components: DaemonComponents): Pr
|
|
|
312
312
|
}
|
|
313
313
|
} catch (e: any) { LOG.warn('Shutdown', `AgentStream dispose: ${e?.message}`); }
|
|
314
314
|
|
|
315
|
-
// 3.
|
|
316
|
-
try {
|
|
315
|
+
// 3. Detach CLIs (persistent runtimes survive daemon restarts)
|
|
316
|
+
try { cliManager.detachAll(); } catch { /* noop */ }
|
|
317
317
|
|
|
318
|
-
// 4.
|
|
318
|
+
// 4. Remove CLI instances without disposing their runtimes again
|
|
319
|
+
try { instanceManager.removeByCategory('cli', { dispose: false }); } catch { /* noop */ }
|
|
320
|
+
|
|
321
|
+
// 5. Dispose remaining instances
|
|
319
322
|
try { instanceManager.disposeAll(); } catch { /* noop */ }
|
|
320
323
|
|
|
321
|
-
//
|
|
324
|
+
// 6. Disconnect CDPs
|
|
322
325
|
for (const m of cdpManagers.values()) {
|
|
323
326
|
try { m.disconnect(); } catch { /* noop */ }
|
|
324
327
|
}
|
package/src/cli-adapter-types.ts
CHANGED
|
@@ -12,7 +12,9 @@ export interface CliAdapter {
|
|
|
12
12
|
sendMessage(text: string): Promise<void>;
|
|
13
13
|
getStatus(): any;
|
|
14
14
|
getPartialResponse(): string;
|
|
15
|
+
saveAndStop?(): Promise<void>;
|
|
15
16
|
shutdown(): void;
|
|
17
|
+
detach?(): void;
|
|
16
18
|
cancel(): void;
|
|
17
19
|
isProcessing(): boolean;
|
|
18
20
|
isReady(): boolean;
|
|
@@ -20,6 +20,13 @@ import { execSync } from 'child_process';
|
|
|
20
20
|
import type { CliAdapter } from '../cli-adapter-types.js';
|
|
21
21
|
import { LOG } from '../logging/logger.js';
|
|
22
22
|
import { TerminalScreen } from './terminal-screen.js';
|
|
23
|
+
import type { ProviderResumeCapability } from '../providers/contracts.js';
|
|
24
|
+
import {
|
|
25
|
+
NodePtyTransportFactory,
|
|
26
|
+
type PtyRuntimeMetadata,
|
|
27
|
+
type PtyRuntimeTransport,
|
|
28
|
+
type PtyTransportFactory,
|
|
29
|
+
} from './pty-transport.js';
|
|
23
30
|
|
|
24
31
|
let pty: any;
|
|
25
32
|
try {
|
|
@@ -71,7 +78,7 @@ export interface CliScripts {
|
|
|
71
78
|
/** Lightweight status detection (high-frequency polling) → AgentStatus string */
|
|
72
79
|
detectStatus?: (input: { tail: string; screenText?: string; rawBuffer?: string }) => string | null;
|
|
73
80
|
/** Parse approval modal from PTY output → ModalInfo | null */
|
|
74
|
-
parseApproval?: (input: { buffer: string; rawBuffer?: string; tail: string }) => { message: string; buttons: string[] } | null;
|
|
81
|
+
parseApproval?: (input: { buffer: string; screenText?: string; rawBuffer?: string; tail: string }) => { message: string; buttons: string[] } | null;
|
|
75
82
|
/** Produce a cli-specific prompt from a dashboard action payload */
|
|
76
83
|
resolveAction?: (data: any) => string;
|
|
77
84
|
/** Custom scripts */
|
|
@@ -128,6 +135,7 @@ export interface CliProviderModule {
|
|
|
128
135
|
/** Output settle debounce before evaluating status (default 300ms) */
|
|
129
136
|
outputSettle?: number;
|
|
130
137
|
};
|
|
138
|
+
resume?: ProviderResumeCapability;
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
// ─── Utility Functions ──────────────────────────────
|
|
@@ -354,7 +362,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
354
362
|
public workingDir: string;
|
|
355
363
|
|
|
356
364
|
private provider: CliProviderModule;
|
|
357
|
-
private ptyProcess:
|
|
365
|
+
private ptyProcess: PtyRuntimeTransport | null = null;
|
|
366
|
+
private transportFactory: PtyTransportFactory;
|
|
358
367
|
private messages: CliChatMessage[] = [];
|
|
359
368
|
private committedMessages: CliChatMessage[] = [];
|
|
360
369
|
private structuredMessages: CliChatMessage[] = [];
|
|
@@ -478,8 +487,14 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
478
487
|
private readonly submitStrategy: 'wait_for_echo' | 'immediate';
|
|
479
488
|
private static readonly SCRIPT_STATUS_DEBOUNCE_MS = 1000;
|
|
480
489
|
|
|
481
|
-
constructor(
|
|
490
|
+
constructor(
|
|
491
|
+
provider: CliProviderModule,
|
|
492
|
+
workingDir: string,
|
|
493
|
+
private extraArgs: string[] = [],
|
|
494
|
+
transportFactory: PtyTransportFactory = new NodePtyTransportFactory(),
|
|
495
|
+
) {
|
|
482
496
|
this.provider = provider;
|
|
497
|
+
this.transportFactory = transportFactory;
|
|
483
498
|
this.cliType = provider.type;
|
|
484
499
|
this.cliName = provider.name;
|
|
485
500
|
this.workingDir = workingDir.startsWith('~')
|
|
@@ -554,7 +569,6 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
554
569
|
|
|
555
570
|
async spawn(): Promise<void> {
|
|
556
571
|
if (this.ptyProcess) return;
|
|
557
|
-
if (!pty) throw new Error('node-pty is not installed');
|
|
558
572
|
|
|
559
573
|
const { spawn: spawnConfig } = this.provider;
|
|
560
574
|
const binaryPath = findBinary(spawnConfig.command);
|
|
@@ -586,7 +600,6 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
586
600
|
}
|
|
587
601
|
|
|
588
602
|
const ptyOpts = {
|
|
589
|
-
name: 'xterm-256color',
|
|
590
603
|
cols: 120,
|
|
591
604
|
rows: 40,
|
|
592
605
|
cwd: this.workingDir,
|
|
@@ -597,7 +610,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
597
610
|
};
|
|
598
611
|
|
|
599
612
|
try {
|
|
600
|
-
this.ptyProcess =
|
|
613
|
+
this.ptyProcess = this.transportFactory.spawn(shellCmd, shellArgs, ptyOpts);
|
|
601
614
|
} catch (err: any) {
|
|
602
615
|
const msg = err?.message || String(err);
|
|
603
616
|
if (!isWin && !useShell && /posix_spawn|spawn/i.test(msg)) {
|
|
@@ -605,7 +618,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
605
618
|
shellCmd = process.env.SHELL || '/bin/zsh';
|
|
606
619
|
const fullCmd = [binaryPath, ...allArgs].map(shSingleQuote).join(' ');
|
|
607
620
|
shellArgs = ['-l', '-c', fullCmd];
|
|
608
|
-
this.ptyProcess =
|
|
621
|
+
this.ptyProcess = this.transportFactory.spawn(shellCmd, shellArgs, ptyOpts);
|
|
609
622
|
} else {
|
|
610
623
|
throw err;
|
|
611
624
|
}
|
|
@@ -659,6 +672,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
659
672
|
this.terminalHistory = '';
|
|
660
673
|
this.currentTurnScope = null;
|
|
661
674
|
this.ready = false;
|
|
675
|
+
await this.ptyProcess.ready;
|
|
662
676
|
this.setStatus('idle', 'pty_ready');
|
|
663
677
|
this.onStatusChange?.();
|
|
664
678
|
}
|
|
@@ -766,6 +780,17 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
766
780
|
}, 60000);
|
|
767
781
|
}
|
|
768
782
|
|
|
783
|
+
private looksLikeVisibleIdlePrompt(screenText: string): boolean {
|
|
784
|
+
const text = String(screenText || '');
|
|
785
|
+
if (!text.trim()) return false;
|
|
786
|
+
return /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(text)
|
|
787
|
+
|| /⏎\s+send/i.test(text)
|
|
788
|
+
|| /\?\s*for\s*shortcuts/i.test(text)
|
|
789
|
+
|| /Type your message(?:\s+or\s+@path\/to\/file)?/i.test(text)
|
|
790
|
+
|| /workspace\s*\(\/directory\)/i.test(text)
|
|
791
|
+
|| /for\s*shortcuts/i.test(text);
|
|
792
|
+
}
|
|
793
|
+
|
|
769
794
|
private evaluateSettled(): void {
|
|
770
795
|
const now = Date.now();
|
|
771
796
|
if (this.submitPendingUntil > now || this.responseSettleIgnoreUntil > now) {
|
|
@@ -779,6 +804,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
779
804
|
return;
|
|
780
805
|
}
|
|
781
806
|
const tail = this.settledBuffer;
|
|
807
|
+
const screenText = this.terminalScreen.getText() || '';
|
|
782
808
|
const modal = this.runParseApproval(tail);
|
|
783
809
|
const rawScriptStatus = this.runDetectStatus(tail);
|
|
784
810
|
// detectStatus is the sole authority for status. parseApproval only enriches modal info.
|
|
@@ -827,6 +853,24 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
827
853
|
|
|
828
854
|
if (scriptStatus === 'waiting_approval') {
|
|
829
855
|
const inCooldown = this.lastApprovalResolvedAt && (Date.now() - this.lastApprovalResolvedAt) < this.timeouts.approvalCooldown;
|
|
856
|
+
const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
857
|
+
if ((inCooldown || visibleIdlePrompt) && !modal) {
|
|
858
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
859
|
+
this.activeModal = null;
|
|
860
|
+
if (this.isWaitingForResponse) {
|
|
861
|
+
this.setStatus('generating', inCooldown ? 'approval_cooldown_ignore' : 'approval_prompt_gone');
|
|
862
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
863
|
+
this.idleTimeout = setTimeout(() => {
|
|
864
|
+
if (this.isWaitingForResponse && this.currentStatus !== 'waiting_approval') {
|
|
865
|
+
this.finishResponse();
|
|
866
|
+
}
|
|
867
|
+
}, this.timeouts.generatingIdle);
|
|
868
|
+
} else {
|
|
869
|
+
this.setStatus('idle', inCooldown ? 'approval_cooldown_ignore' : 'approval_prompt_gone');
|
|
870
|
+
}
|
|
871
|
+
this.onStatusChange?.();
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
830
874
|
if (!inCooldown) {
|
|
831
875
|
this.isWaitingForResponse = true;
|
|
832
876
|
this.setStatus('waiting_approval', 'script_detect');
|
|
@@ -842,13 +886,13 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
842
886
|
}
|
|
843
887
|
|
|
844
888
|
if (scriptStatus === 'generating') {
|
|
845
|
-
const
|
|
889
|
+
const effectiveScreenText = screenText || this.accumulatedBuffer;
|
|
846
890
|
const noActiveTurn = !this.currentTurnScope;
|
|
847
|
-
const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(
|
|
848
|
-
|| (/accept edits on/i.test(
|
|
891
|
+
const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(effectiveScreenText)
|
|
892
|
+
|| (/accept edits on/i.test(effectiveScreenText)
|
|
849
893
|
&& (/Update available!/i.test(screenText)
|
|
850
894
|
|| /\/effort/i.test(screenText)
|
|
851
|
-
|| /^.*➜\s+\S+/m.test(
|
|
895
|
+
|| /^.*➜\s+\S+/m.test(effectiveScreenText)));
|
|
852
896
|
if (prevStatus === 'idle' && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome) {
|
|
853
897
|
return;
|
|
854
898
|
}
|
|
@@ -986,6 +1030,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
986
1030
|
try {
|
|
987
1031
|
return this.cliScripts.parseApproval({
|
|
988
1032
|
buffer: this.terminalScreen.getText() || this.accumulatedBuffer,
|
|
1033
|
+
screenText: this.terminalScreen.getText(),
|
|
989
1034
|
rawBuffer: this.accumulatedRawBuffer,
|
|
990
1035
|
tail,
|
|
991
1036
|
});
|
|
@@ -1203,8 +1248,75 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1203
1248
|
return this.responseBuffer;
|
|
1204
1249
|
}
|
|
1205
1250
|
|
|
1251
|
+
getRuntimeMetadata(): PtyRuntimeMetadata | null {
|
|
1252
|
+
if (!this.ptyProcess || typeof this.ptyProcess.getMetadata !== 'function') return null;
|
|
1253
|
+
return this.ptyProcess.getMetadata();
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1206
1256
|
cancel(): void { this.shutdown(); }
|
|
1207
1257
|
|
|
1258
|
+
async saveAndStop(): Promise<void> {
|
|
1259
|
+
if (!this.ptyProcess) return;
|
|
1260
|
+
const resume = this.provider.resume;
|
|
1261
|
+
if (!resume?.supported) {
|
|
1262
|
+
this.shutdown();
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const stopStrategy = resume.stopStrategy || 'command';
|
|
1267
|
+
const stopCommand = typeof resume.stopCommand === 'string' ? resume.stopCommand.trim() : '';
|
|
1268
|
+
const shutdownGraceMs = Math.max(
|
|
1269
|
+
this.timeouts.shutdownGrace,
|
|
1270
|
+
typeof resume.shutdownGraceMs === 'number' ? resume.shutdownGraceMs : 3000,
|
|
1271
|
+
);
|
|
1272
|
+
const wasProcessing = this.currentStatus === 'generating' || this.currentStatus === 'waiting_approval';
|
|
1273
|
+
|
|
1274
|
+
try {
|
|
1275
|
+
if (wasProcessing) {
|
|
1276
|
+
this.ptyProcess.write('\x03');
|
|
1277
|
+
}
|
|
1278
|
+
if (stopStrategy === 'command' && stopCommand) {
|
|
1279
|
+
const writeCommand = () => {
|
|
1280
|
+
if (!this.ptyProcess) return;
|
|
1281
|
+
const payload = stopCommand.endsWith('\r') || stopCommand.endsWith('\n')
|
|
1282
|
+
? stopCommand
|
|
1283
|
+
: `${stopCommand}${this.sendKey}`;
|
|
1284
|
+
this.ptyProcess.write(payload);
|
|
1285
|
+
};
|
|
1286
|
+
if (wasProcessing) setTimeout(writeCommand, 250);
|
|
1287
|
+
else writeCommand();
|
|
1288
|
+
} else {
|
|
1289
|
+
this.ptyProcess.write('\x03');
|
|
1290
|
+
}
|
|
1291
|
+
} catch (error: any) {
|
|
1292
|
+
LOG.warn('CLI', `[${this.cliType}] saveAndStop signal failed: ${error?.message || error}`);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
const stopped = await this.waitForStopped(shutdownGraceMs);
|
|
1296
|
+
if (!stopped) {
|
|
1297
|
+
LOG.warn('CLI', `[${this.cliType}] graceful stop timed out, forcing shutdown`);
|
|
1298
|
+
this.shutdown();
|
|
1299
|
+
await this.waitForStopped(this.timeouts.shutdownGrace + 500);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
private waitForStopped(timeoutMs: number): Promise<boolean> {
|
|
1304
|
+
return new Promise((resolve) => {
|
|
1305
|
+
const startedAt = Date.now();
|
|
1306
|
+
const timer = setInterval(() => {
|
|
1307
|
+
if (!this.ptyProcess || this.currentStatus === 'stopped') {
|
|
1308
|
+
clearInterval(timer);
|
|
1309
|
+
resolve(true);
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
if (Date.now() - startedAt >= timeoutMs) {
|
|
1313
|
+
clearInterval(timer);
|
|
1314
|
+
resolve(false);
|
|
1315
|
+
}
|
|
1316
|
+
}, 100);
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1208
1320
|
shutdown(): void {
|
|
1209
1321
|
if (this.settleTimer) { clearTimeout(this.settleTimer); this.settleTimer = null; }
|
|
1210
1322
|
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
@@ -1227,6 +1339,30 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1227
1339
|
}
|
|
1228
1340
|
}
|
|
1229
1341
|
|
|
1342
|
+
detach(): void {
|
|
1343
|
+
if (this.settleTimer) { clearTimeout(this.settleTimer); this.settleTimer = null; }
|
|
1344
|
+
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1345
|
+
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
1346
|
+
if (this.pendingOutputParseTimer) { clearTimeout(this.pendingOutputParseTimer); this.pendingOutputParseTimer = null; }
|
|
1347
|
+
this.pendingOutputParseBuffer = '';
|
|
1348
|
+
if (this.ptyOutputFlushTimer) { clearTimeout(this.ptyOutputFlushTimer); this.ptyOutputFlushTimer = null; }
|
|
1349
|
+
this.ptyOutputBuffer = '';
|
|
1350
|
+
if (this.ptyProcess) {
|
|
1351
|
+
try {
|
|
1352
|
+
if (typeof this.ptyProcess.detach === 'function') {
|
|
1353
|
+
this.ptyProcess.detach();
|
|
1354
|
+
} else {
|
|
1355
|
+
this.ptyProcess.kill();
|
|
1356
|
+
}
|
|
1357
|
+
} catch { /* noop */ }
|
|
1358
|
+
this.ptyProcess = null;
|
|
1359
|
+
}
|
|
1360
|
+
this.ready = false;
|
|
1361
|
+
this.startupParseGate = false;
|
|
1362
|
+
this.spawnAt = 0;
|
|
1363
|
+
this.onStatusChange?.();
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1230
1366
|
clearHistory(): void {
|
|
1231
1367
|
this.committedMessages = [];
|
|
1232
1368
|
this.syncMessageViews();
|
|
@@ -1241,6 +1377,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1241
1377
|
if (this.ptyOutputFlushTimer) { clearTimeout(this.ptyOutputFlushTimer); this.ptyOutputFlushTimer = null; }
|
|
1242
1378
|
this.ptyOutputBuffer = '';
|
|
1243
1379
|
this.terminalScreen.reset();
|
|
1380
|
+
this.ptyProcess?.clearBuffer?.();
|
|
1244
1381
|
this.onStatusChange?.();
|
|
1245
1382
|
}
|
|
1246
1383
|
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as os from 'os';
|
|
2
|
+
|
|
3
|
+
let pty: any;
|
|
4
|
+
try {
|
|
5
|
+
pty = require('node-pty');
|
|
6
|
+
} catch {
|
|
7
|
+
pty = null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PtySpawnOptions {
|
|
11
|
+
cwd: string;
|
|
12
|
+
env: Record<string, string>;
|
|
13
|
+
cols: number;
|
|
14
|
+
rows: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PtyRuntimeWriteOwner {
|
|
18
|
+
clientId: string;
|
|
19
|
+
ownerType: 'agent' | 'user';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PtyRuntimeClientInfo {
|
|
23
|
+
clientId: string;
|
|
24
|
+
type: 'daemon' | 'web' | 'local-terminal';
|
|
25
|
+
readOnly: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PtyRuntimeMetadata {
|
|
29
|
+
runtimeId: string;
|
|
30
|
+
runtimeKey?: string;
|
|
31
|
+
displayName?: string;
|
|
32
|
+
workspaceLabel?: string;
|
|
33
|
+
writeOwner?: PtyRuntimeWriteOwner | null;
|
|
34
|
+
attachedClients?: PtyRuntimeClientInfo[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface PtyRuntimeTransport {
|
|
38
|
+
readonly pid: number;
|
|
39
|
+
readonly ready: Promise<void>;
|
|
40
|
+
write(data: string): void;
|
|
41
|
+
resize(cols: number, rows: number): void;
|
|
42
|
+
kill(): void;
|
|
43
|
+
clearBuffer?(): void;
|
|
44
|
+
detach?(): void;
|
|
45
|
+
getMetadata?(): PtyRuntimeMetadata | null;
|
|
46
|
+
onData(callback: (data: string) => void): void;
|
|
47
|
+
onExit(callback: (info: { exitCode: number }) => void): void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface PtyTransportFactory {
|
|
51
|
+
spawn(command: string, args: string[], options: PtySpawnOptions): PtyRuntimeTransport;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class NodePtyRuntimeTransport implements PtyRuntimeTransport {
|
|
55
|
+
readonly ready = Promise.resolve();
|
|
56
|
+
|
|
57
|
+
constructor(private readonly handle: any) {}
|
|
58
|
+
|
|
59
|
+
get pid(): number {
|
|
60
|
+
return this.handle.pid;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
write(data: string): void {
|
|
64
|
+
this.handle.write(data);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
resize(cols: number, rows: number): void {
|
|
68
|
+
this.handle.resize(cols, rows);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
kill(): void {
|
|
72
|
+
this.handle.kill();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getMetadata(): PtyRuntimeMetadata | null {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
onData(callback: (data: string) => void): void {
|
|
80
|
+
this.handle.onData(callback);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
onExit(callback: (info: { exitCode: number }) => void): void {
|
|
84
|
+
this.handle.onExit(callback);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class NodePtyTransportFactory implements PtyTransportFactory {
|
|
89
|
+
spawn(command: string, args: string[], options: PtySpawnOptions): PtyRuntimeTransport {
|
|
90
|
+
if (!pty) throw new Error('node-pty is not installed');
|
|
91
|
+
const handle = pty.spawn(command, args, {
|
|
92
|
+
name: os.platform() === 'win32' ? 'xterm-color' : 'xterm-256color',
|
|
93
|
+
cols: options.cols,
|
|
94
|
+
rows: options.rows,
|
|
95
|
+
cwd: options.cwd,
|
|
96
|
+
env: options.env,
|
|
97
|
+
});
|
|
98
|
+
return new NodePtyRuntimeTransport(handle);
|
|
99
|
+
}
|
|
100
|
+
}
|