@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
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { CliProviderModule } from './provider-cli-shared.js';
|
|
2
|
+
|
|
3
|
+
export interface ProviderResolutionMeta {
|
|
4
|
+
type: string;
|
|
5
|
+
name: string;
|
|
6
|
+
resolvedVersion: string | null;
|
|
7
|
+
resolvedOs: string | null;
|
|
8
|
+
providerDir: string | null;
|
|
9
|
+
scriptDir: string | null;
|
|
10
|
+
scriptsPath: string | null;
|
|
11
|
+
scriptsSource: string | null;
|
|
12
|
+
versionWarning: string | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ResolvedCliAdapterConfig {
|
|
16
|
+
timeouts: {
|
|
17
|
+
ptyFlush: number;
|
|
18
|
+
dialogAccept: number;
|
|
19
|
+
approvalCooldown: number;
|
|
20
|
+
generatingIdle: number;
|
|
21
|
+
idleFinish: number;
|
|
22
|
+
maxResponse: number;
|
|
23
|
+
shutdownGrace: number;
|
|
24
|
+
outputSettle: number;
|
|
25
|
+
};
|
|
26
|
+
approvalKeys: Record<number, string>;
|
|
27
|
+
sendDelayMs: number;
|
|
28
|
+
sendKey: string;
|
|
29
|
+
submitStrategy: 'wait_for_echo' | 'immediate';
|
|
30
|
+
providerResolutionMeta: ProviderResolutionMeta;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function resolveCliAdapterConfig(provider: CliProviderModule): ResolvedCliAdapterConfig {
|
|
34
|
+
const t = provider.timeouts || {};
|
|
35
|
+
const rawKeys = provider.approvalKeys;
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
timeouts: {
|
|
39
|
+
ptyFlush: t.ptyFlush ?? 50,
|
|
40
|
+
dialogAccept: t.dialogAccept ?? 300,
|
|
41
|
+
approvalCooldown: t.approvalCooldown ?? 3000,
|
|
42
|
+
generatingIdle: t.generatingIdle ?? 6000,
|
|
43
|
+
idleFinish: t.idleFinish ?? 5000,
|
|
44
|
+
maxResponse: t.maxResponse ?? 300000,
|
|
45
|
+
shutdownGrace: t.shutdownGrace ?? 1000,
|
|
46
|
+
outputSettle: t.outputSettle ?? 300,
|
|
47
|
+
},
|
|
48
|
+
approvalKeys: (rawKeys && typeof rawKeys === 'object') ? rawKeys : {},
|
|
49
|
+
sendDelayMs: typeof provider.sendDelayMs === 'number' ? Math.max(0, provider.sendDelayMs) : 0,
|
|
50
|
+
sendKey: typeof provider.sendKey === 'string' && provider.sendKey.length > 0
|
|
51
|
+
? provider.sendKey
|
|
52
|
+
: '\r',
|
|
53
|
+
submitStrategy: provider.submitStrategy === 'immediate' ? 'immediate' : 'wait_for_echo',
|
|
54
|
+
providerResolutionMeta: {
|
|
55
|
+
type: provider.type,
|
|
56
|
+
name: provider.name,
|
|
57
|
+
resolvedVersion: provider._resolvedVersion || null,
|
|
58
|
+
resolvedOs: provider._resolvedOs || null,
|
|
59
|
+
providerDir: provider._resolvedProviderDir || null,
|
|
60
|
+
scriptDir: provider._resolvedScriptDir || null,
|
|
61
|
+
scriptsPath: provider._resolvedScriptsPath || null,
|
|
62
|
+
scriptsSource: provider._resolvedScriptsSource || null,
|
|
63
|
+
versionWarning: provider._versionWarning || null,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildCliScreenSnapshot,
|
|
3
|
+
normalizeComparableMessageContent,
|
|
4
|
+
sanitizeTerminalText,
|
|
5
|
+
type CliChatMessage,
|
|
6
|
+
type CliScriptInput,
|
|
7
|
+
} from './provider-cli-shared.js';
|
|
8
|
+
|
|
9
|
+
export interface TurnParseScope {
|
|
10
|
+
prompt: string;
|
|
11
|
+
startedAt: number;
|
|
12
|
+
bufferStart: number;
|
|
13
|
+
rawBufferStart: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function sliceFromOffset(text: string, start: number): string {
|
|
17
|
+
if (!text) return '';
|
|
18
|
+
if (!Number.isFinite(start) || start <= 0) return text;
|
|
19
|
+
if (start >= text.length) return '';
|
|
20
|
+
return text.slice(start);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function hydrateCliParsedMessages(
|
|
24
|
+
parsedMessages: any[],
|
|
25
|
+
options: {
|
|
26
|
+
committedMessages: CliChatMessage[];
|
|
27
|
+
scope?: TurnParseScope | null;
|
|
28
|
+
lastOutputAt: number;
|
|
29
|
+
now?: number;
|
|
30
|
+
},
|
|
31
|
+
): any[] {
|
|
32
|
+
const { committedMessages, scope, lastOutputAt } = options;
|
|
33
|
+
const referenceMessages = [...committedMessages];
|
|
34
|
+
const usedReferenceIndexes = new Set<number>();
|
|
35
|
+
const now = options.now ?? Date.now();
|
|
36
|
+
|
|
37
|
+
const findReferenceTimestamp = (role: 'user' | 'assistant', content: string, parsedIndex: number): number | undefined => {
|
|
38
|
+
const normalizedContent = normalizeComparableMessageContent(content);
|
|
39
|
+
if (!normalizedContent) return undefined;
|
|
40
|
+
|
|
41
|
+
const sameIndex = referenceMessages[parsedIndex];
|
|
42
|
+
if (
|
|
43
|
+
sameIndex
|
|
44
|
+
&& !usedReferenceIndexes.has(parsedIndex)
|
|
45
|
+
&& sameIndex.role === role
|
|
46
|
+
&& normalizeComparableMessageContent(sameIndex.content) === normalizedContent
|
|
47
|
+
&& typeof sameIndex.timestamp === 'number'
|
|
48
|
+
&& Number.isFinite(sameIndex.timestamp)
|
|
49
|
+
) {
|
|
50
|
+
usedReferenceIndexes.add(parsedIndex);
|
|
51
|
+
return sameIndex.timestamp;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (let i = 0; i < referenceMessages.length; i++) {
|
|
55
|
+
if (usedReferenceIndexes.has(i)) continue;
|
|
56
|
+
const candidate = referenceMessages[i];
|
|
57
|
+
if (!candidate || candidate.role !== role) continue;
|
|
58
|
+
const candidateContent = normalizeComparableMessageContent(candidate.content);
|
|
59
|
+
if (!candidateContent) continue;
|
|
60
|
+
const exactMatch = candidateContent === normalizedContent;
|
|
61
|
+
const fuzzyMatch = candidateContent.includes(normalizedContent) || normalizedContent.includes(candidateContent);
|
|
62
|
+
if (!exactMatch && !fuzzyMatch) continue;
|
|
63
|
+
if (typeof candidate.timestamp === 'number' && Number.isFinite(candidate.timestamp)) {
|
|
64
|
+
usedReferenceIndexes.add(i);
|
|
65
|
+
return candidate.timestamp;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return undefined;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return parsedMessages
|
|
73
|
+
.filter((message) => message && (message.role === 'user' || message.role === 'assistant'))
|
|
74
|
+
.map((message, index) => {
|
|
75
|
+
const role = message.role as 'user' | 'assistant';
|
|
76
|
+
const content = typeof message.content === 'string' ? message.content : String(message.content || '');
|
|
77
|
+
const parsedTimestamp = typeof message.timestamp === 'number' && Number.isFinite(message.timestamp)
|
|
78
|
+
? message.timestamp
|
|
79
|
+
: undefined;
|
|
80
|
+
const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(role, content, index);
|
|
81
|
+
const fallbackTimestamp = role === 'user'
|
|
82
|
+
? (scope?.startedAt || now)
|
|
83
|
+
: (lastOutputAt || scope?.startedAt || now);
|
|
84
|
+
const timestamp = referenceTimestamp ?? fallbackTimestamp;
|
|
85
|
+
return {
|
|
86
|
+
...message,
|
|
87
|
+
role,
|
|
88
|
+
content,
|
|
89
|
+
timestamp,
|
|
90
|
+
receivedAt: typeof message.receivedAt === 'number' && Number.isFinite(message.receivedAt)
|
|
91
|
+
? message.receivedAt
|
|
92
|
+
: timestamp,
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function normalizeCliParsedMessages(
|
|
98
|
+
parsedMessages: any[],
|
|
99
|
+
options: {
|
|
100
|
+
committedMessages: CliChatMessage[];
|
|
101
|
+
scope?: TurnParseScope | null;
|
|
102
|
+
lastOutputAt: number;
|
|
103
|
+
now?: number;
|
|
104
|
+
},
|
|
105
|
+
): CliChatMessage[] {
|
|
106
|
+
return hydrateCliParsedMessages(parsedMessages, options).map((message) => ({
|
|
107
|
+
role: message.role,
|
|
108
|
+
content: message.content,
|
|
109
|
+
timestamp: message.timestamp,
|
|
110
|
+
receivedAt: message.receivedAt,
|
|
111
|
+
kind: message.kind,
|
|
112
|
+
id: message.id,
|
|
113
|
+
index: message.index,
|
|
114
|
+
meta: message.meta,
|
|
115
|
+
senderName: message.senderName,
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function buildCliParseInput(options: {
|
|
120
|
+
accumulatedBuffer: string;
|
|
121
|
+
accumulatedRawBuffer: string;
|
|
122
|
+
recentOutputBuffer: string;
|
|
123
|
+
terminalScreenText: string;
|
|
124
|
+
baseMessages: CliChatMessage[];
|
|
125
|
+
partialResponse: string;
|
|
126
|
+
scope?: TurnParseScope | null;
|
|
127
|
+
runtimeSettings: Record<string, any>;
|
|
128
|
+
}): CliScriptInput {
|
|
129
|
+
const {
|
|
130
|
+
accumulatedBuffer,
|
|
131
|
+
accumulatedRawBuffer,
|
|
132
|
+
recentOutputBuffer,
|
|
133
|
+
terminalScreenText,
|
|
134
|
+
baseMessages,
|
|
135
|
+
partialResponse,
|
|
136
|
+
scope,
|
|
137
|
+
runtimeSettings,
|
|
138
|
+
} = options;
|
|
139
|
+
const buffer = scope
|
|
140
|
+
? (sliceFromOffset(accumulatedBuffer, scope.bufferStart) || accumulatedBuffer)
|
|
141
|
+
: accumulatedBuffer;
|
|
142
|
+
const rawBuffer = scope
|
|
143
|
+
? (sliceFromOffset(accumulatedRawBuffer, scope.rawBufferStart) || accumulatedRawBuffer)
|
|
144
|
+
: accumulatedRawBuffer;
|
|
145
|
+
const screenText = terminalScreenText;
|
|
146
|
+
const recentBuffer = buffer.slice(-1000) || recentOutputBuffer;
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
buffer,
|
|
150
|
+
rawBuffer,
|
|
151
|
+
recentBuffer,
|
|
152
|
+
screenText,
|
|
153
|
+
screen: buildCliScreenSnapshot(screenText),
|
|
154
|
+
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
155
|
+
recentScreen: buildCliScreenSnapshot(recentBuffer),
|
|
156
|
+
messages: [...baseMessages],
|
|
157
|
+
partialResponse,
|
|
158
|
+
promptText: scope?.prompt || '',
|
|
159
|
+
settings: { ...runtimeSettings },
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function summarizeCliTraceText(text: string, max = 800): string {
|
|
164
|
+
const value = sanitizeTerminalText(String(text || ''));
|
|
165
|
+
if (value.length <= max) return value;
|
|
166
|
+
return `…${value.slice(-max)}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function summarizeCliTraceMessages(
|
|
170
|
+
messages: CliChatMessage[],
|
|
171
|
+
limit = 3,
|
|
172
|
+
): { role: string; content: string; timestamp?: number }[] {
|
|
173
|
+
return messages.slice(-limit).map((message) => ({
|
|
174
|
+
role: message.role,
|
|
175
|
+
content: summarizeCliTraceText(message.content, 240),
|
|
176
|
+
timestamp: message.timestamp,
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function buildCliTraceParseSnapshot(options: {
|
|
181
|
+
accumulatedBuffer: string;
|
|
182
|
+
accumulatedRawBuffer: string;
|
|
183
|
+
responseBuffer: string;
|
|
184
|
+
partialResponse?: string;
|
|
185
|
+
scope?: TurnParseScope | null;
|
|
186
|
+
}): Record<string, any> {
|
|
187
|
+
const { accumulatedBuffer, accumulatedRawBuffer, responseBuffer, partialResponse, scope } = options;
|
|
188
|
+
const scopedBuffer = scope
|
|
189
|
+
? (sliceFromOffset(accumulatedBuffer, scope.bufferStart) || accumulatedBuffer)
|
|
190
|
+
: accumulatedBuffer;
|
|
191
|
+
const scopedRawBuffer = scope
|
|
192
|
+
? (sliceFromOffset(accumulatedRawBuffer, scope.rawBufferStart) || accumulatedRawBuffer)
|
|
193
|
+
: accumulatedRawBuffer;
|
|
194
|
+
return {
|
|
195
|
+
currentTurnScope: scope || null,
|
|
196
|
+
responseBuffer: summarizeCliTraceText(responseBuffer, 1200),
|
|
197
|
+
partialResponse: summarizeCliTraceText(partialResponse || responseBuffer, 1200),
|
|
198
|
+
turnBuffer: summarizeCliTraceText(scopedBuffer, 1600),
|
|
199
|
+
turnRawPreview: summarizeCliTraceText(scopedRawBuffer, 1600),
|
|
200
|
+
turnSanitizedRawPreview: summarizeCliTraceText(sanitizeTerminalText(scopedRawBuffer), 1600),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as os from 'os';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import type { PtySpawnOptions, PtyRuntimeTransport } from './pty-transport.js';
|
|
4
|
+
import type { TerminalScreen } from './terminal-screen.js';
|
|
5
|
+
import {
|
|
6
|
+
buildCliSpawnEnv,
|
|
7
|
+
findBinary,
|
|
8
|
+
isScriptBinary,
|
|
9
|
+
looksLikeMachOOrElf,
|
|
10
|
+
shSingleQuote,
|
|
11
|
+
type CliProviderModule,
|
|
12
|
+
} from './provider-cli-shared.js';
|
|
13
|
+
|
|
14
|
+
export interface CliSpawnPlan {
|
|
15
|
+
binaryPath: string;
|
|
16
|
+
allArgs: string[];
|
|
17
|
+
shellCmd: string;
|
|
18
|
+
shellArgs: string[];
|
|
19
|
+
ptyOptions: PtySpawnOptions;
|
|
20
|
+
isWin: boolean;
|
|
21
|
+
useShell: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolveCliSpawnPlan(options: {
|
|
25
|
+
provider: CliProviderModule;
|
|
26
|
+
runtimeSettings: Record<string, any>;
|
|
27
|
+
workingDir: string;
|
|
28
|
+
extraArgs: string[];
|
|
29
|
+
}): CliSpawnPlan {
|
|
30
|
+
const { provider, runtimeSettings, workingDir, extraArgs } = options;
|
|
31
|
+
const { spawn: spawnConfig } = provider;
|
|
32
|
+
const configuredCommand = typeof runtimeSettings.executablePath === 'string' && runtimeSettings.executablePath.trim()
|
|
33
|
+
? runtimeSettings.executablePath.trim()
|
|
34
|
+
: spawnConfig.command;
|
|
35
|
+
const binaryPath = findBinary(configuredCommand);
|
|
36
|
+
const isWin = os.platform() === 'win32';
|
|
37
|
+
const allArgs = [...spawnConfig.args, ...extraArgs];
|
|
38
|
+
|
|
39
|
+
let shellCmd: string;
|
|
40
|
+
let shellArgs: string[];
|
|
41
|
+
const useShellUnix = !isWin && (
|
|
42
|
+
!!spawnConfig.shell
|
|
43
|
+
|| !path.isAbsolute(binaryPath)
|
|
44
|
+
|| isScriptBinary(binaryPath)
|
|
45
|
+
|| !looksLikeMachOOrElf(binaryPath)
|
|
46
|
+
);
|
|
47
|
+
const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
|
|
48
|
+
const useShellWin = !!spawnConfig.shell
|
|
49
|
+
|| isCmdShim
|
|
50
|
+
|| !path.isAbsolute(binaryPath)
|
|
51
|
+
|| isScriptBinary(binaryPath);
|
|
52
|
+
const useShell = isWin ? useShellWin : useShellUnix;
|
|
53
|
+
|
|
54
|
+
if (useShell) {
|
|
55
|
+
shellCmd = isWin ? 'cmd.exe' : (process.env.SHELL || '/bin/zsh');
|
|
56
|
+
if (isWin) {
|
|
57
|
+
shellArgs = ['/c', binaryPath, ...allArgs];
|
|
58
|
+
} else {
|
|
59
|
+
const fullCmd = [binaryPath, ...allArgs].map(shSingleQuote).join(' ');
|
|
60
|
+
shellArgs = ['-l', '-c', fullCmd];
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
shellCmd = binaryPath;
|
|
64
|
+
shellArgs = allArgs;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
binaryPath,
|
|
69
|
+
allArgs,
|
|
70
|
+
shellCmd,
|
|
71
|
+
shellArgs,
|
|
72
|
+
isWin,
|
|
73
|
+
useShell,
|
|
74
|
+
ptyOptions: {
|
|
75
|
+
cols: 80,
|
|
76
|
+
rows: 24,
|
|
77
|
+
cwd: workingDir,
|
|
78
|
+
env: buildCliSpawnEnv(process.env, spawnConfig.env),
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function buildCliLoginShellRetry(plan: Pick<CliSpawnPlan, 'binaryPath' | 'allArgs'>): {
|
|
84
|
+
shellCmd: string;
|
|
85
|
+
shellArgs: string[];
|
|
86
|
+
} {
|
|
87
|
+
const shellCmd = process.env.SHELL || '/bin/zsh';
|
|
88
|
+
const fullCmd = [plan.binaryPath, ...plan.allArgs].map(shSingleQuote).join(' ');
|
|
89
|
+
return {
|
|
90
|
+
shellCmd,
|
|
91
|
+
shellArgs: ['-l', '-c', fullCmd],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function getCliSpawnErrorHint(message: string, shellCmd: string, isWin: boolean): string | null {
|
|
96
|
+
if (!isWin) return null;
|
|
97
|
+
if (/error code 267|ERROR_DIRECTORY/i.test(message)) {
|
|
98
|
+
return ' (working directory does not exist or is not a directory)';
|
|
99
|
+
}
|
|
100
|
+
if (/error code 740|elevation/i.test(message)) {
|
|
101
|
+
return ' (requires administrator privileges)';
|
|
102
|
+
}
|
|
103
|
+
if (/error code 2|ENOENT|not found/i.test(message)) {
|
|
104
|
+
return ` (executable not found: ${shellCmd})`;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function respondToCliTerminalQueries(options: {
|
|
110
|
+
ptyProcess: PtyRuntimeTransport | null;
|
|
111
|
+
pendingTail: string;
|
|
112
|
+
data: string;
|
|
113
|
+
terminalScreen: TerminalScreen;
|
|
114
|
+
}): string {
|
|
115
|
+
const { ptyProcess, pendingTail, data, terminalScreen } = options;
|
|
116
|
+
if (!ptyProcess || !data) return pendingTail;
|
|
117
|
+
|
|
118
|
+
const combined = pendingTail + data;
|
|
119
|
+
const regex = /\x1b\[(\?)?6n/g;
|
|
120
|
+
let match: RegExpExecArray | null;
|
|
121
|
+
|
|
122
|
+
while ((match = regex.exec(combined)) !== null) {
|
|
123
|
+
const cursor = terminalScreen.getCursorPosition();
|
|
124
|
+
const row = Math.max(1, (cursor.row | 0) + 1);
|
|
125
|
+
const col = Math.max(1, (cursor.col | 0) + 1);
|
|
126
|
+
const response = match[1]
|
|
127
|
+
? `\x1b[?${row};${col}R`
|
|
128
|
+
: `\x1b[${row};${col}R`;
|
|
129
|
+
ptyProcess.write(response);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const prefixes = ['\x1b[6n', '\x1b[?6n'];
|
|
133
|
+
const maxLength = prefixes.reduce((n, value) => Math.max(n, value.length), 0) - 1;
|
|
134
|
+
const start = Math.max(0, combined.length - maxLength);
|
|
135
|
+
for (let i = start; i < combined.length; i++) {
|
|
136
|
+
const suffix = combined.slice(i);
|
|
137
|
+
if (prefixes.some((pattern) => suffix.length < pattern.length && pattern.startsWith(suffix))) {
|
|
138
|
+
return suffix;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return '';
|
|
142
|
+
}
|