adhdev 0.1.34 → 0.1.36
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.js +2174 -265
- package/package.json +55 -55
package/dist/index.js
CHANGED
|
@@ -32,18 +32,473 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
32
32
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
33
33
|
mod
|
|
34
34
|
));
|
|
35
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
35
36
|
|
|
36
37
|
// ../core/dist/chunk-PP6RFM75.js
|
|
38
|
+
function generateSecureToken(type) {
|
|
39
|
+
const prefix = TOKEN_PREFIX[type];
|
|
40
|
+
const randomBytes = new Uint8Array(32);
|
|
41
|
+
if (typeof globalThis.crypto?.getRandomValues !== "undefined") {
|
|
42
|
+
crypto.getRandomValues(randomBytes);
|
|
43
|
+
} else {
|
|
44
|
+
for (let i = 0; i < 32; i++) {
|
|
45
|
+
randomBytes[i] = Math.floor(Math.random() * 256);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
49
|
+
let token = prefix;
|
|
50
|
+
for (const byte of randomBytes) {
|
|
51
|
+
token += chars[byte % chars.length];
|
|
52
|
+
}
|
|
53
|
+
return token;
|
|
54
|
+
}
|
|
55
|
+
var TOKEN_PREFIX, AuthClient, FileTokenStorage;
|
|
37
56
|
var init_chunk_PP6RFM75 = __esm({
|
|
38
57
|
"../core/dist/chunk-PP6RFM75.js"() {
|
|
39
58
|
"use strict";
|
|
59
|
+
TOKEN_PREFIX = {
|
|
60
|
+
connection: "adc_",
|
|
61
|
+
apiKey: "adk_",
|
|
62
|
+
refresh: "adr_"
|
|
63
|
+
};
|
|
64
|
+
AuthClient = class {
|
|
65
|
+
options;
|
|
66
|
+
constructor(options) {
|
|
67
|
+
this.options = options;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* OAuth PKCE 로그인 URL 생성
|
|
71
|
+
*/
|
|
72
|
+
async getLoginUrl(provider) {
|
|
73
|
+
const codeVerifier = this.generateCodeVerifier();
|
|
74
|
+
const codeChallenge = await this.generateCodeChallenge(codeVerifier);
|
|
75
|
+
const state = this.generateRandomString(32);
|
|
76
|
+
const params = new URLSearchParams({
|
|
77
|
+
provider,
|
|
78
|
+
code_challenge: codeChallenge,
|
|
79
|
+
code_challenge_method: "S256",
|
|
80
|
+
state,
|
|
81
|
+
redirect_uri: `${this.options.serverUrl}/auth/callback`
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
url: `${this.options.serverUrl}/auth/login?${params}`,
|
|
85
|
+
codeVerifier,
|
|
86
|
+
state
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* OAuth 콜백 처리 → 토큰 저장
|
|
91
|
+
*/
|
|
92
|
+
async handleCallback(code, codeVerifier) {
|
|
93
|
+
const response = await fetch(`${this.options.serverUrl}/auth/token`, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: { "Content-Type": "application/json" },
|
|
96
|
+
body: JSON.stringify({ code, code_verifier: codeVerifier })
|
|
97
|
+
});
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
throw new Error(`Auth failed: ${response.statusText}`);
|
|
100
|
+
}
|
|
101
|
+
const tokens = await response.json();
|
|
102
|
+
await this.options.storage.set("access_token", tokens.accessToken);
|
|
103
|
+
await this.options.storage.set("refresh_token", tokens.refreshToken);
|
|
104
|
+
return tokens;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Access Token 갱신
|
|
108
|
+
*/
|
|
109
|
+
async refreshAccessToken() {
|
|
110
|
+
const refreshToken = await this.options.storage.get("refresh_token");
|
|
111
|
+
if (!refreshToken) return null;
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch(`${this.options.serverUrl}/auth/refresh`, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: { "Content-Type": "application/json" },
|
|
116
|
+
body: JSON.stringify({ refresh_token: refreshToken })
|
|
117
|
+
});
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
await this.logout();
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const { accessToken, refreshToken: newRefreshToken } = await response.json();
|
|
123
|
+
await this.options.storage.set("access_token", accessToken);
|
|
124
|
+
if (newRefreshToken) {
|
|
125
|
+
await this.options.storage.set("refresh_token", newRefreshToken);
|
|
126
|
+
}
|
|
127
|
+
return accessToken;
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 현재 유효한 Access Token 가져오기 (필요시 자동 갱신)
|
|
134
|
+
*/
|
|
135
|
+
async getValidAccessToken() {
|
|
136
|
+
const token = await this.options.storage.get("access_token");
|
|
137
|
+
if (!token) return this.refreshAccessToken();
|
|
138
|
+
if (this.isTokenExpired(token)) {
|
|
139
|
+
return this.refreshAccessToken();
|
|
140
|
+
}
|
|
141
|
+
return token;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Connection Token 생성 (IDE 브리지용)
|
|
145
|
+
*/
|
|
146
|
+
async createConnectionToken() {
|
|
147
|
+
const accessToken = await this.getValidAccessToken();
|
|
148
|
+
if (!accessToken) throw new Error("Not authenticated");
|
|
149
|
+
const response = await fetch(
|
|
150
|
+
`${this.options.serverUrl}/auth/connection-token`,
|
|
151
|
+
{
|
|
152
|
+
method: "POST",
|
|
153
|
+
headers: {
|
|
154
|
+
Authorization: `Bearer ${accessToken}`,
|
|
155
|
+
"Content-Type": "application/json"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
if (!response.ok) throw new Error("Failed to create connection token");
|
|
160
|
+
const { connectionToken } = await response.json();
|
|
161
|
+
await this.options.storage.set("connection_token", connectionToken);
|
|
162
|
+
return connectionToken;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* API Key 생성
|
|
166
|
+
*/
|
|
167
|
+
async createApiKey(name, scopes) {
|
|
168
|
+
const accessToken = await this.getValidAccessToken();
|
|
169
|
+
if (!accessToken) throw new Error("Not authenticated");
|
|
170
|
+
const response = await fetch(`${this.options.serverUrl}/api/v1/api-keys`, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
headers: {
|
|
173
|
+
Authorization: `Bearer ${accessToken}`,
|
|
174
|
+
"Content-Type": "application/json"
|
|
175
|
+
},
|
|
176
|
+
body: JSON.stringify({ name, scopes })
|
|
177
|
+
});
|
|
178
|
+
if (!response.ok) throw new Error("Failed to create API key");
|
|
179
|
+
return response.json();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 로그아웃
|
|
183
|
+
*/
|
|
184
|
+
async logout() {
|
|
185
|
+
await this.options.storage.delete("access_token");
|
|
186
|
+
await this.options.storage.delete("refresh_token");
|
|
187
|
+
await this.options.storage.delete("connection_token");
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 로그인 여부 확인
|
|
191
|
+
*/
|
|
192
|
+
async isAuthenticated() {
|
|
193
|
+
const token = await this.getValidAccessToken();
|
|
194
|
+
return token !== null;
|
|
195
|
+
}
|
|
196
|
+
// ─── Crypto Helpers ───────────────────────────────────────
|
|
197
|
+
generateCodeVerifier() {
|
|
198
|
+
return this.generateRandomString(64);
|
|
199
|
+
}
|
|
200
|
+
async generateCodeChallenge(verifier) {
|
|
201
|
+
if (typeof globalThis.crypto?.subtle !== "undefined") {
|
|
202
|
+
const encoder = new TextEncoder();
|
|
203
|
+
const data = encoder.encode(verifier);
|
|
204
|
+
const hash2 = await crypto.subtle.digest("SHA-256", data);
|
|
205
|
+
return this.base64url(new Uint8Array(hash2));
|
|
206
|
+
}
|
|
207
|
+
const { createHash: createHash3 } = await import("crypto");
|
|
208
|
+
const hash = createHash3("sha256").update(verifier).digest();
|
|
209
|
+
return this.base64url(new Uint8Array(hash));
|
|
210
|
+
}
|
|
211
|
+
generateRandomString(length) {
|
|
212
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
213
|
+
let result = "";
|
|
214
|
+
const randomValues = new Uint8Array(length);
|
|
215
|
+
if (typeof globalThis.crypto?.getRandomValues !== "undefined") {
|
|
216
|
+
crypto.getRandomValues(randomValues);
|
|
217
|
+
} else {
|
|
218
|
+
for (let i = 0; i < length; i++) {
|
|
219
|
+
randomValues[i] = Math.floor(Math.random() * 256);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
for (let i = 0; i < length; i++) {
|
|
223
|
+
result += chars[randomValues[i] % chars.length];
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
base64url(bytes) {
|
|
228
|
+
let binary = "";
|
|
229
|
+
for (const byte of bytes) {
|
|
230
|
+
binary += String.fromCharCode(byte);
|
|
231
|
+
}
|
|
232
|
+
if (typeof btoa !== "undefined") {
|
|
233
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
234
|
+
}
|
|
235
|
+
return Buffer.from(bytes).toString("base64url");
|
|
236
|
+
}
|
|
237
|
+
isTokenExpired(jwt) {
|
|
238
|
+
try {
|
|
239
|
+
const parts = jwt.split(".");
|
|
240
|
+
if (parts.length !== 3) return true;
|
|
241
|
+
const payload = JSON.parse(
|
|
242
|
+
typeof atob !== "undefined" ? atob(parts[1]) : Buffer.from(parts[1], "base64").toString()
|
|
243
|
+
);
|
|
244
|
+
if (!payload.exp) return false;
|
|
245
|
+
return Date.now() >= payload.exp * 1e3;
|
|
246
|
+
} catch {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
FileTokenStorage = class {
|
|
252
|
+
configDir;
|
|
253
|
+
constructor(configDir) {
|
|
254
|
+
this.configDir = configDir;
|
|
255
|
+
}
|
|
256
|
+
async get(key) {
|
|
257
|
+
const { existsSync: existsSync7, readFileSync: readFileSync7 } = await import("fs");
|
|
258
|
+
const { join: join9 } = await import("path");
|
|
259
|
+
const filePath = join9(this.configDir, `${key}.secret`);
|
|
260
|
+
if (!existsSync7(filePath)) return null;
|
|
261
|
+
try {
|
|
262
|
+
return readFileSync7(filePath, "utf-8").trim();
|
|
263
|
+
} catch {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async set(key, value) {
|
|
268
|
+
const { mkdirSync: mkdirSync7, writeFileSync: writeFileSync5, chmodSync } = await import("fs");
|
|
269
|
+
const { join: join9 } = await import("path");
|
|
270
|
+
const { existsSync: existsSync7 } = await import("fs");
|
|
271
|
+
if (!existsSync7(this.configDir)) {
|
|
272
|
+
mkdirSync7(this.configDir, { recursive: true });
|
|
273
|
+
}
|
|
274
|
+
const filePath = join9(this.configDir, `${key}.secret`);
|
|
275
|
+
writeFileSync5(filePath, value, "utf-8");
|
|
276
|
+
try {
|
|
277
|
+
chmodSync(filePath, 384);
|
|
278
|
+
} catch {
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async delete(key) {
|
|
282
|
+
const { unlinkSync: unlinkSync4, existsSync: existsSync7 } = await import("fs");
|
|
283
|
+
const { join: join9 } = await import("path");
|
|
284
|
+
const filePath = join9(this.configDir, `${key}.secret`);
|
|
285
|
+
if (existsSync7(filePath)) {
|
|
286
|
+
unlinkSync4(filePath);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
40
290
|
}
|
|
41
291
|
});
|
|
42
292
|
|
|
43
293
|
// ../core/dist/chunk-ZZGGFYGY.js
|
|
294
|
+
function getCompatibilityManager(matrixUrl) {
|
|
295
|
+
if (!_instance) {
|
|
296
|
+
_instance = new CompatibilityManager(matrixUrl);
|
|
297
|
+
}
|
|
298
|
+
return _instance;
|
|
299
|
+
}
|
|
300
|
+
var DEFAULT_MATRIX, CompatibilityManager, _instance;
|
|
44
301
|
var init_chunk_ZZGGFYGY = __esm({
|
|
45
302
|
"../core/dist/chunk-ZZGGFYGY.js"() {
|
|
46
303
|
"use strict";
|
|
304
|
+
DEFAULT_MATRIX = {
|
|
305
|
+
version: "2026-02-25-default",
|
|
306
|
+
ides: {
|
|
307
|
+
vscode: {
|
|
308
|
+
minVersion: "1.85.0",
|
|
309
|
+
testedVersions: ["1.85.0", "1.90.0", "1.95.0", "2.0.0", "2.5.0"]
|
|
310
|
+
},
|
|
311
|
+
cursor: {
|
|
312
|
+
minVersion: "0.40.0",
|
|
313
|
+
testedVersions: ["0.40.0", "0.42.0", "0.45.0"],
|
|
314
|
+
notes: "Forked VS Code, some command IDs may differ"
|
|
315
|
+
},
|
|
316
|
+
vscodium: {
|
|
317
|
+
minVersion: "1.85.0",
|
|
318
|
+
testedVersions: ["1.85.0", "1.90.0"]
|
|
319
|
+
},
|
|
320
|
+
windsurf: {
|
|
321
|
+
minVersion: "1.0.0",
|
|
322
|
+
testedVersions: ["1.0.0"]
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
agents: {
|
|
326
|
+
"roo-code": {
|
|
327
|
+
extensionId: "rooveterinaryinc.roo-cline",
|
|
328
|
+
versions: {
|
|
329
|
+
"*": {
|
|
330
|
+
send: "roo-cline.sendMessage",
|
|
331
|
+
approve: "roo-cline.approveAction",
|
|
332
|
+
reject: "roo-cline.rejectAction",
|
|
333
|
+
status: "roo-cline.getStatus",
|
|
334
|
+
open: "roo-cline.focus"
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
copilot: {
|
|
339
|
+
extensionId: "github.copilot",
|
|
340
|
+
versions: {
|
|
341
|
+
"*": {
|
|
342
|
+
send: "github.copilot.chat.sendMessage",
|
|
343
|
+
accept: "editor.action.inlineSuggest.commit",
|
|
344
|
+
reject: "editor.action.inlineSuggest.hide",
|
|
345
|
+
open: "github.copilot.chat.focus"
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
cline: {
|
|
350
|
+
extensionId: "saoudrizwan.claude-dev",
|
|
351
|
+
versions: {
|
|
352
|
+
"*": {
|
|
353
|
+
send: "cline.sendMessage",
|
|
354
|
+
approve: "cline.approveAction",
|
|
355
|
+
reject: "cline.rejectAction",
|
|
356
|
+
open: "cline.focus"
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
continue: {
|
|
361
|
+
extensionId: "continue.continue",
|
|
362
|
+
versions: {
|
|
363
|
+
"*": {
|
|
364
|
+
send: "continue.sendToChat",
|
|
365
|
+
open: "continue.focusChat"
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
CompatibilityManager = class {
|
|
372
|
+
matrix;
|
|
373
|
+
matrixUrl;
|
|
374
|
+
lastFetch = 0;
|
|
375
|
+
fetchIntervalMs = 36e5;
|
|
376
|
+
// 1시간마다 갱신
|
|
377
|
+
constructor(matrixUrl) {
|
|
378
|
+
this.matrix = { ...DEFAULT_MATRIX };
|
|
379
|
+
this.matrixUrl = matrixUrl || "https://api.adhdev.io/compatibility/matrix.json";
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* 원격에서 최신 호환성 매트릭스 가져오기
|
|
383
|
+
*/
|
|
384
|
+
async fetchMatrix() {
|
|
385
|
+
const now = Date.now();
|
|
386
|
+
if (now - this.lastFetch < this.fetchIntervalMs) return;
|
|
387
|
+
try {
|
|
388
|
+
const response = await fetch(this.matrixUrl);
|
|
389
|
+
if (response.ok) {
|
|
390
|
+
const remote = await response.json();
|
|
391
|
+
this.matrix = remote;
|
|
392
|
+
this.lastFetch = now;
|
|
393
|
+
console.log(`[Compat] Matrix updated: ${remote.version}`);
|
|
394
|
+
}
|
|
395
|
+
} catch (error) {
|
|
396
|
+
console.warn("[Compat] Failed to fetch remote matrix, using local fallback");
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* 특정 IDE가 지원되는지 확인
|
|
401
|
+
*/
|
|
402
|
+
isIDESupported(ideType, ideVersion) {
|
|
403
|
+
const entry = this.matrix.ides[ideType];
|
|
404
|
+
if (!entry) {
|
|
405
|
+
return {
|
|
406
|
+
supported: false,
|
|
407
|
+
tested: false,
|
|
408
|
+
warnings: [`IDE '${ideType}' is not in the compatibility matrix`]
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
const warnings = [];
|
|
412
|
+
const supported = this.compareVersions(ideVersion, entry.minVersion) >= 0;
|
|
413
|
+
const tested = entry.testedVersions.includes(ideVersion);
|
|
414
|
+
if (!tested && supported) {
|
|
415
|
+
warnings.push(
|
|
416
|
+
`IDE version ${ideVersion} has not been tested. Tested versions: ${entry.testedVersions.join(", ")}`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
if (entry.knownIssues?.[ideVersion]) {
|
|
420
|
+
warnings.push(`Known issue for v${ideVersion}: ${entry.knownIssues[ideVersion]}`);
|
|
421
|
+
}
|
|
422
|
+
return { supported, tested, warnings };
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* AI 에이전트의 명령어 확인
|
|
426
|
+
* 버전에 맞는 정확한 VS Code 명령어 ID를 반환
|
|
427
|
+
*/
|
|
428
|
+
getAgentCommand(agentType, action, agentVersion) {
|
|
429
|
+
const agent = this.matrix.agents[agentType];
|
|
430
|
+
if (!agent) {
|
|
431
|
+
return { command: null, unsupported: true };
|
|
432
|
+
}
|
|
433
|
+
let commands = null;
|
|
434
|
+
if (agentVersion) {
|
|
435
|
+
const majorMinor = agentVersion.split(".").slice(0, 2).join(".");
|
|
436
|
+
const major = agentVersion.split(".")[0] + ".x";
|
|
437
|
+
commands = agent.versions[agentVersion] || agent.versions[majorMinor] || agent.versions[major] || agent.versions["*"] || null;
|
|
438
|
+
} else {
|
|
439
|
+
commands = agent.versions["*"] || Object.values(agent.versions)[0] || null;
|
|
440
|
+
}
|
|
441
|
+
if (!commands) {
|
|
442
|
+
return { command: null, unsupported: true };
|
|
443
|
+
}
|
|
444
|
+
const command = commands[action];
|
|
445
|
+
if (!command) {
|
|
446
|
+
const availableActions = Object.keys(commands);
|
|
447
|
+
return {
|
|
448
|
+
command: null,
|
|
449
|
+
unsupported: true,
|
|
450
|
+
fallback: `Available actions for ${agentType}: ${availableActions.join(", ")}`
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return { command, unsupported: false };
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* 현재 매트릭스 버전 정보
|
|
457
|
+
*/
|
|
458
|
+
getMatrixVersion() {
|
|
459
|
+
return this.matrix.version;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* 매트릭스를 수동으로 업데이트 (테스트/디버그용)
|
|
463
|
+
*/
|
|
464
|
+
updateMatrix(matrix) {
|
|
465
|
+
this.matrix = matrix;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* 지원되는 모든 에이전트 목록
|
|
469
|
+
*/
|
|
470
|
+
getSupportedAgents() {
|
|
471
|
+
return Object.keys(this.matrix.agents);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* 특정 에이전트의 지원 액션 목록
|
|
475
|
+
*/
|
|
476
|
+
getAgentActions(agentType) {
|
|
477
|
+
const agent = this.matrix.agents[agentType];
|
|
478
|
+
if (!agent) return [];
|
|
479
|
+
const actions = /* @__PURE__ */ new Set();
|
|
480
|
+
for (const commands of Object.values(agent.versions)) {
|
|
481
|
+
for (const action of Object.keys(commands)) {
|
|
482
|
+
actions.add(action);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return Array.from(actions);
|
|
486
|
+
}
|
|
487
|
+
// ─── Private Helpers ──────────────────────────────────────
|
|
488
|
+
compareVersions(a, b) {
|
|
489
|
+
const pa = a.split(".").map(Number);
|
|
490
|
+
const pb = b.split(".").map(Number);
|
|
491
|
+
const len = Math.max(pa.length, pb.length);
|
|
492
|
+
for (let i = 0; i < len; i++) {
|
|
493
|
+
const va = pa[i] || 0;
|
|
494
|
+
const vb = pb[i] || 0;
|
|
495
|
+
if (va > vb) return 1;
|
|
496
|
+
if (va < vb) return -1;
|
|
497
|
+
}
|
|
498
|
+
return 0;
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
_instance = null;
|
|
47
502
|
}
|
|
48
503
|
});
|
|
49
504
|
|
|
@@ -87,18 +542,18 @@ function checkPathExists(paths) {
|
|
|
87
542
|
return null;
|
|
88
543
|
}
|
|
89
544
|
async function detectIDEs() {
|
|
90
|
-
const
|
|
545
|
+
const os11 = (0, import_os.platform)();
|
|
91
546
|
const results = [];
|
|
92
547
|
for (const def of IDE_DEFINITIONS) {
|
|
93
548
|
const cliPath = findCliCommand(def.cli);
|
|
94
|
-
const appPath = checkPathExists(def.paths[
|
|
549
|
+
const appPath = checkPathExists(def.paths[os11] || []);
|
|
95
550
|
const installed = !!(cliPath || appPath);
|
|
96
551
|
let resolvedCli = cliPath;
|
|
97
|
-
if (!resolvedCli && appPath &&
|
|
552
|
+
if (!resolvedCli && appPath && os11 === "darwin") {
|
|
98
553
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
99
554
|
if ((0, import_fs.existsSync)(bundledCli)) resolvedCli = bundledCli;
|
|
100
555
|
}
|
|
101
|
-
if (!resolvedCli && appPath &&
|
|
556
|
+
if (!resolvedCli && appPath && os11 === "win32") {
|
|
102
557
|
const { dirname: dirname4 } = await import("path");
|
|
103
558
|
const appDir = dirname4(appPath);
|
|
104
559
|
const candidates = [
|
|
@@ -130,6 +585,18 @@ async function detectIDEs() {
|
|
|
130
585
|
}
|
|
131
586
|
return results;
|
|
132
587
|
}
|
|
588
|
+
async function getInstalledIDEs() {
|
|
589
|
+
const all = await detectIDEs();
|
|
590
|
+
return all.filter((ide) => ide.installed);
|
|
591
|
+
}
|
|
592
|
+
async function isIdeInstalled(ideId) {
|
|
593
|
+
const all = await detectIDEs();
|
|
594
|
+
const ide = all.find((i) => i.id === ideId);
|
|
595
|
+
return ide?.installed ?? false;
|
|
596
|
+
}
|
|
597
|
+
function getIdeDefinitions() {
|
|
598
|
+
return IDE_DEFINITIONS;
|
|
599
|
+
}
|
|
133
600
|
var import_child_process, import_fs, import_os, IDE_DEFINITIONS;
|
|
134
601
|
var init_chunk_BFUPGBW2 = __esm({
|
|
135
602
|
"../core/dist/chunk-BFUPGBW2.js"() {
|
|
@@ -230,6 +697,20 @@ var init_chunk_BFUPGBW2 = __esm({
|
|
|
230
697
|
});
|
|
231
698
|
|
|
232
699
|
// ../core/dist/index.js
|
|
700
|
+
var dist_exports = {};
|
|
701
|
+
__export(dist_exports, {
|
|
702
|
+
AuthClient: () => AuthClient,
|
|
703
|
+
CompatibilityManager: () => CompatibilityManager,
|
|
704
|
+
DAEMON_WS_PATH: () => DAEMON_WS_PATH,
|
|
705
|
+
DEFAULT_DAEMON_PORT: () => DEFAULT_DAEMON_PORT,
|
|
706
|
+
FileTokenStorage: () => FileTokenStorage,
|
|
707
|
+
detectIDEs: () => detectIDEs,
|
|
708
|
+
generateSecureToken: () => generateSecureToken,
|
|
709
|
+
getCompatibilityManager: () => getCompatibilityManager,
|
|
710
|
+
getIdeDefinitions: () => getIdeDefinitions,
|
|
711
|
+
getInstalledIDEs: () => getInstalledIDEs,
|
|
712
|
+
isIdeInstalled: () => isIdeInstalled
|
|
713
|
+
});
|
|
233
714
|
var DEFAULT_DAEMON_PORT, DAEMON_WS_PATH;
|
|
234
715
|
var init_dist = __esm({
|
|
235
716
|
"../core/dist/index.js"() {
|
|
@@ -364,8 +845,9 @@ async function detectCLIs() {
|
|
|
364
845
|
return results;
|
|
365
846
|
}
|
|
366
847
|
async function detectCLI(cliId) {
|
|
848
|
+
const normalizedId = cliId === "claude-cli" ? "claude-code" : cliId;
|
|
367
849
|
const all = await detectCLIs();
|
|
368
|
-
return all.find((c) => c.id ===
|
|
850
|
+
return all.find((c) => c.id === normalizedId && c.installed) || null;
|
|
369
851
|
}
|
|
370
852
|
var import_child_process3, os, KNOWN_CLIS;
|
|
371
853
|
var init_cli_detector = __esm({
|
|
@@ -538,7 +1020,7 @@ function detectCurrentWorkspace(ideId) {
|
|
|
538
1020
|
}
|
|
539
1021
|
} else if (plat === "win32") {
|
|
540
1022
|
try {
|
|
541
|
-
const
|
|
1023
|
+
const fs6 = require("fs");
|
|
542
1024
|
const appNameMap = {
|
|
543
1025
|
vscode: "Code",
|
|
544
1026
|
cursor: "Cursor",
|
|
@@ -553,8 +1035,8 @@ function detectCurrentWorkspace(ideId) {
|
|
|
553
1035
|
appName,
|
|
554
1036
|
"storage.json"
|
|
555
1037
|
);
|
|
556
|
-
if (
|
|
557
|
-
const data = JSON.parse(
|
|
1038
|
+
if (fs6.existsSync(storagePath)) {
|
|
1039
|
+
const data = JSON.parse(fs6.readFileSync(storagePath, "utf-8"));
|
|
558
1040
|
const workspaces = data?.openedPathsList?.workspaces3 || data?.openedPathsList?.entries || [];
|
|
559
1041
|
if (workspaces.length > 0) {
|
|
560
1042
|
const recent = workspaces[0];
|
|
@@ -933,6 +1415,47 @@ var init_cli_bridge = __esm({
|
|
|
933
1415
|
function stripAnsi(str) {
|
|
934
1416
|
return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][^\x1B]*\x1B\\/g, "");
|
|
935
1417
|
}
|
|
1418
|
+
function cleanGeminiResponse(raw, lastUserInput) {
|
|
1419
|
+
const lines = raw.split("\n");
|
|
1420
|
+
const cleaned = [];
|
|
1421
|
+
for (const line of lines) {
|
|
1422
|
+
const trimmed = line.trim();
|
|
1423
|
+
if (!trimmed) {
|
|
1424
|
+
cleaned.push("");
|
|
1425
|
+
continue;
|
|
1426
|
+
}
|
|
1427
|
+
if (/^[\u256d\u256e\u2570\u256f\u2502\u251c\u2524\u252c\u2534\u253c\u2500\u2501\u2550\u2554\u2557\u255a\u255d\u2551]+$/.test(trimmed)) continue;
|
|
1428
|
+
if (/^[\u256d\u2570]\u2500\u2500/.test(trimmed) || /\u2500\u2500[\u256e\u256f]$/.test(trimmed)) continue;
|
|
1429
|
+
if (/^\u2502.*\u2502$/.test(trimmed)) continue;
|
|
1430
|
+
if (/[\u2500\u2501\u2550\u2580\u2584]{4,}/.test(trimmed)) continue;
|
|
1431
|
+
if (/^YOLO\s+ctrl\+y/i.test(trimmed)) continue;
|
|
1432
|
+
if (/^\? for shortcuts/.test(trimmed)) continue;
|
|
1433
|
+
if (/^\/model\s/i.test(trimmed)) continue;
|
|
1434
|
+
if (/^~\s.*no sandbox/i.test(trimmed)) continue;
|
|
1435
|
+
if (/^Type your message/i.test(trimmed)) continue;
|
|
1436
|
+
if (/ctrl\+[a-z]/i.test(trimmed) && trimmed.length < 50) continue;
|
|
1437
|
+
if (/Shortcuts?\s*\(for more/i.test(trimmed)) continue;
|
|
1438
|
+
if (/shell mode|cycle mode|paste images|select file or folder|reverse-search|clear prompt|rewind|raw markdown|open external editor/i.test(trimmed)) continue;
|
|
1439
|
+
if (/Shift\+Tab|Option\+M|Ctrl\+X|Ctrl\+R|Ctrl\+Y/i.test(trimmed) && /mode|prompt|editor|search/i.test(trimmed)) continue;
|
|
1440
|
+
if (/^[\u276f>]\s*$/.test(trimmed)) continue;
|
|
1441
|
+
if (/^[*\u2022]\s*$/.test(trimmed)) continue;
|
|
1442
|
+
if (lastUserInput && /^[*\u2022]\s/.test(trimmed)) {
|
|
1443
|
+
const bulletContent = trimmed.replace(/^[*\u2022]\s*/, "").trim();
|
|
1444
|
+
if (bulletContent === lastUserInput.trim() || lastUserInput.trim().startsWith(bulletContent.slice(0, 15))) {
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
if (/Gemini CLI update available/i.test(trimmed)) continue;
|
|
1449
|
+
if (/brew upgrade gemini-cli/i.test(trimmed)) continue;
|
|
1450
|
+
if (/Installed via .+ Please update/i.test(trimmed)) continue;
|
|
1451
|
+
if (/Waiting for auth/i.test(trimmed)) continue;
|
|
1452
|
+
if (/ℹ Gemini CLI/i.test(trimmed)) continue;
|
|
1453
|
+
if (/^[\u280b\u2819\u2839\u2838\u283c\u2834\u2826\u2827\u2807\u280f\s]+$/.test(trimmed)) continue;
|
|
1454
|
+
if (trimmed.length <= 2 && /^[!@#$%^&*()_+=\-<>?/\\|~`]+$/.test(trimmed)) continue;
|
|
1455
|
+
cleaned.push(line);
|
|
1456
|
+
}
|
|
1457
|
+
return cleaned.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
1458
|
+
}
|
|
936
1459
|
function findGeminiBinary() {
|
|
937
1460
|
const isWin = os3.platform() === "win32";
|
|
938
1461
|
const cmd = isWin ? "where gemini" : "which gemini";
|
|
@@ -983,12 +1506,20 @@ var init_gemini_cli = __esm({
|
|
|
983
1506
|
ready = false;
|
|
984
1507
|
startupBuffer = "";
|
|
985
1508
|
startupDialogDismissed = false;
|
|
1509
|
+
// Raw PTY I/O (터미널 뷰용)
|
|
1510
|
+
onPtyData = null;
|
|
1511
|
+
ptyOutputBuffer = "";
|
|
1512
|
+
ptyOutputFlushTimer = null;
|
|
986
1513
|
constructor(workingDir) {
|
|
987
1514
|
this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os3.homedir()) : workingDir;
|
|
988
1515
|
}
|
|
989
1516
|
setOnStatusChange(callback) {
|
|
990
1517
|
this.onStatusChange = callback;
|
|
991
1518
|
}
|
|
1519
|
+
/** PTY raw data 콜백 설정 (터미널 뷰 스트리밍용) */
|
|
1520
|
+
setOnPtyData(callback) {
|
|
1521
|
+
this.onPtyData = callback;
|
|
1522
|
+
}
|
|
992
1523
|
/**
|
|
993
1524
|
* PTY로 gemini 프로세스 시작 (인터랙티브 모드)
|
|
994
1525
|
*/
|
|
@@ -1000,7 +1531,7 @@ var init_gemini_cli = __esm({
|
|
|
1000
1531
|
console.log(`[GeminiAdapter] Spawning interactive session in ${this.workingDir}`);
|
|
1001
1532
|
console.log(`[GeminiAdapter] Binary: ${geminiBin}`);
|
|
1002
1533
|
const shell = isWin ? "cmd.exe" : process.env.SHELL || "/bin/zsh";
|
|
1003
|
-
const shellArgs = isWin ? ["/c", `${geminiBin} --yolo`] : ["-l", "-c",
|
|
1534
|
+
const shellArgs = isWin ? ["/c", `${geminiBin} --yolo`] : ["-l", "-c", `${geminiBin} --yolo`];
|
|
1004
1535
|
console.log(`[GeminiAdapter] Shell: ${shell} ${shellArgs.join(" ")}`);
|
|
1005
1536
|
this.ptyProcess = pty.spawn(shell, shellArgs, {
|
|
1006
1537
|
name: "xterm-256color",
|
|
@@ -1013,6 +1544,18 @@ var init_gemini_cli = __esm({
|
|
|
1013
1544
|
});
|
|
1014
1545
|
this.ptyProcess.onData((data) => {
|
|
1015
1546
|
this.handleOutput(data);
|
|
1547
|
+
if (this.onPtyData) {
|
|
1548
|
+
this.ptyOutputBuffer += data;
|
|
1549
|
+
if (!this.ptyOutputFlushTimer) {
|
|
1550
|
+
this.ptyOutputFlushTimer = setTimeout(() => {
|
|
1551
|
+
if (this.ptyOutputBuffer && this.onPtyData) {
|
|
1552
|
+
this.onPtyData(this.ptyOutputBuffer);
|
|
1553
|
+
}
|
|
1554
|
+
this.ptyOutputBuffer = "";
|
|
1555
|
+
this.ptyOutputFlushTimer = null;
|
|
1556
|
+
}, 50);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1016
1559
|
});
|
|
1017
1560
|
this.ptyProcess.onExit(({ exitCode }) => {
|
|
1018
1561
|
console.log(`[GeminiAdapter] Process exited with code ${exitCode}`);
|
|
@@ -1091,6 +1634,8 @@ var init_gemini_cli = __esm({
|
|
|
1091
1634
|
}
|
|
1092
1635
|
}
|
|
1093
1636
|
response = lines.join("\n").trim();
|
|
1637
|
+
const lastUserText = this.messages.length > 0 && this.messages[this.messages.length - 1].role === "user" ? this.messages[this.messages.length - 1].content : void 0;
|
|
1638
|
+
response = cleanGeminiResponse(response, lastUserText);
|
|
1094
1639
|
if (response) {
|
|
1095
1640
|
this.messages.push({
|
|
1096
1641
|
role: "assistant",
|
|
@@ -1185,6 +1730,21 @@ var init_gemini_cli = __esm({
|
|
|
1185
1730
|
isReady() {
|
|
1186
1731
|
return this.ready;
|
|
1187
1732
|
}
|
|
1733
|
+
/** Raw 키 입력을 PTY에 직접 전달 (터미널 뷰용) */
|
|
1734
|
+
writeRaw(data) {
|
|
1735
|
+
if (this.ptyProcess) {
|
|
1736
|
+
this.ptyProcess.write(data);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
/** PTY 터미널 사이즈 변경 */
|
|
1740
|
+
resize(cols, rows) {
|
|
1741
|
+
if (this.ptyProcess) {
|
|
1742
|
+
try {
|
|
1743
|
+
this.ptyProcess.resize(cols, rows);
|
|
1744
|
+
} catch {
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1188
1748
|
/** 작업 디렉토리 변경 (세션 재시작) */
|
|
1189
1749
|
async changeWorkingDir(newDir) {
|
|
1190
1750
|
this.cancel();
|
|
@@ -1239,6 +1799,7 @@ var init_claude_cli = __esm({
|
|
|
1239
1799
|
startupBuffer = "";
|
|
1240
1800
|
bridge = null;
|
|
1241
1801
|
logBuffer = [];
|
|
1802
|
+
onPtyDataCallback = null;
|
|
1242
1803
|
// Patterns for Claude Code
|
|
1243
1804
|
PROMPT_PATTERNS = [
|
|
1244
1805
|
/❯\s*$/,
|
|
@@ -1282,6 +1843,7 @@ var init_claude_cli = __esm({
|
|
|
1282
1843
|
env: { ...process.env }
|
|
1283
1844
|
});
|
|
1284
1845
|
this.ptyProcess.onData((data) => {
|
|
1846
|
+
this.onPtyDataCallback?.(data);
|
|
1285
1847
|
this.handleOutput(data);
|
|
1286
1848
|
});
|
|
1287
1849
|
this.ptyProcess.onExit(({ exitCode }) => {
|
|
@@ -1407,6 +1969,25 @@ var init_claude_cli = __esm({
|
|
|
1407
1969
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
1408
1970
|
await this.spawn();
|
|
1409
1971
|
}
|
|
1972
|
+
/** PTY raw data 콜백 설정 (터미널 뷰 스트리밍용) */
|
|
1973
|
+
setOnPtyData(callback) {
|
|
1974
|
+
this.onPtyDataCallback = callback;
|
|
1975
|
+
}
|
|
1976
|
+
/** Raw 키 입력을 PTY에 직접 전달 (터미널 뷰용) */
|
|
1977
|
+
writeRaw(data) {
|
|
1978
|
+
if (this.ptyProcess) {
|
|
1979
|
+
this.ptyProcess.write(data);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
/** PTY 터미널 사이즈 변경 */
|
|
1983
|
+
resize(cols, rows) {
|
|
1984
|
+
if (this.ptyProcess) {
|
|
1985
|
+
try {
|
|
1986
|
+
this.ptyProcess.resize(cols, rows);
|
|
1987
|
+
} catch {
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1410
1991
|
};
|
|
1411
1992
|
}
|
|
1412
1993
|
});
|
|
@@ -1924,17 +2505,16 @@ var init_local_server = __esm({
|
|
|
1924
2505
|
});
|
|
1925
2506
|
|
|
1926
2507
|
// src/daemon-cdp.ts
|
|
1927
|
-
var import_ws3, http,
|
|
2508
|
+
var import_ws3, http, KNOWN_AGENTS, DaemonCdpManager;
|
|
1928
2509
|
var init_daemon_cdp = __esm({
|
|
1929
2510
|
"src/daemon-cdp.ts"() {
|
|
1930
2511
|
"use strict";
|
|
1931
2512
|
import_ws3 = __toESM(require("ws"));
|
|
1932
2513
|
http = __toESM(require("http"));
|
|
1933
|
-
fs = __toESM(require("fs"));
|
|
1934
2514
|
KNOWN_AGENTS = [
|
|
1935
|
-
{ agentType: "cline", extensionId: "saoudrizwan.claude-dev", extensionIdPattern: /saoudrizwan\.claude-dev/ },
|
|
1936
|
-
{ agentType: "roo-code", extensionId: "rooveterinaryinc.roo-cline", extensionIdPattern: /rooveterinaryinc\.roo-cline/ },
|
|
1937
|
-
{ agentType: "continue", extensionId: "continue.continue", extensionIdPattern: /continue\.continue/ }
|
|
2515
|
+
{ agentType: "cline", extensionId: "saoudrizwan.claude-dev", extensionIdPattern: /saoudrizwan\.claude-dev/i },
|
|
2516
|
+
{ agentType: "roo-code", extensionId: "rooveterinaryinc.roo-cline", extensionIdPattern: /rooveterinaryinc\.roo-cline/i },
|
|
2517
|
+
{ agentType: "continue", extensionId: "continue.continue", extensionIdPattern: /continue\.continue/i }
|
|
1938
2518
|
];
|
|
1939
2519
|
DaemonCdpManager = class {
|
|
1940
2520
|
ws = null;
|
|
@@ -1961,10 +2541,6 @@ var init_daemon_cdp = __esm({
|
|
|
1961
2541
|
this.disableFallback = disableFallback;
|
|
1962
2542
|
this.logFn = logFn || ((msg) => {
|
|
1963
2543
|
console.log(msg);
|
|
1964
|
-
try {
|
|
1965
|
-
fs.appendFileSync("/tmp/adhdev_daemon_cdp.log", msg + "\n");
|
|
1966
|
-
} catch {
|
|
1967
|
-
}
|
|
1968
2544
|
});
|
|
1969
2545
|
}
|
|
1970
2546
|
setPort(port) {
|
|
@@ -2273,6 +2849,62 @@ var init_daemon_cdp = __esm({
|
|
|
2273
2849
|
})()
|
|
2274
2850
|
`);
|
|
2275
2851
|
}
|
|
2852
|
+
/**
|
|
2853
|
+
* CDP 프로토콜로 텍스트 입력 후 Enter 전송
|
|
2854
|
+
* Lexical 등 execCommand가 작동하지 않는 에디터에서 사용.
|
|
2855
|
+
*
|
|
2856
|
+
* 1. selector로 에디터 찾아서 focus + click
|
|
2857
|
+
* 2. Input.insertText로 텍스트 삽입
|
|
2858
|
+
* 3. Input.dispatchKeyEvent로 Enter 전송
|
|
2859
|
+
*/
|
|
2860
|
+
async typeAndSend(selector, text) {
|
|
2861
|
+
if (!this.isConnected) return false;
|
|
2862
|
+
const focusResult = await this.evaluate(`(() => {
|
|
2863
|
+
const e = document.querySelector(${JSON.stringify(selector)});
|
|
2864
|
+
if (!e) return null;
|
|
2865
|
+
e.focus();
|
|
2866
|
+
const r = e.getBoundingClientRect();
|
|
2867
|
+
return JSON.stringify({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
|
|
2868
|
+
})()`);
|
|
2869
|
+
if (!focusResult) {
|
|
2870
|
+
this.log("[CDP] typeAndSend: selector not found");
|
|
2871
|
+
return false;
|
|
2872
|
+
}
|
|
2873
|
+
const pos = JSON.parse(focusResult);
|
|
2874
|
+
await this.sendInternal("Input.dispatchMouseEvent", {
|
|
2875
|
+
type: "mousePressed",
|
|
2876
|
+
x: Math.round(pos.x),
|
|
2877
|
+
y: Math.round(pos.y),
|
|
2878
|
+
button: "left",
|
|
2879
|
+
clickCount: 1
|
|
2880
|
+
});
|
|
2881
|
+
await this.sendInternal("Input.dispatchMouseEvent", {
|
|
2882
|
+
type: "mouseReleased",
|
|
2883
|
+
x: Math.round(pos.x),
|
|
2884
|
+
y: Math.round(pos.y),
|
|
2885
|
+
button: "left",
|
|
2886
|
+
clickCount: 1
|
|
2887
|
+
});
|
|
2888
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
2889
|
+
await this.sendInternal("Input.insertText", { text });
|
|
2890
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
2891
|
+
await this.sendInternal("Input.dispatchKeyEvent", {
|
|
2892
|
+
type: "rawKeyDown",
|
|
2893
|
+
key: "Enter",
|
|
2894
|
+
code: "Enter",
|
|
2895
|
+
windowsVirtualKeyCode: 13,
|
|
2896
|
+
nativeVirtualKeyCode: 13
|
|
2897
|
+
});
|
|
2898
|
+
await this.sendInternal("Input.dispatchKeyEvent", {
|
|
2899
|
+
type: "keyUp",
|
|
2900
|
+
key: "Enter",
|
|
2901
|
+
code: "Enter",
|
|
2902
|
+
windowsVirtualKeyCode: 13,
|
|
2903
|
+
nativeVirtualKeyCode: 13
|
|
2904
|
+
});
|
|
2905
|
+
this.log(`[CDP] typeAndSend: sent "${text.substring(0, 50)}..."`);
|
|
2906
|
+
return true;
|
|
2907
|
+
}
|
|
2276
2908
|
// ─── Agent Webview Multi-Session ─────────────────────────
|
|
2277
2909
|
async discoverAgentWebviews() {
|
|
2278
2910
|
if (!this.isConnected) return [];
|
|
@@ -2705,21 +3337,17 @@ var require_lib = __commonJS({
|
|
|
2705
3337
|
});
|
|
2706
3338
|
|
|
2707
3339
|
// src/daemon-p2p.ts
|
|
2708
|
-
var
|
|
3340
|
+
var fs, path2, os6, logFile, log, DaemonP2PSender;
|
|
2709
3341
|
var init_daemon_p2p = __esm({
|
|
2710
3342
|
"src/daemon-p2p.ts"() {
|
|
2711
3343
|
"use strict";
|
|
2712
|
-
|
|
3344
|
+
fs = __toESM(require("fs"));
|
|
2713
3345
|
path2 = __toESM(require("path"));
|
|
2714
3346
|
os6 = __toESM(require("os"));
|
|
2715
3347
|
logFile = path2.join(os6.tmpdir(), "adhdev_daemon_p2p.log");
|
|
2716
3348
|
log = (msg) => {
|
|
2717
3349
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [P2P] ${msg}`;
|
|
2718
3350
|
console.log(line);
|
|
2719
|
-
try {
|
|
2720
|
-
fs2.appendFileSync(logFile, line + "\n");
|
|
2721
|
-
} catch {
|
|
2722
|
-
}
|
|
2723
3351
|
};
|
|
2724
3352
|
DaemonP2PSender = class {
|
|
2725
3353
|
bridge;
|
|
@@ -2729,7 +3357,13 @@ var init_daemon_p2p = __esm({
|
|
|
2729
3357
|
fileRequestHandler = null;
|
|
2730
3358
|
inputHandler = null;
|
|
2731
3359
|
commandHandler = null;
|
|
3360
|
+
ptyInputHandler = null;
|
|
3361
|
+
ptyResizeHandler = null;
|
|
2732
3362
|
_ssDebugDone = false;
|
|
3363
|
+
// PTY scrollback buffer per cliType (재연결 시 최근 출력 전송)
|
|
3364
|
+
ptyScrollback = /* @__PURE__ */ new Map();
|
|
3365
|
+
PTY_SCROLLBACK_MAX = 64 * 1024;
|
|
3366
|
+
// 64KB per CLI
|
|
2733
3367
|
get screenshotActive() {
|
|
2734
3368
|
for (const peer of this.peers.values()) {
|
|
2735
3369
|
if (peer.screenshotActive && peer.state === "connected") return true;
|
|
@@ -2770,11 +3404,11 @@ var init_daemon_p2p = __esm({
|
|
|
2770
3404
|
];
|
|
2771
3405
|
for (const candidate of candidates) {
|
|
2772
3406
|
const prebuildPath = path2.join(candidate, "prebuilds", prebuildKey, "node_datachannel.node");
|
|
2773
|
-
if (
|
|
3407
|
+
if (fs.existsSync(prebuildPath)) {
|
|
2774
3408
|
const targetDir = path2.join(candidate, "build", "Release");
|
|
2775
3409
|
const targetPath = path2.join(targetDir, "node_datachannel.node");
|
|
2776
|
-
|
|
2777
|
-
|
|
3410
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
3411
|
+
fs.copyFileSync(prebuildPath, targetPath);
|
|
2778
3412
|
try {
|
|
2779
3413
|
delete require.cache[require.resolve("node-datachannel")];
|
|
2780
3414
|
} catch {
|
|
@@ -2835,7 +3469,7 @@ var init_daemon_p2p = __esm({
|
|
|
2835
3469
|
const configPath = path2.join(os6.homedir(), ".adhdev", "config.json");
|
|
2836
3470
|
let token = "";
|
|
2837
3471
|
try {
|
|
2838
|
-
const config = JSON.parse(
|
|
3472
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
2839
3473
|
token = config.connectionToken || "";
|
|
2840
3474
|
} catch {
|
|
2841
3475
|
}
|
|
@@ -2973,7 +3607,10 @@ var init_daemon_p2p = __esm({
|
|
|
2973
3607
|
screenshotCh.onError((err) => log(`Screenshots error: ${err}`));
|
|
2974
3608
|
const filesCh = pc.createDataChannel("files");
|
|
2975
3609
|
entry.filesChannel = filesCh;
|
|
2976
|
-
filesCh.onOpen(() =>
|
|
3610
|
+
filesCh.onOpen(() => {
|
|
3611
|
+
log(`Files channel OPEN for peer ${pid}`);
|
|
3612
|
+
setTimeout(() => this.sendPtyScrollback(pid), 100);
|
|
3613
|
+
});
|
|
2977
3614
|
filesCh.onClosed(() => {
|
|
2978
3615
|
const peer = this.peers.get(pid);
|
|
2979
3616
|
if (peer?.filesChannel === filesCh) peer.filesChannel = null;
|
|
@@ -3021,6 +3658,18 @@ var init_daemon_p2p = __esm({
|
|
|
3021
3658
|
this.handleP2PCommand(peerId, parsed);
|
|
3022
3659
|
return;
|
|
3023
3660
|
}
|
|
3661
|
+
if (parsed.type === "pty_input") {
|
|
3662
|
+
if (this.ptyInputHandler && parsed.data) {
|
|
3663
|
+
this.ptyInputHandler(parsed.cliType || "gemini-cli", parsed.data);
|
|
3664
|
+
}
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
if (parsed.type === "pty_resize") {
|
|
3668
|
+
if (this.ptyResizeHandler && parsed.cols && parsed.rows) {
|
|
3669
|
+
this.ptyResizeHandler(parsed.cliType || "gemini-cli", parsed.cols, parsed.rows);
|
|
3670
|
+
}
|
|
3671
|
+
return;
|
|
3672
|
+
}
|
|
3024
3673
|
this.handleFileRequest(peerId, parsed);
|
|
3025
3674
|
} catch (e) {
|
|
3026
3675
|
log(`Parse error from peer ${peerId}: ${e?.message}`);
|
|
@@ -3044,6 +3693,44 @@ var init_daemon_p2p = __esm({
|
|
|
3044
3693
|
}
|
|
3045
3694
|
return sentAny;
|
|
3046
3695
|
}
|
|
3696
|
+
/** PTY 출력을 모든 connected peer에게 브로드캐스트 */
|
|
3697
|
+
broadcastPtyOutput(cliType, data) {
|
|
3698
|
+
const prev = this.ptyScrollback.get(cliType) || "";
|
|
3699
|
+
const updated = prev + data;
|
|
3700
|
+
this.ptyScrollback.set(
|
|
3701
|
+
cliType,
|
|
3702
|
+
updated.length > this.PTY_SCROLLBACK_MAX ? updated.slice(-this.PTY_SCROLLBACK_MAX) : updated
|
|
3703
|
+
);
|
|
3704
|
+
const msg = JSON.stringify({ type: "pty_output", cliType, data });
|
|
3705
|
+
let sentAny = false;
|
|
3706
|
+
for (const peer of this.peers.values()) {
|
|
3707
|
+
if (peer.state !== "connected" || !peer.filesChannel) continue;
|
|
3708
|
+
try {
|
|
3709
|
+
peer.filesChannel.sendMessage(msg);
|
|
3710
|
+
sentAny = true;
|
|
3711
|
+
} catch {
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
return sentAny;
|
|
3715
|
+
}
|
|
3716
|
+
/** Peer 연결 시 scrollback 전송 */
|
|
3717
|
+
sendPtyScrollback(peerId) {
|
|
3718
|
+
const peer = this.peers.get(peerId);
|
|
3719
|
+
if (!peer?.filesChannel) return;
|
|
3720
|
+
for (const [cliType, buffer] of this.ptyScrollback) {
|
|
3721
|
+
if (!buffer) continue;
|
|
3722
|
+
try {
|
|
3723
|
+
peer.filesChannel.sendMessage(JSON.stringify({
|
|
3724
|
+
type: "pty_output",
|
|
3725
|
+
cliType,
|
|
3726
|
+
data: buffer,
|
|
3727
|
+
scrollback: true
|
|
3728
|
+
}));
|
|
3729
|
+
log(`Sent PTY scrollback to peer ${peerId}: ${cliType} (${buffer.length} bytes)`);
|
|
3730
|
+
} catch {
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3047
3734
|
sendScreenshot(base64Data) {
|
|
3048
3735
|
let sentAny = false;
|
|
3049
3736
|
const buffer = Buffer.from(base64Data, "base64");
|
|
@@ -3081,6 +3768,12 @@ var init_daemon_p2p = __esm({
|
|
|
3081
3768
|
onCommand(handler) {
|
|
3082
3769
|
this.commandHandler = handler;
|
|
3083
3770
|
}
|
|
3771
|
+
onPtyInput(handler) {
|
|
3772
|
+
this.ptyInputHandler = handler;
|
|
3773
|
+
}
|
|
3774
|
+
onPtyResize(handler) {
|
|
3775
|
+
this.ptyResizeHandler = handler;
|
|
3776
|
+
}
|
|
3084
3777
|
// ─── P2P 명령/입력/파일 처리 ────────────────
|
|
3085
3778
|
async handleP2PCommand(peerId, msg) {
|
|
3086
3779
|
const { id, commandType, data } = msg;
|
|
@@ -3189,17 +3882,12 @@ var init_daemon_p2p = __esm({
|
|
|
3189
3882
|
|
|
3190
3883
|
// src/daemon-script-loader.ts
|
|
3191
3884
|
function log2(msg) {
|
|
3192
|
-
|
|
3193
|
-
fs3.appendFileSync(`${require("os").tmpdir()}/adhdev_daemon_scripts.log`, `${msg}
|
|
3194
|
-
`);
|
|
3195
|
-
} catch {
|
|
3196
|
-
}
|
|
3885
|
+
console.log(msg);
|
|
3197
3886
|
}
|
|
3198
|
-
var
|
|
3887
|
+
var http2, https, DaemonScriptLoader;
|
|
3199
3888
|
var init_daemon_script_loader = __esm({
|
|
3200
3889
|
"src/daemon-script-loader.ts"() {
|
|
3201
3890
|
"use strict";
|
|
3202
|
-
fs3 = __toESM(require("fs"));
|
|
3203
3891
|
http2 = __toESM(require("http"));
|
|
3204
3892
|
https = __toESM(require("https"));
|
|
3205
3893
|
DaemonScriptLoader = class {
|
|
@@ -3209,9 +3897,13 @@ var init_daemon_script_loader = __esm({
|
|
|
3209
3897
|
// name → code
|
|
3210
3898
|
fallbacks = /* @__PURE__ */ new Map();
|
|
3211
3899
|
// name → bundled fallback code
|
|
3900
|
+
capabilityMap = /* @__PURE__ */ new Map();
|
|
3901
|
+
// capability → { script, params }
|
|
3212
3902
|
manifestVersion = "0";
|
|
3213
3903
|
refreshTimer = null;
|
|
3214
3904
|
token = "";
|
|
3905
|
+
platform = "";
|
|
3906
|
+
// darwin, win32, linux
|
|
3215
3907
|
constructor(baseUrl, ideType) {
|
|
3216
3908
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
3217
3909
|
this.ideType = ideType;
|
|
@@ -3219,6 +3911,9 @@ var init_daemon_script_loader = __esm({
|
|
|
3219
3911
|
setToken(token) {
|
|
3220
3912
|
this.token = token;
|
|
3221
3913
|
}
|
|
3914
|
+
setPlatform(platform9) {
|
|
3915
|
+
this.platform = platform9;
|
|
3916
|
+
}
|
|
3222
3917
|
registerFallback(name, code) {
|
|
3223
3918
|
this.fallbacks.set(name, code);
|
|
3224
3919
|
}
|
|
@@ -3235,6 +3930,46 @@ var init_daemon_script_loader = __esm({
|
|
|
3235
3930
|
has(name) {
|
|
3236
3931
|
return this.cache.has(name) || this.fallbacks.has(name);
|
|
3237
3932
|
}
|
|
3933
|
+
/** Resolve a logical capability to its IDE-specific script */
|
|
3934
|
+
getCapability(capability) {
|
|
3935
|
+
const entry = this.capabilityMap.get(capability);
|
|
3936
|
+
if (entry?.script) {
|
|
3937
|
+
return this.get(entry.script);
|
|
3938
|
+
}
|
|
3939
|
+
return this.get(capability);
|
|
3940
|
+
}
|
|
3941
|
+
/** Resolve capability with template params */
|
|
3942
|
+
getCapabilityWithParams(capability, params) {
|
|
3943
|
+
const entry = this.capabilityMap.get(capability);
|
|
3944
|
+
if (entry?.script) {
|
|
3945
|
+
return this.getWithParams(entry.script, params);
|
|
3946
|
+
}
|
|
3947
|
+
return this.getWithParams(capability, params);
|
|
3948
|
+
}
|
|
3949
|
+
/** Check if a capability is supported (implemented) */
|
|
3950
|
+
hasCapability(capability) {
|
|
3951
|
+
const entry = this.capabilityMap.get(capability);
|
|
3952
|
+
if (!entry) return this.has(capability);
|
|
3953
|
+
if (entry.status === "not_implemented" || entry.status === "not_needed") return false;
|
|
3954
|
+
return !!entry.script && this.has(entry.script);
|
|
3955
|
+
}
|
|
3956
|
+
/** Get the capability map for status reporting */
|
|
3957
|
+
getCapabilities() {
|
|
3958
|
+
const result = {};
|
|
3959
|
+
for (const [k, v] of this.capabilityMap) result[k] = v;
|
|
3960
|
+
return result;
|
|
3961
|
+
}
|
|
3962
|
+
/** Manually set capability map (for testing or static config) */
|
|
3963
|
+
setCapabilityMap(map) {
|
|
3964
|
+
this.capabilityMap.clear();
|
|
3965
|
+
for (const [cap, entry] of Object.entries(map)) {
|
|
3966
|
+
if (entry && typeof entry === "object" && "script" in entry) {
|
|
3967
|
+
this.capabilityMap.set(cap, entry);
|
|
3968
|
+
} else if (entry && typeof entry === "object" && "status" in entry) {
|
|
3969
|
+
this.capabilityMap.set(cap, entry);
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3238
3973
|
async start() {
|
|
3239
3974
|
log2(`[DaemonScriptLoader] Starting: baseUrl=${this.baseUrl} ide=${this.ideType}`);
|
|
3240
3975
|
await this.refresh();
|
|
@@ -3315,6 +4050,22 @@ var init_daemon_script_loader = __esm({
|
|
|
3315
4050
|
log2(`[DaemonScriptLoader] Downloading ${scriptNames.length} scripts for ${this.ideType}/${version}`);
|
|
3316
4051
|
const downloads = scriptNames.map((name) => this.downloadScript(version, name));
|
|
3317
4052
|
await Promise.allSettled(downloads);
|
|
4053
|
+
const capabilities = manifest.capabilities?.[this.ideType]?.[version];
|
|
4054
|
+
if (capabilities && typeof capabilities === "object") {
|
|
4055
|
+
this.capabilityMap.clear();
|
|
4056
|
+
for (const [cap, entry] of Object.entries(capabilities)) {
|
|
4057
|
+
if (entry && typeof entry === "object") {
|
|
4058
|
+
const osFilter = entry.os;
|
|
4059
|
+
if (osFilter && Array.isArray(osFilter) && this.platform && !osFilter.includes(this.platform)) {
|
|
4060
|
+
continue;
|
|
4061
|
+
}
|
|
4062
|
+
this.capabilityMap.set(cap, entry);
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
log2(`[DaemonScriptLoader] Loaded ${this.capabilityMap.size} capabilities for ${this.ideType}/${version}`);
|
|
4066
|
+
} else {
|
|
4067
|
+
this.buildDefaultCapabilityMap(scriptNames);
|
|
4068
|
+
}
|
|
3318
4069
|
log2(`[DaemonScriptLoader] Loaded ${this.cache.size} scripts`);
|
|
3319
4070
|
} catch (e) {
|
|
3320
4071
|
log2(`[DaemonScriptLoader] Refresh error: ${e?.message}`);
|
|
@@ -3338,28 +4089,47 @@ var init_daemon_script_loader = __esm({
|
|
|
3338
4089
|
log2(`[DaemonScriptLoader] Download error ${name}: ${e?.message}`);
|
|
3339
4090
|
}
|
|
3340
4091
|
}
|
|
4092
|
+
/** Build a default capability map from script names when no explicit map exists */
|
|
4093
|
+
buildDefaultCapabilityMap(scriptNames) {
|
|
4094
|
+
this.capabilityMap.clear();
|
|
4095
|
+
for (const name of scriptNames) {
|
|
4096
|
+
this.capabilityMap.set(name, { script: name });
|
|
4097
|
+
}
|
|
4098
|
+
if (scriptNames.includes("resolve_modal")) {
|
|
4099
|
+
this.capabilityMap.set("resolve", { script: "resolve_modal", params: ["BUTTON_TEXT"] });
|
|
4100
|
+
} else if (scriptNames.includes("resolve_action")) {
|
|
4101
|
+
this.capabilityMap.set("resolve", { script: "resolve_action", params: ["ACTION"] });
|
|
4102
|
+
}
|
|
4103
|
+
log2(`[DaemonScriptLoader] Built default capability map: ${this.capabilityMap.size} entries`);
|
|
4104
|
+
}
|
|
3341
4105
|
};
|
|
3342
4106
|
}
|
|
3343
4107
|
});
|
|
3344
4108
|
|
|
3345
4109
|
// src/daemon-commands.ts
|
|
3346
|
-
var
|
|
4110
|
+
var fs2, path3, os7, DaemonCommandHandler;
|
|
3347
4111
|
var init_daemon_commands = __esm({
|
|
3348
4112
|
"src/daemon-commands.ts"() {
|
|
3349
4113
|
"use strict";
|
|
3350
|
-
|
|
4114
|
+
fs2 = __toESM(require("fs"));
|
|
3351
4115
|
path3 = __toESM(require("path"));
|
|
3352
4116
|
os7 = __toESM(require("os"));
|
|
3353
4117
|
init_config();
|
|
3354
4118
|
DaemonCommandHandler = class {
|
|
3355
4119
|
ctx;
|
|
3356
4120
|
agentStream = null;
|
|
3357
|
-
/** Get CDP manager for a specific ideType
|
|
4121
|
+
/** Get CDP manager for a specific ideType.
|
|
4122
|
+
* When ideType is explicitly specified (via arg or _currentIdeType),
|
|
4123
|
+
* returns null if that IDE's CDP is unavailable — never falls back to another IDE.
|
|
4124
|
+
* Fallback to first connected manager only when no ideType is known at all.
|
|
4125
|
+
*/
|
|
3358
4126
|
getCdp(ideType) {
|
|
3359
4127
|
const key = ideType || this._currentIdeType;
|
|
3360
4128
|
if (key) {
|
|
3361
4129
|
const m = this.ctx.cdpManagers.get(key.toLowerCase());
|
|
3362
4130
|
if (m?.isConnected) return m;
|
|
4131
|
+
console.log(`[getCdp] \u26A0 Target IDE "${key}" CDP not available, refusing fallback`);
|
|
4132
|
+
return null;
|
|
3363
4133
|
}
|
|
3364
4134
|
for (const m of this.ctx.cdpManagers.values()) {
|
|
3365
4135
|
if (m.isConnected) return m;
|
|
@@ -3368,10 +4138,17 @@ var init_daemon_commands = __esm({
|
|
|
3368
4138
|
}
|
|
3369
4139
|
/** Current IDE type extracted from command args (per-request) */
|
|
3370
4140
|
_currentIdeType;
|
|
3371
|
-
/** Extract ideType from _targetInstance
|
|
4141
|
+
/** Extract ideType from _targetInstance
|
|
4142
|
+
* Handles both simple ('vscode_abc123') and composite ('doId:ide:vscode_abc123') formats.
|
|
4143
|
+
*/
|
|
3372
4144
|
extractIdeType(args) {
|
|
3373
4145
|
if (args?._targetInstance) {
|
|
3374
|
-
|
|
4146
|
+
let raw = args._targetInstance;
|
|
4147
|
+
const ideMatch = raw.match(/:ide:(.+)$/);
|
|
4148
|
+
const cliMatch = raw.match(/:cli:(.+)$/);
|
|
4149
|
+
if (ideMatch) raw = ideMatch[1];
|
|
4150
|
+
else if (cliMatch) raw = cliMatch[1];
|
|
4151
|
+
const parts = raw.split("_");
|
|
3375
4152
|
if (parts.length >= 2) return parts[0];
|
|
3376
4153
|
}
|
|
3377
4154
|
return void 0;
|
|
@@ -3394,6 +4171,7 @@ var init_daemon_commands = __esm({
|
|
|
3394
4171
|
}
|
|
3395
4172
|
async handle(cmd, args) {
|
|
3396
4173
|
this._currentIdeType = this.extractIdeType(args);
|
|
4174
|
+
console.log(`[Cmd] ${cmd} \u2192 ideType=${this._currentIdeType || "none"} _targetInstance=${args?._targetInstance || "none"}`);
|
|
3397
4175
|
switch (cmd) {
|
|
3398
4176
|
// ─── CDP 직접 처리 ───────────────────
|
|
3399
4177
|
case "read_chat":
|
|
@@ -3425,6 +4203,12 @@ var init_daemon_commands = __esm({
|
|
|
3425
4203
|
return this.handleCdpRemoteAction(args);
|
|
3426
4204
|
case "cdp_discover_agents":
|
|
3427
4205
|
return this.handleDiscoverAgents(args);
|
|
4206
|
+
case "cdp_dom_dump":
|
|
4207
|
+
return this.handleCdpDomDump(args);
|
|
4208
|
+
case "cdp_dom_query":
|
|
4209
|
+
return this.handleCdpDomQuery(args);
|
|
4210
|
+
case "cdp_dom_debug":
|
|
4211
|
+
return this.handleCdpDomDebug(args);
|
|
3428
4212
|
// ─── 파일 직접 처리 ──────────────────
|
|
3429
4213
|
case "file_read":
|
|
3430
4214
|
return this.handleFileRead(args);
|
|
@@ -3479,35 +4263,77 @@ var init_daemon_commands = __esm({
|
|
|
3479
4263
|
return this.handleAgentStreamSwitchSession(args);
|
|
3480
4264
|
case "agent_stream_focus":
|
|
3481
4265
|
return this.handleAgentStreamFocus(args);
|
|
4266
|
+
// ─── PTY Raw I/O (터미널 뷰) ─────────
|
|
4267
|
+
case "pty_input":
|
|
4268
|
+
return this.handlePtyInput(args);
|
|
4269
|
+
case "pty_resize":
|
|
4270
|
+
return this.handlePtyResize(args);
|
|
3482
4271
|
default:
|
|
3483
4272
|
return { success: false, error: `Unknown command: ${cmd}` };
|
|
3484
4273
|
}
|
|
3485
4274
|
}
|
|
3486
4275
|
// ─── CDP 기반 채팅 명령 ──────────────────────
|
|
3487
4276
|
async handleReadChat(args) {
|
|
3488
|
-
|
|
4277
|
+
const cdp2 = this.getCdp();
|
|
4278
|
+
if (!cdp2?.isConnected) return { success: false, error: "CDP not connected" };
|
|
3489
4279
|
const script = this.getScriptLoader()?.get("read_chat");
|
|
3490
|
-
if (
|
|
4280
|
+
if (script) {
|
|
4281
|
+
try {
|
|
4282
|
+
const result = await cdp2.evaluate(script, 5e4);
|
|
4283
|
+
if (result && typeof result === "object" && result.messages?.length > 0) {
|
|
4284
|
+
console.log(`[read_chat] Server script OK: ${result.messages?.length} msgs`);
|
|
4285
|
+
return { success: true, ...result };
|
|
4286
|
+
}
|
|
4287
|
+
if (result && typeof result === "object") {
|
|
4288
|
+
console.log(`[read_chat] Server script returned ${result.messages?.length || 0} msgs, trying inline`);
|
|
4289
|
+
}
|
|
4290
|
+
} catch (e) {
|
|
4291
|
+
console.log(`[read_chat] Server script error: ${e.message}`);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
3491
4294
|
try {
|
|
3492
|
-
const
|
|
3493
|
-
|
|
4295
|
+
const inlineScript = `(() => {
|
|
4296
|
+
const container = document.querySelector('.flex.flex-col.gap-2\\\\.5');
|
|
4297
|
+
if (!container) return { messages: [], status: 'idle' };
|
|
4298
|
+
const msgs = [];
|
|
4299
|
+
for (const child of container.children) {
|
|
4300
|
+
if ((child.className || '').includes('mark-js-ignore')) continue;
|
|
4301
|
+
if (child.offsetHeight < 10) continue;
|
|
4302
|
+
const inner = child.children[0];
|
|
4303
|
+
if (!inner) continue;
|
|
4304
|
+
const isUser = inner.className?.includes('flex-row') || !!inner.querySelector('.justify-end');
|
|
4305
|
+
const role = isUser ? 'user' : 'assistant';
|
|
4306
|
+
const text = child.textContent?.trim() || '';
|
|
4307
|
+
if (text.length < 1) continue;
|
|
4308
|
+
msgs.push({ id: 'msg_' + msgs.length, role, content: text.substring(0, 6000), index: msgs.length });
|
|
4309
|
+
}
|
|
4310
|
+
return { messages: msgs, status: 'idle', id: 'cascade' };
|
|
4311
|
+
})()`;
|
|
4312
|
+
const result = await cdp2.evaluate(inlineScript, 1e4);
|
|
4313
|
+
if (result && typeof result === "object") {
|
|
4314
|
+
const msgs = result.messages || [];
|
|
4315
|
+
console.log(`[read_chat] Inline fallback: ${msgs.length} msgs`);
|
|
4316
|
+
return { success: true, ...result };
|
|
4317
|
+
}
|
|
3494
4318
|
} catch (e) {
|
|
3495
|
-
|
|
4319
|
+
console.log(`[read_chat] Inline fallback error: ${e.message}`);
|
|
3496
4320
|
}
|
|
4321
|
+
return { success: true, messages: [], status: "idle" };
|
|
3497
4322
|
}
|
|
3498
4323
|
async handleSendChat(args) {
|
|
3499
4324
|
const text = args?.text || args?.message;
|
|
3500
4325
|
if (!text) return { success: false, error: "text required" };
|
|
4326
|
+
const _log = (msg) => console.log(`[send_chat] ${msg}`);
|
|
3501
4327
|
const targetAgent = args?.agentType || args?.cliType || this._currentIdeType;
|
|
3502
4328
|
if (targetAgent) {
|
|
3503
4329
|
for (const [key, adapter] of this.ctx.adapters.entries()) {
|
|
3504
4330
|
if (adapter.cliType === targetAgent || key.startsWith(targetAgent)) {
|
|
3505
|
-
|
|
4331
|
+
_log(`Routing to CLI adapter: ${adapter.cliType} (${key})`);
|
|
3506
4332
|
try {
|
|
3507
4333
|
await adapter.sendMessage(text);
|
|
3508
4334
|
return { success: true, sent: true, targetAgent: adapter.cliType };
|
|
3509
4335
|
} catch (e) {
|
|
3510
|
-
|
|
4336
|
+
_log(`CLI adapter failed: ${e.message}`);
|
|
3511
4337
|
return { success: false, error: `CLI send failed: ${e.message}` };
|
|
3512
4338
|
}
|
|
3513
4339
|
}
|
|
@@ -3515,49 +4341,86 @@ var init_daemon_commands = __esm({
|
|
|
3515
4341
|
}
|
|
3516
4342
|
const targetCdp = this.getCdp();
|
|
3517
4343
|
const targetLoader = this.getScriptLoader();
|
|
3518
|
-
if (targetCdp?.isConnected
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
console.log(`[send_chat] Failed on ${this._currentIdeType}: ${e.message}`);
|
|
4344
|
+
if (targetCdp?.isConnected) {
|
|
4345
|
+
_log(`Targeting IDE: ${this._currentIdeType || "fallback-first"}`);
|
|
4346
|
+
const inputSelector = '[data-lexical-editor="true"]';
|
|
4347
|
+
try {
|
|
4348
|
+
const sent = await targetCdp.typeAndSend(inputSelector, text);
|
|
4349
|
+
if (sent) {
|
|
4350
|
+
_log(`typeAndSend success`);
|
|
4351
|
+
return { success: true, sent: true, method: "typeAndSend" };
|
|
3527
4352
|
}
|
|
4353
|
+
} catch (e) {
|
|
4354
|
+
_log(`typeAndSend failed: ${e.message}`);
|
|
3528
4355
|
}
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
4356
|
+
const ceSelector = '[contenteditable="true"][role="textbox"]';
|
|
4357
|
+
try {
|
|
4358
|
+
const sent = await targetCdp.typeAndSend(ceSelector, text);
|
|
4359
|
+
if (sent) {
|
|
4360
|
+
_log(`typeAndSend(ce) success`);
|
|
4361
|
+
return { success: true, sent: true, method: "typeAndSend-ce" };
|
|
4362
|
+
}
|
|
4363
|
+
} catch (e) {
|
|
4364
|
+
_log(`typeAndSend(ce) failed: ${e.message}`);
|
|
4365
|
+
}
|
|
4366
|
+
if (targetLoader) {
|
|
4367
|
+
const script = targetLoader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
|
|
4368
|
+
if (script) {
|
|
4369
|
+
try {
|
|
4370
|
+
const result = await targetCdp.evaluate(script, 3e4);
|
|
4371
|
+
_log(`script result: ${JSON.stringify(result)}`);
|
|
4372
|
+
return { success: true, sent: true, method: "script" };
|
|
4373
|
+
} catch (e) {
|
|
4374
|
+
_log(`script failed on ${this._currentIdeType}: ${e.message}`);
|
|
4375
|
+
}
|
|
4376
|
+
}
|
|
4377
|
+
}
|
|
4378
|
+
} else {
|
|
4379
|
+
_log(`No CDP (connected=${targetCdp?.isConnected}) for ${this._currentIdeType}`);
|
|
4380
|
+
}
|
|
4381
|
+
if (this._currentIdeType) {
|
|
4382
|
+
_log(`Target IDE "${this._currentIdeType}" CDP not available \u2014 refusing cross-IDE fallback`);
|
|
4383
|
+
return { success: false, error: `CDP for ${this._currentIdeType} not connected. Ensure the IDE is running with --remote-debugging-port.` };
|
|
4384
|
+
}
|
|
4385
|
+
_log(`No target IDE specified, trying all ${this.ctx.cdpManagers.size} CDPs...`);
|
|
4386
|
+
for (const [ideType, cdp2] of this.ctx.cdpManagers) {
|
|
4387
|
+
if (!cdp2.isConnected) continue;
|
|
4388
|
+
try {
|
|
4389
|
+
const sent = await cdp2.typeAndSend('[data-lexical-editor="true"]', text);
|
|
4390
|
+
if (sent) {
|
|
4391
|
+
_log(`fallback typeAndSend success on ${ideType}`);
|
|
4392
|
+
return { success: true, sent: true, targetIde: ideType, method: "typeAndSend" };
|
|
4393
|
+
}
|
|
4394
|
+
} catch {
|
|
4395
|
+
}
|
|
4396
|
+
const loader = this.ctx.scriptLoaders.get(ideType);
|
|
4397
|
+
if (!loader) continue;
|
|
4398
|
+
const script = loader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
|
|
3536
4399
|
if (!script) continue;
|
|
3537
|
-
|
|
4400
|
+
_log(`Trying script on ${ideType}`);
|
|
3538
4401
|
try {
|
|
3539
|
-
await
|
|
3540
|
-
|
|
4402
|
+
const result = await cdp2.evaluate(script, 3e4);
|
|
4403
|
+
_log(`Success on ${ideType}: ${JSON.stringify(result)}`);
|
|
4404
|
+
return { success: true, sent: true, targetIde: ideType, method: "script" };
|
|
3541
4405
|
} catch (e) {
|
|
3542
|
-
|
|
4406
|
+
_log(`Failed on ${ideType}: ${e.message}`);
|
|
3543
4407
|
}
|
|
3544
4408
|
}
|
|
4409
|
+
_log("All methods failed");
|
|
3545
4410
|
return { success: false, error: "No CDP or CLI adapter could send the message" };
|
|
3546
4411
|
}
|
|
3547
4412
|
async handleListChats(args) {
|
|
3548
|
-
const
|
|
3549
|
-
if (!
|
|
4413
|
+
const cdp2 = this.getCdp();
|
|
4414
|
+
if (!cdp2?.isConnected) {
|
|
3550
4415
|
console.log(`[list_chats] CDP not connected, ideType=${this._currentIdeType}`);
|
|
3551
4416
|
return { success: false, error: "CDP not connected" };
|
|
3552
4417
|
}
|
|
3553
|
-
const
|
|
3554
|
-
if (!
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
}
|
|
4418
|
+
const loader = this.getScriptLoader();
|
|
4419
|
+
if (!loader) return { success: false, error: "Script loader not initialized" };
|
|
4420
|
+
const script = loader.getCapability("list_chats");
|
|
4421
|
+
if (!script) return { success: false, error: "list_chats script not available" };
|
|
3558
4422
|
try {
|
|
3559
|
-
|
|
3560
|
-
const result = await cdp.evaluate(script, 3e4);
|
|
4423
|
+
const result = await cdp2.evaluate(script, 3e4);
|
|
3561
4424
|
let parsed = result;
|
|
3562
4425
|
if (typeof result === "string") {
|
|
3563
4426
|
try {
|
|
@@ -3565,10 +4428,13 @@ var init_daemon_commands = __esm({
|
|
|
3565
4428
|
} catch {
|
|
3566
4429
|
}
|
|
3567
4430
|
}
|
|
3568
|
-
|
|
3569
|
-
|
|
4431
|
+
if (Array.isArray(parsed)) {
|
|
4432
|
+
console.log(`[list_chats] OK: ${parsed.length} chats (ideType=${this._currentIdeType})`);
|
|
4433
|
+
return { success: true, chats: parsed };
|
|
4434
|
+
}
|
|
4435
|
+
return { success: true, chats: [] };
|
|
3570
4436
|
} catch (e) {
|
|
3571
|
-
console.log(`[list_chats]
|
|
4437
|
+
console.log(`[list_chats] error: ${e.message}`);
|
|
3572
4438
|
return { success: false, error: e.message };
|
|
3573
4439
|
}
|
|
3574
4440
|
}
|
|
@@ -3584,15 +4450,22 @@ var init_daemon_commands = __esm({
|
|
|
3584
4450
|
}
|
|
3585
4451
|
}
|
|
3586
4452
|
async handleSwitchChat(args) {
|
|
3587
|
-
|
|
4453
|
+
const ideType = this._currentIdeType;
|
|
4454
|
+
const cdp2 = this.getCdp(ideType);
|
|
4455
|
+
if (!cdp2?.isConnected) return { success: false, error: "CDP not connected" };
|
|
3588
4456
|
const sessionId = args?.sessionId || args?.id || args?.chatId;
|
|
3589
4457
|
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
3590
|
-
|
|
3591
|
-
|
|
4458
|
+
console.log(`[switch_chat] sessionId=${sessionId}, ideType=${ideType}`);
|
|
4459
|
+
const loader = this.getScriptLoader();
|
|
4460
|
+
if (!loader) return { success: false, error: "Script loader not initialized" };
|
|
4461
|
+
const script = loader.getCapabilityWithParams("switch_session", { SESSION_ID: JSON.stringify(sessionId) });
|
|
4462
|
+
if (!script) return { success: false, error: "switch_session script not available" };
|
|
3592
4463
|
try {
|
|
3593
|
-
await
|
|
3594
|
-
|
|
4464
|
+
const result = await cdp2.evaluate(script, 15e3);
|
|
4465
|
+
console.log(`[switch_chat] result:`, result);
|
|
4466
|
+
return { success: true, result };
|
|
3595
4467
|
} catch (e) {
|
|
4468
|
+
console.error(`[switch_chat] error:`, e.message);
|
|
3596
4469
|
return { success: false, error: e.message };
|
|
3597
4470
|
}
|
|
3598
4471
|
}
|
|
@@ -3780,11 +4653,304 @@ var init_daemon_commands = __esm({
|
|
|
3780
4653
|
const agents = await this.getCdp().discoverAgentWebviews();
|
|
3781
4654
|
return { success: true, agents };
|
|
3782
4655
|
}
|
|
4656
|
+
// ─── CDP DOM Analysis Tools ─────────────────
|
|
4657
|
+
/**
|
|
4658
|
+
* CDP DOM Dump — IDE의 DOM 트리를 가져옴
|
|
4659
|
+
*
|
|
4660
|
+
* args:
|
|
4661
|
+
* selector?: string — CSS 셀렉터로 특정 영역만 덤프 (default: 전체)
|
|
4662
|
+
* depth?: number — 덤프 깊이 제한 (default: 10)
|
|
4663
|
+
* attrs?: boolean — 속성 포함 여부 (default: true)
|
|
4664
|
+
* maxLength?: number — 최대 문자 수 (default: 200000)
|
|
4665
|
+
* format?: 'html' | 'tree' | 'summary' — 출력 형식 (default: 'html')
|
|
4666
|
+
* sessionId?: string — agent webview 세션 ID (있으면 해당 webview DOM)
|
|
4667
|
+
*/
|
|
4668
|
+
async handleCdpDomDump(args) {
|
|
4669
|
+
if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
|
|
4670
|
+
const selector = args?.selector || "body";
|
|
4671
|
+
const depth = args?.depth || 10;
|
|
4672
|
+
const maxLength = args?.maxLength || 2e5;
|
|
4673
|
+
const format = args?.format || "html";
|
|
4674
|
+
const attrs = args?.attrs !== false;
|
|
4675
|
+
const sessionId = args?.sessionId;
|
|
4676
|
+
try {
|
|
4677
|
+
let expression;
|
|
4678
|
+
if (format === "summary") {
|
|
4679
|
+
expression = `(() => {
|
|
4680
|
+
const root = document.querySelector('${selector.replace(/'/g, "\\'")}');
|
|
4681
|
+
if (!root) return JSON.stringify({ error: 'Selector not found: ${selector}' });
|
|
4682
|
+
|
|
4683
|
+
function summarize(el, depth, maxD) {
|
|
4684
|
+
if (depth > maxD) return { tag: '...', note: 'max depth' };
|
|
4685
|
+
const node = {
|
|
4686
|
+
tag: el.tagName?.toLowerCase(),
|
|
4687
|
+
id: el.id || undefined,
|
|
4688
|
+
class: el.className && typeof el.className === 'string' ? el.className.split(' ').filter(c => c).slice(0, 5).join(' ') : undefined,
|
|
4689
|
+
role: el.getAttribute?.('role') || undefined,
|
|
4690
|
+
'data-testid': el.getAttribute?.('data-testid') || undefined,
|
|
4691
|
+
childCount: el.children?.length || 0,
|
|
4692
|
+
};
|
|
4693
|
+
if (el.children?.length > 0 && depth < maxD) {
|
|
4694
|
+
node.children = Array.from(el.children).slice(0, 30).map(c => summarize(c, depth + 1, maxD));
|
|
4695
|
+
}
|
|
4696
|
+
return node;
|
|
4697
|
+
}
|
|
4698
|
+
return JSON.stringify(summarize(root, 0, ${depth}));
|
|
4699
|
+
})()`;
|
|
4700
|
+
} else if (format === "tree") {
|
|
4701
|
+
expression = `(() => {
|
|
4702
|
+
const root = document.querySelector('${selector.replace(/'/g, "\\'")}');
|
|
4703
|
+
if (!root) return 'Selector not found: ${selector}';
|
|
4704
|
+
|
|
4705
|
+
function tree(el, indent, depth, maxD) {
|
|
4706
|
+
if (depth > maxD) return indent + '...\\n';
|
|
4707
|
+
let line = indent + '<' + (el.tagName?.toLowerCase() || '?');
|
|
4708
|
+
if (el.id) line += ' #' + el.id;
|
|
4709
|
+
if (el.className && typeof el.className === 'string') {
|
|
4710
|
+
const cls = el.className.trim().split(' ').filter(c => c).slice(0, 3).join('.');
|
|
4711
|
+
if (cls) line += ' .' + cls;
|
|
4712
|
+
}
|
|
4713
|
+
const role = el.getAttribute?.('role');
|
|
4714
|
+
if (role) line += ' [role=' + role + ']';
|
|
4715
|
+
const testId = el.getAttribute?.('data-testid');
|
|
4716
|
+
if (testId) line += ' [data-testid=' + testId + ']';
|
|
4717
|
+
line += '> (' + (el.children?.length || 0) + ')\\n';
|
|
4718
|
+
|
|
4719
|
+
let result = line;
|
|
4720
|
+
if (el.children?.length > 0 && depth < maxD) {
|
|
4721
|
+
for (let i = 0; i < Math.min(el.children.length, 30); i++) {
|
|
4722
|
+
result += tree(el.children[i], indent + ' ', depth + 1, maxD);
|
|
4723
|
+
}
|
|
4724
|
+
if (el.children.length > 30) result += indent + ' ... +' + (el.children.length - 30) + ' more\\n';
|
|
4725
|
+
}
|
|
4726
|
+
return result;
|
|
4727
|
+
}
|
|
4728
|
+
return tree(root, '', 0, ${depth});
|
|
4729
|
+
})()`;
|
|
4730
|
+
} else {
|
|
4731
|
+
expression = `(() => {
|
|
4732
|
+
const root = document.querySelector('${selector.replace(/'/g, "\\'")}');
|
|
4733
|
+
if (!root) return 'Selector not found: ${selector}';
|
|
4734
|
+
let html = root.outerHTML;
|
|
4735
|
+
if (html.length > ${maxLength}) {
|
|
4736
|
+
html = html.slice(0, ${maxLength}) + '\\n<!-- TRUNCATED at ${maxLength} chars -->';
|
|
4737
|
+
}
|
|
4738
|
+
return html;
|
|
4739
|
+
})()`;
|
|
4740
|
+
}
|
|
4741
|
+
let result;
|
|
4742
|
+
if (sessionId) {
|
|
4743
|
+
result = await this.getCdp().evaluateInSession(sessionId, expression);
|
|
4744
|
+
} else {
|
|
4745
|
+
result = await this.getCdp().evaluate(expression, 3e4);
|
|
4746
|
+
}
|
|
4747
|
+
if (format === "summary" && typeof result === "string") {
|
|
4748
|
+
try {
|
|
4749
|
+
result = JSON.parse(result);
|
|
4750
|
+
} catch {
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
const size = typeof result === "string" ? result.length : JSON.stringify(result).length;
|
|
4754
|
+
return { success: true, result, format, selector, size };
|
|
4755
|
+
} catch (e) {
|
|
4756
|
+
return { success: false, error: e.message };
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
/**
|
|
4760
|
+
* CDP DOM Query — CSS 셀렉터 테스트
|
|
4761
|
+
* 셀렉터가 몇 개 요소에 매칭되는지, 어떤 요소인지 확인
|
|
4762
|
+
*
|
|
4763
|
+
* args:
|
|
4764
|
+
* selector: string — CSS 셀렉터
|
|
4765
|
+
* limit?: number — 반환할 최대 요소 수 (default: 20)
|
|
4766
|
+
* content?: boolean — 텍스트 내용 포함 여부 (default: true)
|
|
4767
|
+
* sessionId?: string — agent webview 세션 ID
|
|
4768
|
+
*/
|
|
4769
|
+
async handleCdpDomQuery(args) {
|
|
4770
|
+
if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
|
|
4771
|
+
const selector = args?.selector;
|
|
4772
|
+
if (!selector) return { success: false, error: "selector required" };
|
|
4773
|
+
const limit = args?.limit || 20;
|
|
4774
|
+
const content = args?.content !== false;
|
|
4775
|
+
const sessionId = args?.sessionId;
|
|
4776
|
+
const expression = `(() => {
|
|
4777
|
+
try {
|
|
4778
|
+
const els = document.querySelectorAll('${selector.replace(/'/g, "\\'")}');
|
|
4779
|
+
const results = [];
|
|
4780
|
+
for (let i = 0; i < Math.min(els.length, ${limit}); i++) {
|
|
4781
|
+
const el = els[i];
|
|
4782
|
+
const item = {
|
|
4783
|
+
index: i,
|
|
4784
|
+
tag: el.tagName?.toLowerCase(),
|
|
4785
|
+
id: el.id || null,
|
|
4786
|
+
class: el.className && typeof el.className === 'string' ? el.className.trim().slice(0, 200) : null,
|
|
4787
|
+
role: el.getAttribute?.('role') || null,
|
|
4788
|
+
'data-testid': el.getAttribute?.('data-testid') || null,
|
|
4789
|
+
rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
|
|
4790
|
+
visible: el.offsetParent !== null || el.offsetWidth > 0,
|
|
4791
|
+
};
|
|
4792
|
+
${content ? `item.text = (el.textContent || '').trim().slice(0, 200);` : ""}
|
|
4793
|
+
${content ? `item.value = el.value !== undefined ? String(el.value).slice(0, 200) : undefined;` : ""}
|
|
4794
|
+
results.push(item);
|
|
4795
|
+
}
|
|
4796
|
+
return JSON.stringify({ total: els.length, results });
|
|
4797
|
+
} catch(e) {
|
|
4798
|
+
return JSON.stringify({ error: e.message });
|
|
4799
|
+
}
|
|
4800
|
+
})()`;
|
|
4801
|
+
try {
|
|
4802
|
+
let raw;
|
|
4803
|
+
if (sessionId) {
|
|
4804
|
+
raw = await this.getCdp().evaluateInSession(sessionId, expression);
|
|
4805
|
+
} else {
|
|
4806
|
+
raw = await this.getCdp().evaluate(expression, 15e3);
|
|
4807
|
+
}
|
|
4808
|
+
const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
4809
|
+
return { success: true, ...parsed, selector };
|
|
4810
|
+
} catch (e) {
|
|
4811
|
+
return { success: false, error: e.message };
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4814
|
+
/**
|
|
4815
|
+
* CDP DOM Debug — IDE AI 패널 특화 분석
|
|
4816
|
+
* 새 IDE 지원 시 필요한 핵심 정보를 한 번에 수집
|
|
4817
|
+
*
|
|
4818
|
+
* args:
|
|
4819
|
+
* ideType?: string — IDE 유형 힌트
|
|
4820
|
+
* sessionId?: string — agent webview 세션 ID
|
|
4821
|
+
*/
|
|
4822
|
+
async handleCdpDomDebug(args) {
|
|
4823
|
+
if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
|
|
4824
|
+
const sessionId = args?.sessionId;
|
|
4825
|
+
const expression = `(() => {
|
|
4826
|
+
const result = {
|
|
4827
|
+
url: location.href,
|
|
4828
|
+
title: document.title,
|
|
4829
|
+
viewport: { w: window.innerWidth, h: window.innerHeight },
|
|
4830
|
+
|
|
4831
|
+
// \uC785\uB825 \uD544\uB4DC \uD6C4\uBCF4
|
|
4832
|
+
inputs: [],
|
|
4833
|
+
// textarea \uD6C4\uBCF4
|
|
4834
|
+
textareas: [],
|
|
4835
|
+
// contenteditable \uD6C4\uBCF4
|
|
4836
|
+
editables: [],
|
|
4837
|
+
// \uBC84\uD2BC (send, submit \uB4F1)
|
|
4838
|
+
buttons: [],
|
|
4839
|
+
// iframes (agent webviews)
|
|
4840
|
+
iframes: [],
|
|
4841
|
+
// role="textbox" \uD6C4\uBCF4
|
|
4842
|
+
textboxes: [],
|
|
4843
|
+
};
|
|
4844
|
+
|
|
4845
|
+
// \uC785\uB825 \uD544\uB4DC
|
|
4846
|
+
document.querySelectorAll('input[type="text"], input:not([type])').forEach((el, i) => {
|
|
4847
|
+
if (i >= 10) return;
|
|
4848
|
+
result.inputs.push({
|
|
4849
|
+
tag: 'input',
|
|
4850
|
+
id: el.id || null,
|
|
4851
|
+
class: (el.className || '').toString().slice(0, 150),
|
|
4852
|
+
placeholder: el.getAttribute('placeholder') || null,
|
|
4853
|
+
name: el.name || null,
|
|
4854
|
+
value: el.value?.slice(0, 100) || null,
|
|
4855
|
+
visible: el.offsetParent !== null,
|
|
4856
|
+
rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
|
|
4857
|
+
});
|
|
4858
|
+
});
|
|
4859
|
+
|
|
4860
|
+
// textarea
|
|
4861
|
+
document.querySelectorAll('textarea').forEach((el, i) => {
|
|
4862
|
+
if (i >= 10) return;
|
|
4863
|
+
result.textareas.push({
|
|
4864
|
+
id: el.id || null,
|
|
4865
|
+
class: (el.className || '').toString().slice(0, 150),
|
|
4866
|
+
placeholder: el.getAttribute('placeholder') || null,
|
|
4867
|
+
rows: el.rows,
|
|
4868
|
+
value: el.value?.slice(0, 100) || null,
|
|
4869
|
+
visible: el.offsetParent !== null,
|
|
4870
|
+
rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
|
|
4871
|
+
});
|
|
4872
|
+
});
|
|
4873
|
+
|
|
4874
|
+
// contenteditable
|
|
4875
|
+
document.querySelectorAll('[contenteditable="true"]').forEach((el, i) => {
|
|
4876
|
+
if (i >= 10) return;
|
|
4877
|
+
result.editables.push({
|
|
4878
|
+
tag: el.tagName?.toLowerCase(),
|
|
4879
|
+
id: el.id || null,
|
|
4880
|
+
class: (el.className || '').toString().slice(0, 150),
|
|
4881
|
+
role: el.getAttribute('role') || null,
|
|
4882
|
+
text: (el.textContent || '').trim().slice(0, 100),
|
|
4883
|
+
visible: el.offsetParent !== null,
|
|
4884
|
+
rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
|
|
4885
|
+
});
|
|
4886
|
+
});
|
|
4887
|
+
|
|
4888
|
+
// role="textbox"
|
|
4889
|
+
document.querySelectorAll('[role="textbox"]').forEach((el, i) => {
|
|
4890
|
+
if (i >= 10) return;
|
|
4891
|
+
result.textboxes.push({
|
|
4892
|
+
tag: el.tagName?.toLowerCase(),
|
|
4893
|
+
id: el.id || null,
|
|
4894
|
+
class: (el.className || '').toString().slice(0, 150),
|
|
4895
|
+
'aria-label': el.getAttribute('aria-label') || null,
|
|
4896
|
+
text: (el.textContent || '').trim().slice(0, 100),
|
|
4897
|
+
visible: el.offsetParent !== null,
|
|
4898
|
+
rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
|
|
4899
|
+
});
|
|
4900
|
+
});
|
|
4901
|
+
|
|
4902
|
+
// \uBC84\uD2BC (send, submit, accept, reject, approve \uB4F1)
|
|
4903
|
+
const btnKeywords = /send|submit|accept|reject|approve|deny|cancel|confirm|run|execute|apply/i;
|
|
4904
|
+
document.querySelectorAll('button, [role="button"], input[type="submit"]').forEach((el, i) => {
|
|
4905
|
+
const text = (el.textContent || el.getAttribute('aria-label') || '').trim();
|
|
4906
|
+
if (i < 30 && (text.length < 30 || btnKeywords.test(text))) {
|
|
4907
|
+
result.buttons.push({
|
|
4908
|
+
tag: el.tagName?.toLowerCase(),
|
|
4909
|
+
id: el.id || null,
|
|
4910
|
+
class: (el.className || '').toString().slice(0, 150),
|
|
4911
|
+
text: text.slice(0, 80),
|
|
4912
|
+
'aria-label': el.getAttribute('aria-label') || null,
|
|
4913
|
+
disabled: el.disabled || el.getAttribute('disabled') !== null,
|
|
4914
|
+
visible: el.offsetParent !== null,
|
|
4915
|
+
rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
|
|
4916
|
+
});
|
|
4917
|
+
}
|
|
4918
|
+
});
|
|
4919
|
+
|
|
4920
|
+
// iframes
|
|
4921
|
+
document.querySelectorAll('iframe, webview').forEach((el, i) => {
|
|
4922
|
+
if (i >= 20) return;
|
|
4923
|
+
result.iframes.push({
|
|
4924
|
+
tag: el.tagName?.toLowerCase(),
|
|
4925
|
+
id: el.id || null,
|
|
4926
|
+
class: (el.className || '').toString().slice(0, 150),
|
|
4927
|
+
src: el.getAttribute('src')?.slice(0, 200) || null,
|
|
4928
|
+
title: el.getAttribute('title') || null,
|
|
4929
|
+
visible: el.offsetParent !== null,
|
|
4930
|
+
rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
|
|
4931
|
+
});
|
|
4932
|
+
});
|
|
4933
|
+
|
|
4934
|
+
return JSON.stringify(result);
|
|
4935
|
+
})()`;
|
|
4936
|
+
try {
|
|
4937
|
+
let raw;
|
|
4938
|
+
if (sessionId) {
|
|
4939
|
+
raw = await this.getCdp().evaluateInSession(sessionId, expression);
|
|
4940
|
+
} else {
|
|
4941
|
+
raw = await this.getCdp().evaluate(expression, 3e4);
|
|
4942
|
+
}
|
|
4943
|
+
const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
4944
|
+
return { success: true, ...parsed };
|
|
4945
|
+
} catch (e) {
|
|
4946
|
+
return { success: false, error: e.message };
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
3783
4949
|
// ─── 파일 직접 처리 ─────────────────────────
|
|
3784
4950
|
async handleFileRead(args) {
|
|
3785
4951
|
try {
|
|
3786
4952
|
const filePath = this.resolveSafePath(args?.path);
|
|
3787
|
-
const content =
|
|
4953
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
3788
4954
|
return { success: true, content, path: filePath };
|
|
3789
4955
|
} catch (e) {
|
|
3790
4956
|
return { success: false, error: e.message };
|
|
@@ -3793,8 +4959,8 @@ var init_daemon_commands = __esm({
|
|
|
3793
4959
|
async handleFileWrite(args) {
|
|
3794
4960
|
try {
|
|
3795
4961
|
const filePath = this.resolveSafePath(args?.path);
|
|
3796
|
-
|
|
3797
|
-
|
|
4962
|
+
fs2.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
4963
|
+
fs2.writeFileSync(filePath, args?.content || "", "utf-8");
|
|
3798
4964
|
return { success: true, path: filePath };
|
|
3799
4965
|
} catch (e) {
|
|
3800
4966
|
return { success: false, error: e.message };
|
|
@@ -3803,11 +4969,11 @@ var init_daemon_commands = __esm({
|
|
|
3803
4969
|
async handleFileList(args) {
|
|
3804
4970
|
try {
|
|
3805
4971
|
const dirPath = this.resolveSafePath(args?.path || ".");
|
|
3806
|
-
const entries =
|
|
4972
|
+
const entries = fs2.readdirSync(dirPath, { withFileTypes: true });
|
|
3807
4973
|
const files = entries.map((e) => ({
|
|
3808
4974
|
name: e.name,
|
|
3809
4975
|
type: e.isDirectory() ? "directory" : "file",
|
|
3810
|
-
size: e.isFile() ?
|
|
4976
|
+
size: e.isFile() ? fs2.statSync(path3.join(dirPath, e.name)).size : void 0
|
|
3811
4977
|
}));
|
|
3812
4978
|
return { success: true, files, path: dirPath };
|
|
3813
4979
|
} catch (e) {
|
|
@@ -3845,7 +5011,7 @@ var init_daemon_commands = __esm({
|
|
|
3845
5011
|
const candidates = ["Cursor", "Code", "VSCodium"];
|
|
3846
5012
|
for (const app of candidates) {
|
|
3847
5013
|
const stateFile = path3.join(storageDir, app, "User", "globalStorage", "state.vscdb");
|
|
3848
|
-
if (
|
|
5014
|
+
if (fs2.existsSync(stateFile)) {
|
|
3849
5015
|
const result = await this.delegateToExtension("adhdev.getRecentWorkspaces", []);
|
|
3850
5016
|
if (result.success && Array.isArray(result.result)) {
|
|
3851
5017
|
const merged = Array.from(/* @__PURE__ */ new Set([...cliRecent, ...result.result])).slice(0, 20);
|
|
@@ -3938,6 +5104,40 @@ var init_daemon_commands = __esm({
|
|
|
3938
5104
|
const ok = await this.agentStream.focusAgentEditor(this.getCdp(), agentType);
|
|
3939
5105
|
return { success: ok };
|
|
3940
5106
|
}
|
|
5107
|
+
// ─── PTY Raw I/O (터미널 뷰) ──────────────────────
|
|
5108
|
+
handlePtyInput(args) {
|
|
5109
|
+
const { cliType, data } = args || {};
|
|
5110
|
+
if (!data) return { success: false, error: "data required" };
|
|
5111
|
+
if (this.ctx.adapters) {
|
|
5112
|
+
const targetCli = cliType || "gemini-cli";
|
|
5113
|
+
for (const [, adapter] of this.ctx.adapters) {
|
|
5114
|
+
if (adapter.cliType === targetCli && typeof adapter.writeRaw === "function") {
|
|
5115
|
+
adapter.writeRaw(data);
|
|
5116
|
+
return { success: true };
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
return { success: false, error: `CLI adapter not found: ${cliType}` };
|
|
5121
|
+
}
|
|
5122
|
+
handlePtyResize(args) {
|
|
5123
|
+
const { cliType, cols, rows, force } = args || {};
|
|
5124
|
+
if (!cols || !rows) return { success: false, error: "cols and rows required" };
|
|
5125
|
+
if (this.ctx.adapters) {
|
|
5126
|
+
const targetCli = cliType || "gemini-cli";
|
|
5127
|
+
for (const [, adapter] of this.ctx.adapters) {
|
|
5128
|
+
if (adapter.cliType === targetCli && typeof adapter.resize === "function") {
|
|
5129
|
+
if (force) {
|
|
5130
|
+
adapter.resize(cols - 1, rows);
|
|
5131
|
+
setTimeout(() => adapter.resize(cols, rows), 50);
|
|
5132
|
+
} else {
|
|
5133
|
+
adapter.resize(cols, rows);
|
|
5134
|
+
}
|
|
5135
|
+
return { success: true };
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
return { success: false, error: `CLI adapter not found: ${cliType}` };
|
|
5140
|
+
}
|
|
3941
5141
|
};
|
|
3942
5142
|
}
|
|
3943
5143
|
});
|
|
@@ -4236,13 +5436,13 @@ var init_manager = __esm({
|
|
|
4236
5436
|
}
|
|
4237
5437
|
}
|
|
4238
5438
|
}
|
|
4239
|
-
async switchActiveAgent(
|
|
5439
|
+
async switchActiveAgent(cdp2, agentType) {
|
|
4240
5440
|
if (this._activeAgentType === agentType) return;
|
|
4241
5441
|
if (this._activeAgentType) {
|
|
4242
5442
|
const prev = this.managed.get(this._activeAgentType);
|
|
4243
5443
|
if (prev) {
|
|
4244
5444
|
try {
|
|
4245
|
-
await
|
|
5445
|
+
await cdp2.detachAgent(prev.sessionId);
|
|
4246
5446
|
} catch {
|
|
4247
5447
|
}
|
|
4248
5448
|
this.managed.delete(this._activeAgentType);
|
|
@@ -4254,7 +5454,7 @@ var init_manager = __esm({
|
|
|
4254
5454
|
this.logFn(`[AgentStream] Active agent: ${agentType || "none"}`);
|
|
4255
5455
|
}
|
|
4256
5456
|
/** Agent webview 발견 + 세션 연결 */
|
|
4257
|
-
async syncAgentSessions(
|
|
5457
|
+
async syncAgentSessions(cdp2) {
|
|
4258
5458
|
if (!this.enabled || !this._activeAgentType) return;
|
|
4259
5459
|
const now = Date.now();
|
|
4260
5460
|
if (this.managed.has(this._activeAgentType) && now - this.lastDiscoveryTime < this.discoveryIntervalMs) {
|
|
@@ -4262,12 +5462,12 @@ var init_manager = __esm({
|
|
|
4262
5462
|
}
|
|
4263
5463
|
this.lastDiscoveryTime = now;
|
|
4264
5464
|
try {
|
|
4265
|
-
const targets = await
|
|
5465
|
+
const targets = await cdp2.discoverAgentWebviews();
|
|
4266
5466
|
const activeTarget = targets.find((t) => t.agentType === this._activeAgentType);
|
|
4267
5467
|
if (activeTarget && !this.managed.has(this._activeAgentType)) {
|
|
4268
5468
|
const adapter = this.allAdapters.find((a) => a.agentType === this._activeAgentType);
|
|
4269
5469
|
if (adapter) {
|
|
4270
|
-
const sessionId = await
|
|
5470
|
+
const sessionId = await cdp2.attachToAgent(activeTarget);
|
|
4271
5471
|
if (sessionId) {
|
|
4272
5472
|
this.managed.set(this._activeAgentType, {
|
|
4273
5473
|
adapter,
|
|
@@ -4283,7 +5483,7 @@ var init_manager = __esm({
|
|
|
4283
5483
|
}
|
|
4284
5484
|
for (const [type, agent] of this.managed) {
|
|
4285
5485
|
if (type !== this._activeAgentType) {
|
|
4286
|
-
await
|
|
5486
|
+
await cdp2.detachAgent(agent.sessionId);
|
|
4287
5487
|
this.managed.delete(type);
|
|
4288
5488
|
}
|
|
4289
5489
|
}
|
|
@@ -4293,7 +5493,7 @@ var init_manager = __esm({
|
|
|
4293
5493
|
}
|
|
4294
5494
|
}
|
|
4295
5495
|
/** 활성 에이전트 상태 수집 */
|
|
4296
|
-
async collectAgentStreams(
|
|
5496
|
+
async collectAgentStreams(cdp2) {
|
|
4297
5497
|
if (!this.enabled) return [];
|
|
4298
5498
|
const results = [];
|
|
4299
5499
|
if (this._activeAgentType && this.managed.has(this._activeAgentType)) {
|
|
@@ -4305,7 +5505,7 @@ var init_manager = __esm({
|
|
|
4305
5505
|
results.push(agent.lastState);
|
|
4306
5506
|
} else {
|
|
4307
5507
|
try {
|
|
4308
|
-
const evaluate = (expr, timeout) =>
|
|
5508
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4309
5509
|
const state = await agent.adapter.readChat(evaluate);
|
|
4310
5510
|
agent.lastState = state;
|
|
4311
5511
|
agent.lastError = null;
|
|
@@ -4327,7 +5527,7 @@ var init_manager = __esm({
|
|
|
4327
5527
|
});
|
|
4328
5528
|
if (errorMsg.includes("timeout") || errorMsg.includes("not connected") || errorMsg.includes("Session")) {
|
|
4329
5529
|
try {
|
|
4330
|
-
await
|
|
5530
|
+
await cdp2.detachAgent(agent.sessionId);
|
|
4331
5531
|
} catch {
|
|
4332
5532
|
}
|
|
4333
5533
|
this.managed.delete(type);
|
|
@@ -4338,12 +5538,12 @@ var init_manager = __esm({
|
|
|
4338
5538
|
}
|
|
4339
5539
|
return results;
|
|
4340
5540
|
}
|
|
4341
|
-
async sendToAgent(
|
|
5541
|
+
async sendToAgent(cdp2, agentType, text, targetIdeType) {
|
|
4342
5542
|
await this.ensureAgentPanelOpen(agentType, targetIdeType);
|
|
4343
5543
|
const agent = this.managed.get(agentType);
|
|
4344
5544
|
if (!agent) return false;
|
|
4345
5545
|
try {
|
|
4346
|
-
const evaluate = (expr, timeout) =>
|
|
5546
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4347
5547
|
await agent.adapter.sendMessage(evaluate, text);
|
|
4348
5548
|
return true;
|
|
4349
5549
|
} catch (e) {
|
|
@@ -4351,24 +5551,24 @@ var init_manager = __esm({
|
|
|
4351
5551
|
return false;
|
|
4352
5552
|
}
|
|
4353
5553
|
}
|
|
4354
|
-
async resolveAgentAction(
|
|
5554
|
+
async resolveAgentAction(cdp2, agentType, action, targetIdeType) {
|
|
4355
5555
|
await this.ensureAgentPanelOpen(agentType, targetIdeType);
|
|
4356
5556
|
const agent = this.managed.get(agentType);
|
|
4357
5557
|
if (!agent) return false;
|
|
4358
5558
|
try {
|
|
4359
|
-
const evaluate = (expr, timeout) =>
|
|
5559
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4360
5560
|
return await agent.adapter.resolveAction(evaluate, action);
|
|
4361
5561
|
} catch (e) {
|
|
4362
5562
|
this.logFn(`[AgentStream] resolveAction(${agentType}) error: ${e.message}`);
|
|
4363
5563
|
return false;
|
|
4364
5564
|
}
|
|
4365
5565
|
}
|
|
4366
|
-
async newAgentSession(
|
|
5566
|
+
async newAgentSession(cdp2, agentType, targetIdeType) {
|
|
4367
5567
|
await this.ensureAgentPanelOpen(agentType, targetIdeType);
|
|
4368
5568
|
const agent = this.managed.get(agentType);
|
|
4369
5569
|
if (!agent) return false;
|
|
4370
5570
|
try {
|
|
4371
|
-
const evaluate = (expr, timeout) =>
|
|
5571
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4372
5572
|
await agent.adapter.newSession(evaluate);
|
|
4373
5573
|
return true;
|
|
4374
5574
|
} catch (e) {
|
|
@@ -4376,33 +5576,45 @@ var init_manager = __esm({
|
|
|
4376
5576
|
return false;
|
|
4377
5577
|
}
|
|
4378
5578
|
}
|
|
4379
|
-
async listAgentChats(
|
|
4380
|
-
|
|
5579
|
+
async listAgentChats(cdp2, agentType) {
|
|
5580
|
+
let agent = this.managed.get(agentType);
|
|
5581
|
+
if (!agent) {
|
|
5582
|
+
this.logFn(`[AgentStream] listChats: ${agentType} not managed, trying on-demand activation`);
|
|
5583
|
+
await this.switchActiveAgent(cdp2, agentType);
|
|
5584
|
+
await this.syncAgentSessions(cdp2);
|
|
5585
|
+
agent = this.managed.get(agentType);
|
|
5586
|
+
}
|
|
4381
5587
|
if (!agent || typeof agent.adapter.listChats !== "function") return [];
|
|
4382
5588
|
try {
|
|
4383
|
-
const evaluate = (expr, timeout) =>
|
|
5589
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4384
5590
|
return await agent.adapter.listChats(evaluate);
|
|
4385
5591
|
} catch (e) {
|
|
4386
5592
|
this.logFn(`[AgentStream] listChats(${agentType}) error: ${e.message}`);
|
|
4387
5593
|
return [];
|
|
4388
5594
|
}
|
|
4389
5595
|
}
|
|
4390
|
-
async switchAgentSession(
|
|
4391
|
-
|
|
5596
|
+
async switchAgentSession(cdp2, agentType, sessionId) {
|
|
5597
|
+
let agent = this.managed.get(agentType);
|
|
5598
|
+
if (!agent) {
|
|
5599
|
+
this.logFn(`[AgentStream] switchSession: ${agentType} not managed, trying on-demand activation`);
|
|
5600
|
+
await this.switchActiveAgent(cdp2, agentType);
|
|
5601
|
+
await this.syncAgentSessions(cdp2);
|
|
5602
|
+
agent = this.managed.get(agentType);
|
|
5603
|
+
}
|
|
4392
5604
|
if (!agent || typeof agent.adapter.switchSession !== "function") return false;
|
|
4393
5605
|
try {
|
|
4394
|
-
const evaluate = (expr, timeout) =>
|
|
5606
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4395
5607
|
return await agent.adapter.switchSession(evaluate, sessionId);
|
|
4396
5608
|
} catch (e) {
|
|
4397
5609
|
this.logFn(`[AgentStream] switchSession(${agentType}) error: ${e.message}`);
|
|
4398
5610
|
return false;
|
|
4399
5611
|
}
|
|
4400
5612
|
}
|
|
4401
|
-
async focusAgentEditor(
|
|
5613
|
+
async focusAgentEditor(cdp2, agentType) {
|
|
4402
5614
|
const agent = this.managed.get(agentType);
|
|
4403
5615
|
if (!agent || typeof agent.adapter.focusEditor !== "function") return false;
|
|
4404
5616
|
try {
|
|
4405
|
-
const evaluate = (expr, timeout) =>
|
|
5617
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4406
5618
|
await agent.adapter.focusEditor(evaluate);
|
|
4407
5619
|
return true;
|
|
4408
5620
|
} catch (e) {
|
|
@@ -4416,12 +5628,12 @@ var init_manager = __esm({
|
|
|
4416
5628
|
getManagedAgent(agentType) {
|
|
4417
5629
|
return this.managed.get(agentType);
|
|
4418
5630
|
}
|
|
4419
|
-
async dispose(
|
|
5631
|
+
async dispose(cdp2) {
|
|
4420
5632
|
for (const loader of this.scriptLoaders.values()) loader.stop();
|
|
4421
5633
|
this.scriptLoaders.clear();
|
|
4422
5634
|
for (const [, agent] of this.managed) {
|
|
4423
5635
|
try {
|
|
4424
|
-
await
|
|
5636
|
+
await cdp2.detachAgent(agent.sessionId);
|
|
4425
5637
|
} catch {
|
|
4426
5638
|
}
|
|
4427
5639
|
}
|
|
@@ -4450,6 +5662,107 @@ var init_agent_stream = __esm({
|
|
|
4450
5662
|
}
|
|
4451
5663
|
});
|
|
4452
5664
|
|
|
5665
|
+
// src/daemon-logger.ts
|
|
5666
|
+
var daemon_logger_exports = {};
|
|
5667
|
+
__export(daemon_logger_exports, {
|
|
5668
|
+
LOG_DIR_PATH: () => LOG_DIR_PATH,
|
|
5669
|
+
LOG_PATH: () => LOG_PATH,
|
|
5670
|
+
cdpLogFn: () => cdpLogFn,
|
|
5671
|
+
daemonLog: () => daemonLog,
|
|
5672
|
+
installGlobalInterceptor: () => installGlobalInterceptor
|
|
5673
|
+
});
|
|
5674
|
+
function rotateIfNeeded() {
|
|
5675
|
+
try {
|
|
5676
|
+
const stat = fs3.statSync(LOG_FILE);
|
|
5677
|
+
if (stat.size > MAX_LOG_SIZE) {
|
|
5678
|
+
const backup = LOG_FILE + ".old";
|
|
5679
|
+
try {
|
|
5680
|
+
fs3.unlinkSync(backup);
|
|
5681
|
+
} catch {
|
|
5682
|
+
}
|
|
5683
|
+
fs3.renameSync(LOG_FILE, backup);
|
|
5684
|
+
}
|
|
5685
|
+
} catch {
|
|
5686
|
+
}
|
|
5687
|
+
}
|
|
5688
|
+
function writeToFile(line) {
|
|
5689
|
+
try {
|
|
5690
|
+
fs3.appendFileSync(LOG_FILE, line + "\n");
|
|
5691
|
+
} catch {
|
|
5692
|
+
}
|
|
5693
|
+
}
|
|
5694
|
+
function daemonLog(category, msg) {
|
|
5695
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${category}] ${msg}`;
|
|
5696
|
+
origConsoleLog(line);
|
|
5697
|
+
writeToFile(line);
|
|
5698
|
+
}
|
|
5699
|
+
function cdpLogFn(ideType) {
|
|
5700
|
+
return (msg) => {
|
|
5701
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [CDP:${ideType}] ${msg}`;
|
|
5702
|
+
origConsoleLog(line);
|
|
5703
|
+
writeToFile(line);
|
|
5704
|
+
};
|
|
5705
|
+
}
|
|
5706
|
+
function installGlobalInterceptor() {
|
|
5707
|
+
if (interceptorInstalled) return;
|
|
5708
|
+
interceptorInstalled = true;
|
|
5709
|
+
const stripAnsi4 = (str) => str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
5710
|
+
console.log = (...args) => {
|
|
5711
|
+
origConsoleLog(...args);
|
|
5712
|
+
try {
|
|
5713
|
+
const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
5714
|
+
const clean = stripAnsi4(msg);
|
|
5715
|
+
const line = clean.startsWith("[20") ? clean : `[${(/* @__PURE__ */ new Date()).toISOString()}] ${clean}`;
|
|
5716
|
+
writeToFile(line);
|
|
5717
|
+
} catch {
|
|
5718
|
+
}
|
|
5719
|
+
};
|
|
5720
|
+
console.error = (...args) => {
|
|
5721
|
+
origConsoleError(...args);
|
|
5722
|
+
try {
|
|
5723
|
+
const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
5724
|
+
const clean = stripAnsi4(msg);
|
|
5725
|
+
writeToFile(`[${(/* @__PURE__ */ new Date()).toISOString()}] [ERROR] ${clean}`);
|
|
5726
|
+
} catch {
|
|
5727
|
+
}
|
|
5728
|
+
};
|
|
5729
|
+
console.warn = (...args) => {
|
|
5730
|
+
origConsoleWarn(...args);
|
|
5731
|
+
try {
|
|
5732
|
+
const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
5733
|
+
const clean = stripAnsi4(msg);
|
|
5734
|
+
writeToFile(`[${(/* @__PURE__ */ new Date()).toISOString()}] [WARN] ${clean}`);
|
|
5735
|
+
} catch {
|
|
5736
|
+
}
|
|
5737
|
+
};
|
|
5738
|
+
writeToFile(`
|
|
5739
|
+
=== ADHDev Daemon started at ${(/* @__PURE__ */ new Date()).toISOString()} ===`);
|
|
5740
|
+
writeToFile(`Log file: ${LOG_FILE}`);
|
|
5741
|
+
}
|
|
5742
|
+
var fs3, path4, os8, LOG_DIR, LOG_FILE, MAX_LOG_SIZE, origConsoleLog, origConsoleError, origConsoleWarn, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
|
|
5743
|
+
var init_daemon_logger = __esm({
|
|
5744
|
+
"src/daemon-logger.ts"() {
|
|
5745
|
+
"use strict";
|
|
5746
|
+
fs3 = __toESM(require("fs"));
|
|
5747
|
+
path4 = __toESM(require("path"));
|
|
5748
|
+
os8 = __toESM(require("os"));
|
|
5749
|
+
LOG_DIR = process.platform === "darwin" ? path4.join(os8.homedir(), "Library", "Logs", "adhdev") : path4.join(os8.homedir(), ".local", "share", "adhdev", "logs");
|
|
5750
|
+
LOG_FILE = path4.join(LOG_DIR, "daemon.log");
|
|
5751
|
+
MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
5752
|
+
try {
|
|
5753
|
+
fs3.mkdirSync(LOG_DIR, { recursive: true });
|
|
5754
|
+
} catch {
|
|
5755
|
+
}
|
|
5756
|
+
rotateIfNeeded();
|
|
5757
|
+
origConsoleLog = console.log.bind(console);
|
|
5758
|
+
origConsoleError = console.error.bind(console);
|
|
5759
|
+
origConsoleWarn = console.warn.bind(console);
|
|
5760
|
+
interceptorInstalled = false;
|
|
5761
|
+
LOG_PATH = LOG_FILE;
|
|
5762
|
+
LOG_DIR_PATH = LOG_DIR;
|
|
5763
|
+
}
|
|
5764
|
+
});
|
|
5765
|
+
|
|
4453
5766
|
// src/adhdev-daemon.ts
|
|
4454
5767
|
var adhdev_daemon_exports = {};
|
|
4455
5768
|
__export(adhdev_daemon_exports, {
|
|
@@ -4458,24 +5771,24 @@ __export(adhdev_daemon_exports, {
|
|
|
4458
5771
|
stopDaemon: () => stopDaemon
|
|
4459
5772
|
});
|
|
4460
5773
|
function getDaemonPidFile() {
|
|
4461
|
-
const dir =
|
|
4462
|
-
if (!
|
|
4463
|
-
return
|
|
5774
|
+
const dir = path5.join(os9.homedir(), ".adhdev");
|
|
5775
|
+
if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
|
|
5776
|
+
return path5.join(dir, "daemon.pid");
|
|
4464
5777
|
}
|
|
4465
5778
|
function writeDaemonPid(pid) {
|
|
4466
|
-
|
|
5779
|
+
fs4.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
|
|
4467
5780
|
}
|
|
4468
5781
|
function removeDaemonPid() {
|
|
4469
5782
|
try {
|
|
4470
|
-
|
|
5783
|
+
fs4.unlinkSync(getDaemonPidFile());
|
|
4471
5784
|
} catch {
|
|
4472
5785
|
}
|
|
4473
5786
|
}
|
|
4474
5787
|
function isDaemonRunning() {
|
|
4475
5788
|
const pidFile = getDaemonPidFile();
|
|
4476
5789
|
try {
|
|
4477
|
-
if (!
|
|
4478
|
-
const pid = parseInt(
|
|
5790
|
+
if (!fs4.existsSync(pidFile)) return false;
|
|
5791
|
+
const pid = parseInt(fs4.readFileSync(pidFile, "utf-8").trim());
|
|
4479
5792
|
process.kill(pid, 0);
|
|
4480
5793
|
return true;
|
|
4481
5794
|
} catch {
|
|
@@ -4486,8 +5799,8 @@ function isDaemonRunning() {
|
|
|
4486
5799
|
function stopDaemon() {
|
|
4487
5800
|
const pidFile = getDaemonPidFile();
|
|
4488
5801
|
try {
|
|
4489
|
-
if (!
|
|
4490
|
-
const pid = parseInt(
|
|
5802
|
+
if (!fs4.existsSync(pidFile)) return false;
|
|
5803
|
+
const pid = parseInt(fs4.readFileSync(pidFile, "utf-8").trim());
|
|
4491
5804
|
process.kill(pid, "SIGTERM");
|
|
4492
5805
|
removeDaemonPid();
|
|
4493
5806
|
return true;
|
|
@@ -4496,7 +5809,7 @@ function stopDaemon() {
|
|
|
4496
5809
|
return false;
|
|
4497
5810
|
}
|
|
4498
5811
|
}
|
|
4499
|
-
var
|
|
5812
|
+
var os9, fs4, path5, crypto2, import_chalk2, DANGEROUS_PATTERNS, AdhdevDaemon;
|
|
4500
5813
|
var init_adhdev_daemon = __esm({
|
|
4501
5814
|
"src/adhdev-daemon.ts"() {
|
|
4502
5815
|
"use strict";
|
|
@@ -4515,9 +5828,9 @@ var init_adhdev_daemon = __esm({
|
|
|
4515
5828
|
init_daemon_commands();
|
|
4516
5829
|
init_agent_stream();
|
|
4517
5830
|
init_dist();
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
5831
|
+
os9 = __toESM(require("os"));
|
|
5832
|
+
fs4 = __toESM(require("fs"));
|
|
5833
|
+
path5 = __toESM(require("path"));
|
|
4521
5834
|
crypto2 = __toESM(require("crypto"));
|
|
4522
5835
|
import_chalk2 = __toESM(require("chalk"));
|
|
4523
5836
|
DANGEROUS_PATTERNS = [
|
|
@@ -4542,6 +5855,11 @@ var init_adhdev_daemon = __esm({
|
|
|
4542
5855
|
scriptLoaders = /* @__PURE__ */ new Map();
|
|
4543
5856
|
commandHandler = null;
|
|
4544
5857
|
screenshotTimer = null;
|
|
5858
|
+
lastStatusSentFull = false;
|
|
5859
|
+
lastStatusSentAt = 0;
|
|
5860
|
+
statusPendingThrottle = false;
|
|
5861
|
+
lastP2PStatusHash = "";
|
|
5862
|
+
statusReportCount = 0;
|
|
4545
5863
|
agentStreamManager = null;
|
|
4546
5864
|
agentStreamTimer = null;
|
|
4547
5865
|
running = false;
|
|
@@ -4562,6 +5880,11 @@ var init_adhdev_daemon = __esm({
|
|
|
4562
5880
|
this.localPort = DEFAULT_DAEMON_PORT;
|
|
4563
5881
|
}
|
|
4564
5882
|
async start(options = {}) {
|
|
5883
|
+
try {
|
|
5884
|
+
const { installGlobalInterceptor: installGlobalInterceptor2 } = (init_daemon_logger(), __toCommonJS(daemon_logger_exports));
|
|
5885
|
+
installGlobalInterceptor2();
|
|
5886
|
+
} catch {
|
|
5887
|
+
}
|
|
4565
5888
|
this.localPort = options.localPort || DEFAULT_DAEMON_PORT;
|
|
4566
5889
|
const workingDir = options.workingDir || process.cwd();
|
|
4567
5890
|
if (isDaemonRunning()) {
|
|
@@ -4592,7 +5915,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4592
5915
|
setTimeout(() => this.sendUnifiedStatusReport(), 500);
|
|
4593
5916
|
},
|
|
4594
5917
|
onExtensionStatus: () => {
|
|
4595
|
-
this.
|
|
5918
|
+
this.throttledStatusReport();
|
|
4596
5919
|
},
|
|
4597
5920
|
onExtensionEvent: (ext, event, data) => {
|
|
4598
5921
|
if (event === "token_updated" && data.token) {
|
|
@@ -4635,6 +5958,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4635
5958
|
for (const ideType of this.cdpManagers.keys()) {
|
|
4636
5959
|
const loader = new DaemonScriptLoader(serverUrl, ideType);
|
|
4637
5960
|
loader.setToken(config.connectionToken);
|
|
5961
|
+
loader.setPlatform(os9.platform());
|
|
4638
5962
|
await loader.start().catch((e) => {
|
|
4639
5963
|
console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${ideType}: ${e.message}`));
|
|
4640
5964
|
});
|
|
@@ -4644,6 +5968,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4644
5968
|
const fallbackType = this.detectedIdes.find((ide) => ide.installed)?.id || "cursor";
|
|
4645
5969
|
const loader = new DaemonScriptLoader(serverUrl, fallbackType);
|
|
4646
5970
|
loader.setToken(config.connectionToken);
|
|
5971
|
+
loader.setPlatform(os9.platform());
|
|
4647
5972
|
await loader.start().catch((e) => {
|
|
4648
5973
|
console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed: ${e.message}`));
|
|
4649
5974
|
});
|
|
@@ -4669,8 +5994,8 @@ var init_adhdev_daemon = __esm({
|
|
|
4669
5994
|
console.log(import_chalk2.default.yellow(` \u26A0 Failed to start CLI ${options.cliType}: ${e.message}`));
|
|
4670
5995
|
});
|
|
4671
5996
|
}
|
|
4672
|
-
const machineId =
|
|
4673
|
-
const machineHash = crypto2.createHash("md5").update(
|
|
5997
|
+
const machineId = os9.hostname().replace(/[^a-zA-Z0-9]/g, "_");
|
|
5998
|
+
const machineHash = crypto2.createHash("md5").update(os9.hostname() + os9.homedir()).digest("hex").slice(0, 8);
|
|
4674
5999
|
const instanceId = `daemon_${machineId}_${machineHash}`;
|
|
4675
6000
|
this.bridge = new CliBridgeConnection({
|
|
4676
6001
|
serverUrl: options.serverUrl || config.serverUrl,
|
|
@@ -4678,7 +6003,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4678
6003
|
cliInfo: {
|
|
4679
6004
|
type: "adhdev-daemon",
|
|
4680
6005
|
version: "0.2.0",
|
|
4681
|
-
platform:
|
|
6006
|
+
platform: os9.platform(),
|
|
4682
6007
|
instanceId
|
|
4683
6008
|
}
|
|
4684
6009
|
});
|
|
@@ -4690,8 +6015,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4690
6015
|
return this.commandHandler.handle("cdp_remote_action", event);
|
|
4691
6016
|
});
|
|
4692
6017
|
this.p2p.onCommand(async (cmdType, data, id) => {
|
|
4693
|
-
|
|
4694
|
-
return this.commandHandler.handle(cmdType, data);
|
|
6018
|
+
return this.handleP2PCommand(cmdType, data);
|
|
4695
6019
|
});
|
|
4696
6020
|
this.p2p.onFileRequest(async (req) => {
|
|
4697
6021
|
if (req.type === "read") {
|
|
@@ -4705,18 +6029,34 @@ var init_adhdev_daemon = __esm({
|
|
|
4705
6029
|
return { id: req.id, success: result.success, entries: result.files, error: result.error };
|
|
4706
6030
|
}
|
|
4707
6031
|
});
|
|
6032
|
+
this.p2p.onPtyInput((cliType, data) => {
|
|
6033
|
+
for (const [, adapter] of this.adapters) {
|
|
6034
|
+
if (adapter.cliType === cliType && typeof adapter.writeRaw === "function") {
|
|
6035
|
+
adapter.writeRaw(data);
|
|
6036
|
+
return;
|
|
6037
|
+
}
|
|
6038
|
+
}
|
|
6039
|
+
});
|
|
6040
|
+
this.p2p.onPtyResize((cliType, cols, rows) => {
|
|
6041
|
+
for (const [, adapter] of this.adapters) {
|
|
6042
|
+
if (adapter.cliType === cliType && typeof adapter.resize === "function") {
|
|
6043
|
+
adapter.resize(cols, rows);
|
|
6044
|
+
return;
|
|
6045
|
+
}
|
|
6046
|
+
}
|
|
6047
|
+
});
|
|
4708
6048
|
let ssDebugCount = 0;
|
|
4709
6049
|
this.screenshotTimer = setInterval(async () => {
|
|
4710
6050
|
const active = this.p2p?.screenshotActive;
|
|
4711
6051
|
const ssIdeType = this.p2p?.screenshotIdeType;
|
|
4712
|
-
const
|
|
4713
|
-
if (!active || !
|
|
6052
|
+
const cdp2 = ssIdeType ? this.getCdpFor(ssIdeType) : this.getAnyCdp();
|
|
6053
|
+
if (!active || !cdp2) return;
|
|
4714
6054
|
ssDebugCount++;
|
|
4715
6055
|
if (ssDebugCount <= 3 || ssDebugCount % 25 === 0) {
|
|
4716
6056
|
console.log(`[SS] Capturing screenshot... (tick ${ssDebugCount}, active=${active}, cdp=true)`);
|
|
4717
6057
|
}
|
|
4718
6058
|
try {
|
|
4719
|
-
const buf = await
|
|
6059
|
+
const buf = await cdp2.captureScreenshot();
|
|
4720
6060
|
if (buf) {
|
|
4721
6061
|
const sent = this.p2p.sendScreenshot(buf.toString("base64"));
|
|
4722
6062
|
if (ssDebugCount <= 3) console.log(`[SS] Screenshot sent: ${buf.length} bytes, delivered=${sent}`);
|
|
@@ -4842,7 +6182,10 @@ var init_adhdev_daemon = __esm({
|
|
|
4842
6182
|
"agent_stream_new",
|
|
4843
6183
|
"agent_stream_list_chats",
|
|
4844
6184
|
"agent_stream_switch_session",
|
|
4845
|
-
"agent_stream_focus"
|
|
6185
|
+
"agent_stream_focus",
|
|
6186
|
+
// PTY I/O commands (터미널 뷰)
|
|
6187
|
+
"pty_input",
|
|
6188
|
+
"pty_resize"
|
|
4846
6189
|
];
|
|
4847
6190
|
for (const cmdType of directCdpCommands) {
|
|
4848
6191
|
this.bridge.on(cmdType, async (msg) => {
|
|
@@ -4911,7 +6254,19 @@ var init_adhdev_daemon = __esm({
|
|
|
4911
6254
|
const dir = args?.dir || process.cwd();
|
|
4912
6255
|
if (!cliType) throw new Error("cliType required");
|
|
4913
6256
|
const key = this.getCliKey(cliType, dir);
|
|
4914
|
-
|
|
6257
|
+
if (this.adapters.has(key)) {
|
|
6258
|
+
await this.stopCliSession(key);
|
|
6259
|
+
} else {
|
|
6260
|
+
let found = false;
|
|
6261
|
+
for (const [k, adapter] of this.adapters) {
|
|
6262
|
+
if (adapter.cliType === cliType) {
|
|
6263
|
+
await this.stopCliSession(k);
|
|
6264
|
+
found = true;
|
|
6265
|
+
break;
|
|
6266
|
+
}
|
|
6267
|
+
}
|
|
6268
|
+
if (!found) console.log(import_chalk2.default.yellow(` \u26A0 No adapter found for ${cliType} (key: ${key})`));
|
|
6269
|
+
}
|
|
4915
6270
|
this.sendResult(msg, true, { cliType, dir });
|
|
4916
6271
|
return;
|
|
4917
6272
|
}
|
|
@@ -4953,7 +6308,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4953
6308
|
if (result.success && result.port && result.ideId && !this.cdpManagers.has(result.ideId)) {
|
|
4954
6309
|
console.log(import_chalk2.default.cyan(`[launch_ide] Connecting CDP for ${result.ideId} on port ${result.port}...`));
|
|
4955
6310
|
const manager = new DaemonCdpManager(result.port, (msg2) => {
|
|
4956
|
-
console.log(
|
|
6311
|
+
console.log(`[CDP:${result.ideId}] ${msg2}`);
|
|
4957
6312
|
}, true);
|
|
4958
6313
|
const connected = await manager.connect();
|
|
4959
6314
|
if (connected) {
|
|
@@ -4965,6 +6320,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4965
6320
|
const serverUrl = config.serverUrl || "https://api.adhf.dev";
|
|
4966
6321
|
const loader = new DaemonScriptLoader(serverUrl, result.ideId);
|
|
4967
6322
|
loader.setToken(config.connectionToken || "");
|
|
6323
|
+
loader.setPlatform(os9.platform());
|
|
4968
6324
|
await loader.start().catch((e) => {
|
|
4969
6325
|
console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${result.ideId}: ${e?.message}`));
|
|
4970
6326
|
});
|
|
@@ -4999,6 +6355,13 @@ var init_adhdev_daemon = __esm({
|
|
|
4999
6355
|
if (this.commandHandler) {
|
|
5000
6356
|
const result = await this.commandHandler.handle(cmd, args);
|
|
5001
6357
|
this.sendResult(msg, result.success, result);
|
|
6358
|
+
const CHAT_COMMANDS = ["send_chat", "new_chat", "switch_chat", "set_mode", "change_model", "agent_stream_send"];
|
|
6359
|
+
if (CHAT_COMMANDS.includes(cmd)) {
|
|
6360
|
+
setTimeout(() => this.sendUnifiedStatusReport().catch(() => {
|
|
6361
|
+
}), 1e3);
|
|
6362
|
+
setTimeout(() => this.sendUnifiedStatusReport().catch(() => {
|
|
6363
|
+
}), 3e3);
|
|
6364
|
+
}
|
|
5002
6365
|
} else {
|
|
5003
6366
|
this.sendResult(msg, false, { error: `Command handler not initialized: ${cmd}` });
|
|
5004
6367
|
}
|
|
@@ -5007,6 +6370,87 @@ var init_adhdev_daemon = __esm({
|
|
|
5007
6370
|
this.sendResult(msg, false, { error: e.message });
|
|
5008
6371
|
}
|
|
5009
6372
|
}
|
|
6373
|
+
/** P2P 명령 처리 — DaemonCommandHandler + daemon-level 명령 */
|
|
6374
|
+
async handleP2PCommand(cmdType, data) {
|
|
6375
|
+
try {
|
|
6376
|
+
switch (cmdType) {
|
|
6377
|
+
case "launch_ide": {
|
|
6378
|
+
const ideKey = data.ideId || data.ideType || "";
|
|
6379
|
+
const launchArgs = { ...data, ideId: ideKey };
|
|
6380
|
+
console.log(`[launch_ide] (P2P) target=${ideKey || "auto"}`);
|
|
6381
|
+
const result = await launchWithCdp(launchArgs);
|
|
6382
|
+
if (result.success && result.port && result.ideId && !this.cdpManagers.has(result.ideId)) {
|
|
6383
|
+
console.log(import_chalk2.default.cyan(`[launch_ide] Connecting CDP for ${result.ideId} on port ${result.port}...`));
|
|
6384
|
+
const manager = new DaemonCdpManager(result.port, (msg) => {
|
|
6385
|
+
console.log(`[CDP:${result.ideId}] ${msg}`);
|
|
6386
|
+
}, true);
|
|
6387
|
+
const connected = await manager.connect();
|
|
6388
|
+
if (connected) {
|
|
6389
|
+
this.cdpManagers.set(result.ideId, manager);
|
|
6390
|
+
console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${result.ideId} (port ${result.port})`));
|
|
6391
|
+
if (!this.scriptLoaders.has(result.ideId)) {
|
|
6392
|
+
const config = loadConfig();
|
|
6393
|
+
const serverUrl = config.serverUrl || "https://api.adhf.dev";
|
|
6394
|
+
const loader = new DaemonScriptLoader(serverUrl, result.ideId);
|
|
6395
|
+
loader.setToken(config.connectionToken || "");
|
|
6396
|
+
loader.setPlatform(os9.platform());
|
|
6397
|
+
await loader.start().catch(() => {
|
|
6398
|
+
});
|
|
6399
|
+
this.scriptLoaders.set(result.ideId, loader);
|
|
6400
|
+
}
|
|
6401
|
+
}
|
|
6402
|
+
}
|
|
6403
|
+
this.startAgentStreamPolling();
|
|
6404
|
+
return { success: result.success, ...result };
|
|
6405
|
+
}
|
|
6406
|
+
case "detect_ides": {
|
|
6407
|
+
this.detectedIdes = await detectIDEs();
|
|
6408
|
+
return { success: true, ides: this.detectedIdes };
|
|
6409
|
+
}
|
|
6410
|
+
case "launch_cli": {
|
|
6411
|
+
const cliType = data.cliType || "gemini-cli";
|
|
6412
|
+
const dir = data.dir || process.cwd();
|
|
6413
|
+
await this.startCliSession(cliType, dir);
|
|
6414
|
+
return { success: true, started: true };
|
|
6415
|
+
}
|
|
6416
|
+
case "stop_cli": {
|
|
6417
|
+
const cliType = data.cliType || "gemini-cli";
|
|
6418
|
+
const dir = data.dir || "";
|
|
6419
|
+
const key = `${cliType}:${dir}`;
|
|
6420
|
+
const adapter = this.adapters.get(key);
|
|
6421
|
+
if (adapter) {
|
|
6422
|
+
try {
|
|
6423
|
+
adapter.destroy?.();
|
|
6424
|
+
} catch {
|
|
6425
|
+
}
|
|
6426
|
+
this.adapters.delete(key);
|
|
6427
|
+
}
|
|
6428
|
+
return { success: true, stopped: true };
|
|
6429
|
+
}
|
|
6430
|
+
case "restart_session": {
|
|
6431
|
+
const cliType = data.cliType || data.ideType || "gemini-cli";
|
|
6432
|
+
const dir = data.dir || process.cwd();
|
|
6433
|
+
for (const [key, adapter] of this.adapters) {
|
|
6434
|
+
if (key.startsWith(cliType)) {
|
|
6435
|
+
try {
|
|
6436
|
+
adapter.destroy?.();
|
|
6437
|
+
} catch {
|
|
6438
|
+
}
|
|
6439
|
+
this.adapters.delete(key);
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
await this.startCliSession(cliType, dir);
|
|
6443
|
+
return { success: true, restarted: true };
|
|
6444
|
+
}
|
|
6445
|
+
}
|
|
6446
|
+
if (this.commandHandler) {
|
|
6447
|
+
return this.commandHandler.handle(cmdType, data);
|
|
6448
|
+
}
|
|
6449
|
+
return { success: false, error: `Unknown command: ${cmdType}` };
|
|
6450
|
+
} catch (e) {
|
|
6451
|
+
return { success: false, error: e.message };
|
|
6452
|
+
}
|
|
6453
|
+
}
|
|
5010
6454
|
// ─── CLI 세션 관리 ──────────────────────────────
|
|
5011
6455
|
createAdapter(cliType, workingDir) {
|
|
5012
6456
|
if (cliType === "claude-cli" || cliType === "claude-code") {
|
|
@@ -5018,7 +6462,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5018
6462
|
}
|
|
5019
6463
|
}
|
|
5020
6464
|
async startCliSession(cliType, workingDir) {
|
|
5021
|
-
const resolvedDir = workingDir.startsWith("~") ? workingDir.replace(/^~/,
|
|
6465
|
+
const resolvedDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os9.homedir()) : workingDir;
|
|
5022
6466
|
const cliInfo = await detectCLI(cliType);
|
|
5023
6467
|
if (!cliInfo) throw new Error(`${cliType} not found`);
|
|
5024
6468
|
const key = this.getCliKey(cliType, resolvedDir);
|
|
@@ -5033,6 +6477,17 @@ var init_adhdev_daemon = __esm({
|
|
|
5033
6477
|
adapter.setBridge(this.bridge);
|
|
5034
6478
|
}
|
|
5035
6479
|
adapter.setOnStatusChange(() => this.sendUnifiedStatusReport());
|
|
6480
|
+
if (typeof adapter.setOnPtyData === "function") {
|
|
6481
|
+
adapter.setOnPtyData((data) => {
|
|
6482
|
+
const sentViaP2P = this.p2p?.broadcastPtyOutput(adapter.cliType, data);
|
|
6483
|
+
if (!sentViaP2P && this.bridge) {
|
|
6484
|
+
this.bridge.sendMessage("pty_output", {
|
|
6485
|
+
cliType: adapter.cliType,
|
|
6486
|
+
data
|
|
6487
|
+
});
|
|
6488
|
+
}
|
|
6489
|
+
});
|
|
6490
|
+
}
|
|
5036
6491
|
this.adapters.set(key, adapter);
|
|
5037
6492
|
this.lastAgentStatus.set(key, "idle");
|
|
5038
6493
|
console.log(import_chalk2.default.green(` \u2713 CLI started: ${cliInfo.displayName} v${cliInfo.version || "unknown"} in ${resolvedDir}`));
|
|
@@ -5052,28 +6507,44 @@ var init_adhdev_daemon = __esm({
|
|
|
5052
6507
|
// ─── 통합 상태 보고 ─────────────────────────────
|
|
5053
6508
|
startStatusReporting() {
|
|
5054
6509
|
const scheduleNext = () => {
|
|
5055
|
-
const isProcessing = Array.from(this.adapters.values()).some((a) => a.isProcessing());
|
|
5056
|
-
const interval = isProcessing ? 2e3 : 15e3;
|
|
5057
6510
|
this.statusTimer = setTimeout(() => {
|
|
5058
6511
|
this.sendUnifiedStatusReport().catch(() => {
|
|
5059
6512
|
});
|
|
5060
6513
|
scheduleNext();
|
|
5061
|
-
},
|
|
6514
|
+
}, 5e3);
|
|
5062
6515
|
};
|
|
5063
6516
|
scheduleNext();
|
|
5064
6517
|
}
|
|
6518
|
+
/** Throttled status report: 최소 3초 간격 보장 */
|
|
6519
|
+
throttledStatusReport() {
|
|
6520
|
+
const now = Date.now();
|
|
6521
|
+
const elapsed = now - this.lastStatusSentAt;
|
|
6522
|
+
if (elapsed >= 3e3) {
|
|
6523
|
+
this.sendUnifiedStatusReport().catch(() => {
|
|
6524
|
+
});
|
|
6525
|
+
} else if (!this.statusPendingThrottle) {
|
|
6526
|
+
this.statusPendingThrottle = true;
|
|
6527
|
+
setTimeout(() => {
|
|
6528
|
+
this.statusPendingThrottle = false;
|
|
6529
|
+
this.sendUnifiedStatusReport().catch(() => {
|
|
6530
|
+
});
|
|
6531
|
+
}, 3e3 - elapsed);
|
|
6532
|
+
}
|
|
6533
|
+
}
|
|
5065
6534
|
async sendUnifiedStatusReport() {
|
|
5066
6535
|
if (!this.bridge?.isConnected()) return;
|
|
6536
|
+
this.lastStatusSentAt = Date.now();
|
|
6537
|
+
console.log(`[StatusReport] Starting... cdp=${this.cdpManagers.size} scripts=${this.scriptLoaders.size} busy=${this._cdpChatBusy}`);
|
|
5067
6538
|
if (this.cdpManagers.size > 0 && this.scriptLoaders.size > 0 && !this._cdpChatBusy) {
|
|
5068
6539
|
this._cdpChatBusy = true;
|
|
5069
|
-
for (const [ideType,
|
|
5070
|
-
if (!
|
|
6540
|
+
for (const [ideType, cdp2] of this.cdpManagers) {
|
|
6541
|
+
if (!cdp2.isConnected) continue;
|
|
5071
6542
|
const loader = this.scriptLoaders.get(ideType);
|
|
5072
6543
|
if (!loader) continue;
|
|
5073
6544
|
const readChatScript = loader.get("read_chat");
|
|
5074
6545
|
if (!readChatScript) continue;
|
|
5075
6546
|
try {
|
|
5076
|
-
const raw = await
|
|
6547
|
+
const raw = await cdp2.evaluate(readChatScript, 3e4);
|
|
5077
6548
|
if (raw && typeof raw === "object") {
|
|
5078
6549
|
let { activeModal } = raw;
|
|
5079
6550
|
if (activeModal) {
|
|
@@ -5085,9 +6556,54 @@ var init_adhdev_daemon = __esm({
|
|
|
5085
6556
|
buttons: (activeModal.buttons ?? []).filter((t) => t.length < 30)
|
|
5086
6557
|
};
|
|
5087
6558
|
}
|
|
5088
|
-
|
|
6559
|
+
const msgCount = raw.messages?.length || 0;
|
|
6560
|
+
console.log(`[StatusReport] ${ideType} read_chat: ${msgCount} msgs, status=${raw.status}, id=${raw.id?.substring(0, 15)}`);
|
|
6561
|
+
if (msgCount > 0) {
|
|
6562
|
+
const prevChat = this._cachedActiveChatMap.get(ideType);
|
|
6563
|
+
const prevMsgs = prevChat?.messages || [];
|
|
6564
|
+
const prevByHash = /* @__PURE__ */ new Map();
|
|
6565
|
+
for (const pm of prevMsgs) {
|
|
6566
|
+
const h = `${pm.role}:${(pm.content || "").slice(0, 100)}`;
|
|
6567
|
+
if (pm.receivedAt) prevByHash.set(h, pm.receivedAt);
|
|
6568
|
+
}
|
|
6569
|
+
const now2 = Date.now();
|
|
6570
|
+
for (const msg of raw.messages) {
|
|
6571
|
+
const h = `${msg.role}:${(msg.content || "").slice(0, 100)}`;
|
|
6572
|
+
msg.receivedAt = prevByHash.get(h) || now2;
|
|
6573
|
+
}
|
|
6574
|
+
this._cachedActiveChatMap.set(ideType, { ...raw, activeModal });
|
|
6575
|
+
} else {
|
|
6576
|
+
try {
|
|
6577
|
+
const inlineResult = await cdp2.evaluate(`(() => {
|
|
6578
|
+
const container = document.querySelector('.flex.flex-col.gap-2\\\\.5');
|
|
6579
|
+
if (!container) return { messages: [], status: 'idle', id: 'cascade' };
|
|
6580
|
+
const msgs = [];
|
|
6581
|
+
for (const child of container.children) {
|
|
6582
|
+
if ((child.className || '').includes('mark-js-ignore')) continue;
|
|
6583
|
+
if (child.offsetHeight < 10) continue;
|
|
6584
|
+
const inner = child.children[0];
|
|
6585
|
+
if (!inner) continue;
|
|
6586
|
+
const isUser = inner.className?.includes('flex-row') || !!inner.querySelector('.justify-end');
|
|
6587
|
+
const role = isUser ? 'user' : 'assistant';
|
|
6588
|
+
const text = child.textContent?.trim() || '';
|
|
6589
|
+
if (text.length < 1) continue;
|
|
6590
|
+
msgs.push({ id: 'msg_' + msgs.length, role, content: text.substring(0, 6000), index: msgs.length });
|
|
6591
|
+
}
|
|
6592
|
+
return { messages: msgs, status: 'idle', id: 'cascade' };
|
|
6593
|
+
})()`, 1e4);
|
|
6594
|
+
console.log(`[StatusReport] ${ideType} inline fallback: ${inlineResult?.messages?.length || 0} msgs`);
|
|
6595
|
+
if (inlineResult?.messages?.length > 0) {
|
|
6596
|
+
this._cachedActiveChatMap.set(ideType, { ...inlineResult, activeModal });
|
|
6597
|
+
}
|
|
6598
|
+
} catch (e2) {
|
|
6599
|
+
console.log(`[StatusReport] ${ideType} inline error: ${e2.message}`);
|
|
6600
|
+
}
|
|
6601
|
+
}
|
|
6602
|
+
} else {
|
|
6603
|
+
console.log(`[StatusReport] ${ideType} read_chat: null/non-object result`);
|
|
5089
6604
|
}
|
|
5090
|
-
} catch {
|
|
6605
|
+
} catch (e1) {
|
|
6606
|
+
console.log(`[StatusReport] ${ideType} read_chat error: ${e1.message}`);
|
|
5091
6607
|
}
|
|
5092
6608
|
}
|
|
5093
6609
|
this._cdpChatBusy = false;
|
|
@@ -5138,6 +6654,29 @@ var init_adhdev_daemon = __esm({
|
|
|
5138
6654
|
cdpConnected: isMainCdpIde ? this.cdpManagers.get(ext.ideType.toLowerCase())?.isConnected || false : false
|
|
5139
6655
|
};
|
|
5140
6656
|
});
|
|
6657
|
+
const managedIdeTypes = new Set(managedIdes.map((ide) => ide.ideType.toLowerCase()));
|
|
6658
|
+
for (const [ideType, cdp2] of this.cdpManagers) {
|
|
6659
|
+
if (!cdp2.isConnected) continue;
|
|
6660
|
+
if (managedIdeTypes.has(ideType)) continue;
|
|
6661
|
+
if (enabledIdesFilter.length > 0 && !enabledIdesFilter.includes(ideType)) continue;
|
|
6662
|
+
const cachedChat = this._cachedActiveChatMap.get(ideType);
|
|
6663
|
+
const cdpStreams = this._cachedAgentStreamsMap.get(ideType) || [];
|
|
6664
|
+
const machineHash = require("crypto").createHash("md5").update(os9.hostname() + ideType).digest("hex").slice(0, 8);
|
|
6665
|
+
console.log(`[StatusReport] Adding CDP-only IDE: ${ideType} (no bridge ext, chat=${cachedChat?.messages?.length || 0} msgs)`);
|
|
6666
|
+
managedIdes.push({
|
|
6667
|
+
ideType,
|
|
6668
|
+
ideVersion: "",
|
|
6669
|
+
instanceId: `${ideType}_${machineHash}`,
|
|
6670
|
+
workspaceFolders: [],
|
|
6671
|
+
activeFile: null,
|
|
6672
|
+
terminals: 0,
|
|
6673
|
+
aiAgents: [],
|
|
6674
|
+
activeChat: cachedChat || null,
|
|
6675
|
+
chats: [],
|
|
6676
|
+
agentStreams: cdpStreams,
|
|
6677
|
+
cdpConnected: true
|
|
6678
|
+
});
|
|
6679
|
+
}
|
|
5141
6680
|
const managedClis = [];
|
|
5142
6681
|
for (const [key, adapter] of this.adapters.entries()) {
|
|
5143
6682
|
const adapterStatus = adapter.getStatus();
|
|
@@ -5148,7 +6687,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5148
6687
|
this.generatingStartedAt.set(key, now);
|
|
5149
6688
|
this.bridge?.sendMessage("status_event", {
|
|
5150
6689
|
event: "agent:generating_started",
|
|
5151
|
-
chatTitle: `${adapter.cliName}
|
|
6690
|
+
chatTitle: `${adapter.cliName} \xB7 ${adapter.workingDir.split("/").filter(Boolean).pop() || "session"}`,
|
|
5152
6691
|
timestamp: now
|
|
5153
6692
|
});
|
|
5154
6693
|
} else if (lastStatus === "generating" && cliStatus === "idle") {
|
|
@@ -5156,7 +6695,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5156
6695
|
const duration = startedAt ? Math.round((now - startedAt) / 1e3) : 0;
|
|
5157
6696
|
this.bridge?.sendMessage("status_event", {
|
|
5158
6697
|
event: "agent:generating_completed",
|
|
5159
|
-
chatTitle: `${adapter.cliName}
|
|
6698
|
+
chatTitle: `${adapter.cliName} \xB7 ${adapter.workingDir.split("/").filter(Boolean).pop() || "session"}`,
|
|
5160
6699
|
duration,
|
|
5161
6700
|
timestamp: now
|
|
5162
6701
|
});
|
|
@@ -5182,7 +6721,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5182
6721
|
activeChat: {
|
|
5183
6722
|
id: key,
|
|
5184
6723
|
status: cliStatus,
|
|
5185
|
-
title: `${adapter.cliName}
|
|
6724
|
+
title: `${adapter.cliName} \xB7 ${adapter.workingDir.split("/").filter(Boolean).pop() || "session"}`,
|
|
5186
6725
|
messages: cliMessages,
|
|
5187
6726
|
inputContent: "",
|
|
5188
6727
|
activeModal: adapterStatus.activeModal
|
|
@@ -5196,15 +6735,15 @@ var init_adhdev_daemon = __esm({
|
|
|
5196
6735
|
// Machine 정보
|
|
5197
6736
|
daemonMode: true,
|
|
5198
6737
|
machine: {
|
|
5199
|
-
hostname:
|
|
5200
|
-
platform:
|
|
5201
|
-
release:
|
|
5202
|
-
arch:
|
|
5203
|
-
cpus:
|
|
5204
|
-
totalMem:
|
|
5205
|
-
freeMem:
|
|
5206
|
-
loadavg:
|
|
5207
|
-
uptime:
|
|
6738
|
+
hostname: os9.hostname(),
|
|
6739
|
+
platform: os9.platform(),
|
|
6740
|
+
release: os9.release(),
|
|
6741
|
+
arch: os9.arch(),
|
|
6742
|
+
cpus: os9.cpus().length,
|
|
6743
|
+
totalMem: os9.totalmem(),
|
|
6744
|
+
freeMem: os9.freemem(),
|
|
6745
|
+
loadavg: os9.loadavg(),
|
|
6746
|
+
uptime: os9.uptime()
|
|
5208
6747
|
},
|
|
5209
6748
|
// 관리 중인 IDE/CLI (계층 데이터)
|
|
5210
6749
|
managedIdes,
|
|
@@ -5227,7 +6766,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5227
6766
|
timestamp: now,
|
|
5228
6767
|
// ─── Legacy compat (서버 기존 로직용, 점진적 제거 예정) ───
|
|
5229
6768
|
activeFile: extSummary.activeFile,
|
|
5230
|
-
workspaceFolders: extSummary.workspaceFolders.length > 0 ? extSummary.workspaceFolders : Array.from(this.adapters.values()).map((a) => ({ name:
|
|
6769
|
+
workspaceFolders: extSummary.workspaceFolders.length > 0 ? extSummary.workspaceFolders : Array.from(this.adapters.values()).map((a) => ({ name: path5.basename(a.workingDir), path: a.workingDir })),
|
|
5231
6770
|
terminals: extSummary.terminals,
|
|
5232
6771
|
// Legacy aiAgents: managedIdes에서 각 IDE의 aiAgents를 통합
|
|
5233
6772
|
aiAgents: [
|
|
@@ -5246,19 +6785,49 @@ var init_adhdev_daemon = __esm({
|
|
|
5246
6785
|
],
|
|
5247
6786
|
connectedExtensions: extSummary.connectedIdes,
|
|
5248
6787
|
system: {
|
|
5249
|
-
platform:
|
|
5250
|
-
release:
|
|
5251
|
-
arch:
|
|
5252
|
-
cpus:
|
|
5253
|
-
totalMem:
|
|
5254
|
-
freeMem:
|
|
5255
|
-
loadavg:
|
|
5256
|
-
uptime:
|
|
5257
|
-
hostname:
|
|
6788
|
+
platform: os9.platform(),
|
|
6789
|
+
release: os9.release(),
|
|
6790
|
+
arch: os9.arch(),
|
|
6791
|
+
cpus: os9.cpus().length,
|
|
6792
|
+
totalMem: os9.totalmem(),
|
|
6793
|
+
freeMem: os9.freemem(),
|
|
6794
|
+
loadavg: os9.loadavg(),
|
|
6795
|
+
uptime: os9.uptime(),
|
|
6796
|
+
hostname: os9.hostname()
|
|
5258
6797
|
}
|
|
5259
6798
|
};
|
|
5260
|
-
|
|
5261
|
-
|
|
6799
|
+
const { timestamp: _ts, system: _sys, ...hashTarget } = payload;
|
|
6800
|
+
if (hashTarget.machine) {
|
|
6801
|
+
const { freeMem: _f, loadavg: _l, uptime: _u, ...stableMachine } = hashTarget.machine;
|
|
6802
|
+
hashTarget.machine = stableMachine;
|
|
6803
|
+
}
|
|
6804
|
+
const statusHash = this.simpleHash(JSON.stringify(hashTarget));
|
|
6805
|
+
if (statusHash !== this.lastP2PStatusHash) {
|
|
6806
|
+
this.lastP2PStatusHash = statusHash;
|
|
6807
|
+
this.p2p?.sendStatus(payload);
|
|
6808
|
+
}
|
|
6809
|
+
const p2pConnected = (this.p2p?.connectedPeerCount || 0) > 0;
|
|
6810
|
+
this.statusReportCount = (this.statusReportCount || 0) + 1;
|
|
6811
|
+
const forceFullSync = this.statusReportCount % 12 === 0;
|
|
6812
|
+
if (p2pConnected && this.lastStatusSentFull && !forceFullSync) {
|
|
6813
|
+
this.bridge.sendMessage("status_heartbeat", {
|
|
6814
|
+
timestamp: now,
|
|
6815
|
+
p2pConnected: true,
|
|
6816
|
+
p2pPeers: this.p2p?.connectedPeerCount || 0
|
|
6817
|
+
});
|
|
6818
|
+
} else {
|
|
6819
|
+
this.bridge.sendMessage("status_report", payload);
|
|
6820
|
+
this.lastStatusSentFull = true;
|
|
6821
|
+
}
|
|
6822
|
+
}
|
|
6823
|
+
/** Fast string hash (FNV-1a 32-bit) */
|
|
6824
|
+
simpleHash(s) {
|
|
6825
|
+
let h = 2166136261;
|
|
6826
|
+
for (let i = 0; i < s.length; i++) {
|
|
6827
|
+
h ^= s.charCodeAt(i);
|
|
6828
|
+
h = h * 16777619 >>> 0;
|
|
6829
|
+
}
|
|
6830
|
+
return h.toString(36);
|
|
5262
6831
|
}
|
|
5263
6832
|
sendResult(msg, success, extra) {
|
|
5264
6833
|
if (!msg.id) return;
|
|
@@ -5320,17 +6889,41 @@ var init_adhdev_daemon = __esm({
|
|
|
5320
6889
|
return this.cdpManagers.get(ideType.toLowerCase()) || null;
|
|
5321
6890
|
}
|
|
5322
6891
|
/** Agent stream polling 시작 (idempotent — 이미 시작됐으면 무시) */
|
|
6892
|
+
_agentStreamCdpIdeType = null;
|
|
6893
|
+
// agent가 연결된 IDE
|
|
5323
6894
|
startAgentStreamPolling() {
|
|
5324
6895
|
if (this.agentStreamTimer) return;
|
|
5325
6896
|
this.agentStreamTimer = setInterval(async () => {
|
|
5326
6897
|
if (!this.agentStreamManager || this.cdpManagers.size === 0) return;
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
6898
|
+
if (this._agentStreamCdpIdeType) {
|
|
6899
|
+
const cdp2 = this.cdpManagers.get(this._agentStreamCdpIdeType);
|
|
6900
|
+
if (cdp2?.isConnected) {
|
|
6901
|
+
try {
|
|
6902
|
+
await this.agentStreamManager.syncAgentSessions(cdp2);
|
|
6903
|
+
const streams = await this.agentStreamManager.collectAgentStreams(cdp2);
|
|
6904
|
+
this._cachedAgentStreamsMap.set(this._agentStreamCdpIdeType, streams);
|
|
6905
|
+
} catch {
|
|
6906
|
+
}
|
|
6907
|
+
return;
|
|
6908
|
+
}
|
|
6909
|
+
this._agentStreamCdpIdeType = null;
|
|
6910
|
+
}
|
|
6911
|
+
if (!this.agentStreamManager.activeAgentType) {
|
|
6912
|
+
for (const [ideType, cdp2] of this.cdpManagers) {
|
|
6913
|
+
if (!cdp2.isConnected) continue;
|
|
6914
|
+
try {
|
|
6915
|
+
const discovered = await cdp2.discoverAgentWebviews();
|
|
6916
|
+
if (discovered.length > 0) {
|
|
6917
|
+
this._agentStreamCdpIdeType = ideType;
|
|
6918
|
+
await this.agentStreamManager.switchActiveAgent(cdp2, discovered[0].agentType);
|
|
6919
|
+
console.log(`[AgentStream] Auto-activated: ${discovered[0].agentType} (${ideType})`);
|
|
6920
|
+
await this.agentStreamManager.syncAgentSessions(cdp2);
|
|
6921
|
+
const streams = await this.agentStreamManager.collectAgentStreams(cdp2);
|
|
6922
|
+
this._cachedAgentStreamsMap.set(ideType, streams);
|
|
6923
|
+
return;
|
|
6924
|
+
}
|
|
6925
|
+
} catch {
|
|
6926
|
+
}
|
|
5334
6927
|
}
|
|
5335
6928
|
}
|
|
5336
6929
|
}, 5e3);
|
|
@@ -5357,7 +6950,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5357
6950
|
const filteredPorts = enabledIdes.length > 0 ? portsToTry.filter((p) => enabledIdes.includes(p.ide)) : portsToTry;
|
|
5358
6951
|
for (const { port, ide } of filteredPorts) {
|
|
5359
6952
|
const manager = new DaemonCdpManager(port, (msg) => {
|
|
5360
|
-
console.log(
|
|
6953
|
+
console.log(`[CDP:${ide}] ${msg}`);
|
|
5361
6954
|
}, true);
|
|
5362
6955
|
const connected = await manager.connect();
|
|
5363
6956
|
if (connected) {
|
|
@@ -5400,27 +6993,27 @@ __export(cli_daemon_exports, {
|
|
|
5400
6993
|
uninstallService: () => uninstallService
|
|
5401
6994
|
});
|
|
5402
6995
|
function getPidFile(workingDir, cliType) {
|
|
5403
|
-
const absPath =
|
|
6996
|
+
const absPath = path6.resolve(workingDir);
|
|
5404
6997
|
const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
|
|
5405
|
-
const dir =
|
|
5406
|
-
if (!
|
|
5407
|
-
return
|
|
6998
|
+
const dir = path6.join(os10.homedir(), ".adhdev");
|
|
6999
|
+
if (!fs5.existsSync(dir)) fs5.mkdirSync(dir, { recursive: true });
|
|
7000
|
+
return path6.join(dir, `bridge-${cliType}-${hash}.pid`);
|
|
5408
7001
|
}
|
|
5409
7002
|
function writePidFile(pid, workingDir, cliType) {
|
|
5410
7003
|
const pidFile = getPidFile(workingDir, cliType);
|
|
5411
|
-
|
|
7004
|
+
fs5.writeFileSync(pidFile, String(pid), "utf-8");
|
|
5412
7005
|
}
|
|
5413
7006
|
function removePidFile(workingDir, cliType) {
|
|
5414
7007
|
try {
|
|
5415
|
-
|
|
7008
|
+
fs5.unlinkSync(getPidFile(workingDir, cliType));
|
|
5416
7009
|
} catch {
|
|
5417
7010
|
}
|
|
5418
7011
|
}
|
|
5419
7012
|
function isDaemonRunning2(workingDir = process.cwd(), cliType = "gemini-cli") {
|
|
5420
7013
|
const pidFile = getPidFile(workingDir, cliType);
|
|
5421
7014
|
try {
|
|
5422
|
-
if (!
|
|
5423
|
-
const pid = parseInt(
|
|
7015
|
+
if (!fs5.existsSync(pidFile)) return false;
|
|
7016
|
+
const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
|
|
5424
7017
|
process.kill(pid, 0);
|
|
5425
7018
|
return true;
|
|
5426
7019
|
} catch {
|
|
@@ -5431,8 +7024,8 @@ function isDaemonRunning2(workingDir = process.cwd(), cliType = "gemini-cli") {
|
|
|
5431
7024
|
function stopDaemon2(workingDir = process.cwd(), cliType = "gemini-cli") {
|
|
5432
7025
|
const pidFile = getPidFile(workingDir, cliType);
|
|
5433
7026
|
try {
|
|
5434
|
-
if (!
|
|
5435
|
-
const pid = parseInt(
|
|
7027
|
+
if (!fs5.existsSync(pidFile)) return false;
|
|
7028
|
+
const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
|
|
5436
7029
|
process.kill(pid, "SIGTERM");
|
|
5437
7030
|
removePidFile(workingDir, cliType);
|
|
5438
7031
|
return true;
|
|
@@ -5442,40 +7035,40 @@ function stopDaemon2(workingDir = process.cwd(), cliType = "gemini-cli") {
|
|
|
5442
7035
|
}
|
|
5443
7036
|
}
|
|
5444
7037
|
function getServiceLabel(workingDir, cliType) {
|
|
5445
|
-
const absPath =
|
|
7038
|
+
const absPath = path6.resolve(workingDir);
|
|
5446
7039
|
const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
|
|
5447
7040
|
return `dev.adhf.cli-bridge.${cliType}.${hash}`;
|
|
5448
7041
|
}
|
|
5449
7042
|
function getLogFile(workingDir, cliType, isErr = false) {
|
|
5450
|
-
const absPath =
|
|
7043
|
+
const absPath = path6.resolve(workingDir);
|
|
5451
7044
|
const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
|
|
5452
|
-
if (!
|
|
5453
|
-
return
|
|
7045
|
+
if (!fs5.existsSync(LOG_DIR2)) fs5.mkdirSync(LOG_DIR2, { recursive: true });
|
|
7046
|
+
return path6.join(LOG_DIR2, `bridge-${cliType}-${hash}${isErr ? ".err" : ""}.log`);
|
|
5454
7047
|
}
|
|
5455
7048
|
function getServicePaths(label) {
|
|
5456
|
-
const platform9 =
|
|
7049
|
+
const platform9 = os10.platform();
|
|
5457
7050
|
if (platform9 === "darwin") {
|
|
5458
7051
|
return {
|
|
5459
|
-
plist:
|
|
7052
|
+
plist: path6.join(os10.homedir(), "Library", "LaunchAgents", `${label}.plist`)
|
|
5460
7053
|
};
|
|
5461
7054
|
} else if (platform9 === "linux") {
|
|
5462
7055
|
return {
|
|
5463
|
-
systemd:
|
|
7056
|
+
systemd: path6.join(os10.homedir(), ".config", "systemd", "user", `${label}.service`)
|
|
5464
7057
|
};
|
|
5465
7058
|
} else if (platform9 === "win32") {
|
|
5466
7059
|
return {
|
|
5467
|
-
startup:
|
|
7060
|
+
startup: path6.join(os10.homedir(), "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs", "Startup", `${label}.vbs`)
|
|
5468
7061
|
};
|
|
5469
7062
|
}
|
|
5470
7063
|
return {};
|
|
5471
7064
|
}
|
|
5472
7065
|
function installService(options = {}) {
|
|
5473
|
-
const platform9 =
|
|
7066
|
+
const platform9 = os10.platform();
|
|
5474
7067
|
const cliType = options.cliType || "gemini-cli";
|
|
5475
|
-
const workingDir = options.workingDir ||
|
|
7068
|
+
const workingDir = options.workingDir || os10.homedir();
|
|
5476
7069
|
const adhdevBin = process.argv[1];
|
|
5477
7070
|
const nodeBin = process.execPath;
|
|
5478
|
-
if (!
|
|
7071
|
+
if (!fs5.existsSync(LOG_DIR2)) fs5.mkdirSync(LOG_DIR2, { recursive: true });
|
|
5479
7072
|
const label = getServiceLabel(workingDir, cliType);
|
|
5480
7073
|
const logFile2 = getLogFile(workingDir, cliType);
|
|
5481
7074
|
const errLogFile = getLogFile(workingDir, cliType, true);
|
|
@@ -5530,17 +7123,17 @@ function installMacService(nodeBin, adhdevBin, cliType, workingDir, label, logFi
|
|
|
5530
7123
|
<key>PATH</key>
|
|
5531
7124
|
<string>${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}</string>
|
|
5532
7125
|
<key>HOME</key>
|
|
5533
|
-
<string>${
|
|
7126
|
+
<string>${os10.homedir()}</string>
|
|
5534
7127
|
</dict>
|
|
5535
7128
|
</dict>
|
|
5536
7129
|
</plist>`;
|
|
5537
|
-
const agentsDir =
|
|
5538
|
-
if (!
|
|
7130
|
+
const agentsDir = path6.dirname(plistPath);
|
|
7131
|
+
if (!fs5.existsSync(agentsDir)) fs5.mkdirSync(agentsDir, { recursive: true });
|
|
5539
7132
|
try {
|
|
5540
7133
|
execSync8(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "pipe" });
|
|
5541
7134
|
} catch {
|
|
5542
7135
|
}
|
|
5543
|
-
|
|
7136
|
+
fs5.writeFileSync(plistPath, plistContent, "utf-8");
|
|
5544
7137
|
try {
|
|
5545
7138
|
execSync8(`launchctl load "${plistPath}"`, { stdio: "pipe" });
|
|
5546
7139
|
} catch (e) {
|
|
@@ -5566,14 +7159,14 @@ RestartSec=10
|
|
|
5566
7159
|
StandardOutput=append:${logFile2}
|
|
5567
7160
|
StandardError=append:${errLogFile}
|
|
5568
7161
|
Environment=PATH=${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}
|
|
5569
|
-
Environment=HOME=${
|
|
7162
|
+
Environment=HOME=${os10.homedir()}
|
|
5570
7163
|
|
|
5571
7164
|
[Install]
|
|
5572
7165
|
WantedBy=default.target
|
|
5573
7166
|
`;
|
|
5574
|
-
const serviceDir =
|
|
5575
|
-
if (!
|
|
5576
|
-
|
|
7167
|
+
const serviceDir = path6.dirname(servicePath);
|
|
7168
|
+
if (!fs5.existsSync(serviceDir)) fs5.mkdirSync(serviceDir, { recursive: true });
|
|
7169
|
+
fs5.writeFileSync(servicePath, serviceContent, "utf-8");
|
|
5577
7170
|
try {
|
|
5578
7171
|
execSync8("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
5579
7172
|
execSync8(`systemctl --user enable ${label}.service`, { stdio: "pipe" });
|
|
@@ -5590,9 +7183,9 @@ function installWindowsService(nodeBin, adhdevBin, cliType, workingDir, label) {
|
|
|
5590
7183
|
const vbsContent = `Set WshShell = CreateObject("WScript.Shell")
|
|
5591
7184
|
WshShell.Run """${nodeBin}"" ""${adhdevBin}"" bridge --foreground --cli ${cliType} --dir ""${workingDir}""", 0, False
|
|
5592
7185
|
`;
|
|
5593
|
-
const startupDir =
|
|
5594
|
-
if (!
|
|
5595
|
-
|
|
7186
|
+
const startupDir = path6.dirname(startupPath);
|
|
7187
|
+
if (!fs5.existsSync(startupDir)) fs5.mkdirSync(startupDir, { recursive: true });
|
|
7188
|
+
fs5.writeFileSync(startupPath, vbsContent, "utf-8");
|
|
5596
7189
|
try {
|
|
5597
7190
|
const { execSync: execSync8 } = require("child_process");
|
|
5598
7191
|
execSync8(`wscript "${startupPath}"`, { stdio: "pipe" });
|
|
@@ -5601,9 +7194,9 @@ WshShell.Run """${nodeBin}"" ""${adhdevBin}"" bridge --foreground --cli ${cliTyp
|
|
|
5601
7194
|
return true;
|
|
5602
7195
|
}
|
|
5603
7196
|
function uninstallService(options = {}) {
|
|
5604
|
-
const platform9 =
|
|
7197
|
+
const platform9 = os10.platform();
|
|
5605
7198
|
const cliType = options.cliType || "gemini-cli";
|
|
5606
|
-
const workingDir = options.workingDir ||
|
|
7199
|
+
const workingDir = options.workingDir || os10.homedir();
|
|
5607
7200
|
const label = getServiceLabel(workingDir, cliType);
|
|
5608
7201
|
const paths = getServicePaths(label);
|
|
5609
7202
|
const { execSync: execSync8 } = require("child_process");
|
|
@@ -5613,7 +7206,7 @@ function uninstallService(options = {}) {
|
|
|
5613
7206
|
} catch {
|
|
5614
7207
|
}
|
|
5615
7208
|
try {
|
|
5616
|
-
if (
|
|
7209
|
+
if (fs5.existsSync(paths.plist)) fs5.unlinkSync(paths.plist);
|
|
5617
7210
|
} catch {
|
|
5618
7211
|
}
|
|
5619
7212
|
} else if (platform9 === "linux" && paths.systemd) {
|
|
@@ -5623,7 +7216,7 @@ function uninstallService(options = {}) {
|
|
|
5623
7216
|
} catch {
|
|
5624
7217
|
}
|
|
5625
7218
|
try {
|
|
5626
|
-
if (
|
|
7219
|
+
if (fs5.existsSync(paths.systemd)) fs5.unlinkSync(paths.systemd);
|
|
5627
7220
|
} catch {
|
|
5628
7221
|
}
|
|
5629
7222
|
try {
|
|
@@ -5632,25 +7225,25 @@ function uninstallService(options = {}) {
|
|
|
5632
7225
|
}
|
|
5633
7226
|
} else if (platform9 === "win32" && paths.startup) {
|
|
5634
7227
|
try {
|
|
5635
|
-
if (
|
|
7228
|
+
if (fs5.existsSync(paths.startup)) fs5.unlinkSync(paths.startup);
|
|
5636
7229
|
} catch {
|
|
5637
7230
|
}
|
|
5638
7231
|
}
|
|
5639
7232
|
removePidFile(workingDir, cliType);
|
|
5640
7233
|
return true;
|
|
5641
7234
|
}
|
|
5642
|
-
function isServiceInstalled(workingDir =
|
|
7235
|
+
function isServiceInstalled(workingDir = os10.homedir(), cliType = "gemini-cli") {
|
|
5643
7236
|
const label = getServiceLabel(workingDir, cliType);
|
|
5644
7237
|
const paths = getServicePaths(label);
|
|
5645
|
-
if (paths.plist) return
|
|
5646
|
-
if (paths.systemd) return
|
|
5647
|
-
if (paths.startup) return
|
|
7238
|
+
if (paths.plist) return fs5.existsSync(paths.plist);
|
|
7239
|
+
if (paths.systemd) return fs5.existsSync(paths.systemd);
|
|
7240
|
+
if (paths.startup) return fs5.existsSync(paths.startup);
|
|
5648
7241
|
return false;
|
|
5649
7242
|
}
|
|
5650
|
-
function getLogPath(workingDir =
|
|
7243
|
+
function getLogPath(workingDir = os10.homedir(), cliType = "gemini-cli") {
|
|
5651
7244
|
return getLogFile(workingDir, cliType);
|
|
5652
7245
|
}
|
|
5653
|
-
var
|
|
7246
|
+
var os10, fs5, path6, crypto3, import_chalk3, DANGEROUS_PATTERNS2, CliDaemon, LOG_DIR2;
|
|
5654
7247
|
var init_cli_daemon = __esm({
|
|
5655
7248
|
"src/cli-daemon.ts"() {
|
|
5656
7249
|
"use strict";
|
|
@@ -5660,9 +7253,9 @@ var init_cli_daemon = __esm({
|
|
|
5660
7253
|
init_codex_cli();
|
|
5661
7254
|
init_cli_detector();
|
|
5662
7255
|
init_config();
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
7256
|
+
os10 = __toESM(require("os"));
|
|
7257
|
+
fs5 = __toESM(require("fs"));
|
|
7258
|
+
path6 = __toESM(require("path"));
|
|
5666
7259
|
crypto3 = __toESM(require("crypto"));
|
|
5667
7260
|
import_chalk3 = __toESM(require("chalk"));
|
|
5668
7261
|
init_detector();
|
|
@@ -5731,8 +7324,8 @@ var init_cli_daemon = __esm({
|
|
|
5731
7324
|
this.adapter.setOnStatusChange(() => {
|
|
5732
7325
|
this.sendStatusReport();
|
|
5733
7326
|
});
|
|
5734
|
-
const machineId =
|
|
5735
|
-
const dirHash = crypto3.createHash("md5").update(
|
|
7327
|
+
const machineId = os10.hostname().replace(/[^a-zA-Z0-9]/g, "_");
|
|
7328
|
+
const dirHash = crypto3.createHash("md5").update(path6.resolve(workingDir)).digest("hex").slice(0, 4);
|
|
5736
7329
|
const instanceId = `${cliType}_${machineId}_${dirHash}`;
|
|
5737
7330
|
this.bridge = new CliBridgeConnection({
|
|
5738
7331
|
serverUrl: options.serverUrl || config.serverUrl,
|
|
@@ -5740,7 +7333,7 @@ var init_cli_daemon = __esm({
|
|
|
5740
7333
|
cliInfo: {
|
|
5741
7334
|
type: cliType,
|
|
5742
7335
|
version: cliInfo.version || "unknown",
|
|
5743
|
-
platform:
|
|
7336
|
+
platform: os10.platform(),
|
|
5744
7337
|
instanceId
|
|
5745
7338
|
}
|
|
5746
7339
|
});
|
|
@@ -5811,11 +7404,11 @@ var init_cli_daemon = __esm({
|
|
|
5811
7404
|
}
|
|
5812
7405
|
case "list_files": {
|
|
5813
7406
|
const targetDir = args?.dir || this.adapter?.workingDir || process.cwd();
|
|
5814
|
-
const entries =
|
|
7407
|
+
const entries = fs5.readdirSync(targetDir).map((f) => {
|
|
5815
7408
|
try {
|
|
5816
7409
|
return {
|
|
5817
7410
|
name: f,
|
|
5818
|
-
isDirectory:
|
|
7411
|
+
isDirectory: fs5.statSync(path6.join(targetDir, f)).isDirectory()
|
|
5819
7412
|
};
|
|
5820
7413
|
} catch {
|
|
5821
7414
|
return null;
|
|
@@ -5927,14 +7520,14 @@ var init_cli_daemon = __esm({
|
|
|
5927
7520
|
this.sendStatusReport();
|
|
5928
7521
|
});
|
|
5929
7522
|
if (this.bridge) {
|
|
5930
|
-
const machineId =
|
|
5931
|
-
const dirHash = crypto3.createHash("md5").update(
|
|
7523
|
+
const machineId = os10.hostname().replace(/[^a-zA-Z0-9]/g, "_");
|
|
7524
|
+
const dirHash = crypto3.createHash("md5").update(path6.resolve(workingDir)).digest("hex").slice(0, 4);
|
|
5932
7525
|
const config2 = this.bridge.options;
|
|
5933
7526
|
config2.cliInfo = {
|
|
5934
7527
|
...config2.cliInfo,
|
|
5935
7528
|
type: cliType,
|
|
5936
7529
|
version: cliInfo.version || "unknown",
|
|
5937
|
-
platform:
|
|
7530
|
+
platform: os10.platform(),
|
|
5938
7531
|
instanceId: `${cliType}_${machineId}_${dirHash}`
|
|
5939
7532
|
};
|
|
5940
7533
|
}
|
|
@@ -6001,13 +7594,13 @@ var init_cli_daemon = __esm({
|
|
|
6001
7594
|
const payload = {
|
|
6002
7595
|
activeFile: null,
|
|
6003
7596
|
workspaceFolders: [{
|
|
6004
|
-
name:
|
|
7597
|
+
name: path6.basename(adapterStatus.workingDir),
|
|
6005
7598
|
path: adapterStatus.workingDir
|
|
6006
7599
|
}],
|
|
6007
7600
|
currentConfig: {
|
|
6008
7601
|
cli: this.bridge?.getCliInfo()?.type || "unknown",
|
|
6009
7602
|
dir: adapterStatus.workingDir,
|
|
6010
|
-
homeDir:
|
|
7603
|
+
homeDir: os10.homedir()
|
|
6011
7604
|
},
|
|
6012
7605
|
terminals: 1,
|
|
6013
7606
|
aiAgents: [{
|
|
@@ -6026,19 +7619,19 @@ var init_cli_daemon = __esm({
|
|
|
6026
7619
|
currentConfig: {
|
|
6027
7620
|
cli: this.bridge?.getCliInfo()?.type || "unknown",
|
|
6028
7621
|
dir: adapterStatus.workingDir,
|
|
6029
|
-
homeDir:
|
|
7622
|
+
homeDir: os10.homedir()
|
|
6030
7623
|
}
|
|
6031
7624
|
}],
|
|
6032
7625
|
system: {
|
|
6033
|
-
platform:
|
|
6034
|
-
release:
|
|
6035
|
-
arch:
|
|
6036
|
-
cpus:
|
|
6037
|
-
totalMem:
|
|
6038
|
-
freeMem:
|
|
6039
|
-
loadavg:
|
|
6040
|
-
uptime:
|
|
6041
|
-
hostname:
|
|
7626
|
+
platform: os10.platform(),
|
|
7627
|
+
release: os10.release(),
|
|
7628
|
+
arch: os10.arch(),
|
|
7629
|
+
cpus: os10.cpus().length,
|
|
7630
|
+
totalMem: os10.totalmem(),
|
|
7631
|
+
freeMem: os10.freemem(),
|
|
7632
|
+
loadavg: os10.loadavg(),
|
|
7633
|
+
uptime: os10.uptime(),
|
|
7634
|
+
hostname: os10.hostname()
|
|
6042
7635
|
},
|
|
6043
7636
|
detectedIdes: this.detectedIdes,
|
|
6044
7637
|
activeChat: {
|
|
@@ -6077,7 +7670,7 @@ var init_cli_daemon = __esm({
|
|
|
6077
7670
|
process.exit(0);
|
|
6078
7671
|
}
|
|
6079
7672
|
};
|
|
6080
|
-
|
|
7673
|
+
LOG_DIR2 = path6.join(os10.homedir(), ".adhdev", "logs");
|
|
6081
7674
|
}
|
|
6082
7675
|
});
|
|
6083
7676
|
|
|
@@ -6220,8 +7813,8 @@ async function installExtension(ide, extension) {
|
|
|
6220
7813
|
const res = await fetch(extension.vsixUrl);
|
|
6221
7814
|
if (res.ok) {
|
|
6222
7815
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
6223
|
-
const
|
|
6224
|
-
|
|
7816
|
+
const fs6 = await import("fs");
|
|
7817
|
+
fs6.writeFileSync(vsixPath, buffer);
|
|
6225
7818
|
return new Promise((resolve3) => {
|
|
6226
7819
|
const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
|
|
6227
7820
|
(0, import_child_process2.exec)(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
|
|
@@ -6634,18 +8227,18 @@ async function loginFlow() {
|
|
|
6634
8227
|
async function injectTokenToIDE(ide, connectionToken) {
|
|
6635
8228
|
if (!ide.cliCommand) return;
|
|
6636
8229
|
try {
|
|
6637
|
-
const
|
|
6638
|
-
const
|
|
6639
|
-
const
|
|
6640
|
-
const platform9 =
|
|
6641
|
-
const home =
|
|
8230
|
+
const os11 = await import("os");
|
|
8231
|
+
const fs6 = await import("fs");
|
|
8232
|
+
const path7 = await import("path");
|
|
8233
|
+
const platform9 = os11.platform();
|
|
8234
|
+
const home = os11.homedir();
|
|
6642
8235
|
const getSettingsPath = (appName2) => {
|
|
6643
8236
|
if (platform9 === "darwin") {
|
|
6644
|
-
return
|
|
8237
|
+
return path7.join(home, "Library", "Application Support", appName2, "User", "settings.json");
|
|
6645
8238
|
} else if (platform9 === "win32") {
|
|
6646
|
-
return
|
|
8239
|
+
return path7.join(process.env.APPDATA || path7.join(home, "AppData", "Roaming"), appName2, "User", "settings.json");
|
|
6647
8240
|
} else {
|
|
6648
|
-
return
|
|
8241
|
+
return path7.join(home, ".config", appName2, "User", "settings.json");
|
|
6649
8242
|
}
|
|
6650
8243
|
};
|
|
6651
8244
|
const appNameMap = {
|
|
@@ -6660,18 +8253,18 @@ async function injectTokenToIDE(ide, connectionToken) {
|
|
|
6660
8253
|
if (!appName) return;
|
|
6661
8254
|
const settingsPath = getSettingsPath(appName);
|
|
6662
8255
|
let settings = {};
|
|
6663
|
-
if (
|
|
8256
|
+
if (fs6.existsSync(settingsPath)) {
|
|
6664
8257
|
try {
|
|
6665
|
-
settings = JSON.parse(
|
|
8258
|
+
settings = JSON.parse(fs6.readFileSync(settingsPath, "utf-8"));
|
|
6666
8259
|
} catch {
|
|
6667
8260
|
settings = {};
|
|
6668
8261
|
}
|
|
6669
8262
|
} else {
|
|
6670
|
-
|
|
8263
|
+
fs6.mkdirSync(path7.dirname(settingsPath), { recursive: true });
|
|
6671
8264
|
}
|
|
6672
8265
|
settings["adhdev.connectionToken"] = connectionToken;
|
|
6673
8266
|
settings["adhdev.autoConnect"] = true;
|
|
6674
|
-
|
|
8267
|
+
fs6.writeFileSync(settingsPath, JSON.stringify(settings, null, 4), "utf-8");
|
|
6675
8268
|
console.log(import_chalk.default.green(" \u2713 Connection token saved to IDE settings"));
|
|
6676
8269
|
} catch (e) {
|
|
6677
8270
|
console.log(import_chalk.default.yellow(` \u26A0 Could not inject token: ${e.message}`));
|
|
@@ -6993,6 +8586,322 @@ program.command("daemon:stop").description("Stop ADHDev Daemon").action(async ()
|
|
|
6993
8586
|
`));
|
|
6994
8587
|
}
|
|
6995
8588
|
});
|
|
8589
|
+
async function sendDaemonCommand(cmd, args = {}, port = 19222) {
|
|
8590
|
+
const WebSocket4 = (await import("ws")).default;
|
|
8591
|
+
const { DAEMON_WS_PATH: DAEMON_WS_PATH2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
8592
|
+
return new Promise((resolve3, reject) => {
|
|
8593
|
+
const wsUrl = `ws://127.0.0.1:${port}${DAEMON_WS_PATH2 || "/daemon"}`;
|
|
8594
|
+
const ws = new WebSocket4(wsUrl);
|
|
8595
|
+
const timeout = setTimeout(() => {
|
|
8596
|
+
ws.close();
|
|
8597
|
+
reject(new Error("Timeout: no response from daemon after 15s"));
|
|
8598
|
+
}, 15e3);
|
|
8599
|
+
ws.on("open", () => {
|
|
8600
|
+
ws.send(JSON.stringify({
|
|
8601
|
+
type: "ext:register",
|
|
8602
|
+
payload: {
|
|
8603
|
+
ideType: "cli-debug",
|
|
8604
|
+
ideVersion: "1.0.0",
|
|
8605
|
+
extensionVersion: "1.0.0",
|
|
8606
|
+
instanceId: `cli-debug-${Date.now()}`,
|
|
8607
|
+
machineId: "cli"
|
|
8608
|
+
}
|
|
8609
|
+
}));
|
|
8610
|
+
setTimeout(() => {
|
|
8611
|
+
ws.send(JSON.stringify({
|
|
8612
|
+
type: "ext:command",
|
|
8613
|
+
payload: { command: cmd, args, messageId: `cli-${Date.now()}` }
|
|
8614
|
+
}));
|
|
8615
|
+
}, 200);
|
|
8616
|
+
});
|
|
8617
|
+
ws.on("message", (data) => {
|
|
8618
|
+
try {
|
|
8619
|
+
const msg = JSON.parse(data.toString());
|
|
8620
|
+
if (msg.type === "daemon:command_result" || msg.type === "command_result") {
|
|
8621
|
+
clearTimeout(timeout);
|
|
8622
|
+
ws.close();
|
|
8623
|
+
resolve3(msg.payload?.result || msg.payload || msg);
|
|
8624
|
+
}
|
|
8625
|
+
} catch {
|
|
8626
|
+
}
|
|
8627
|
+
});
|
|
8628
|
+
ws.on("error", (e) => {
|
|
8629
|
+
clearTimeout(timeout);
|
|
8630
|
+
reject(new Error(`Cannot connect to daemon at port ${port}: ${e.message}
|
|
8631
|
+
Is 'adhdev daemon' running?`));
|
|
8632
|
+
});
|
|
8633
|
+
ws.on("close", () => {
|
|
8634
|
+
clearTimeout(timeout);
|
|
8635
|
+
});
|
|
8636
|
+
});
|
|
8637
|
+
}
|
|
8638
|
+
async function directCdpEval(expression, port = 9222) {
|
|
8639
|
+
const http3 = await import("http");
|
|
8640
|
+
const targets = await new Promise((resolve3, reject) => {
|
|
8641
|
+
http3.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
8642
|
+
let data = "";
|
|
8643
|
+
res.on("data", (c) => data += c);
|
|
8644
|
+
res.on("end", () => {
|
|
8645
|
+
try {
|
|
8646
|
+
resolve3(JSON.parse(data));
|
|
8647
|
+
} catch {
|
|
8648
|
+
reject(new Error("Invalid JSON"));
|
|
8649
|
+
}
|
|
8650
|
+
});
|
|
8651
|
+
}).on("error", (e) => reject(new Error(`Cannot reach CDP at port ${port}: ${e.message}`)));
|
|
8652
|
+
});
|
|
8653
|
+
const isNonMain = (title) => !title || /extension-output|ADHDev CDP|Debug Console|Output\s*$|Launchpad/i.test(title);
|
|
8654
|
+
const pages = targets.filter((t) => (t.type === "page" || t.type === "Page") && t.webSocketDebuggerUrl);
|
|
8655
|
+
const mainPages = pages.filter((t) => !isNonMain(t.title || ""));
|
|
8656
|
+
const target = (mainPages.length > 0 ? mainPages[0] : pages[0]) || targets[0];
|
|
8657
|
+
if (!target?.webSocketDebuggerUrl) throw new Error("No CDP target found");
|
|
8658
|
+
const WebSocket4 = (await import("ws")).default;
|
|
8659
|
+
return new Promise((resolve3, reject) => {
|
|
8660
|
+
const ws = new WebSocket4(target.webSocketDebuggerUrl);
|
|
8661
|
+
const timeout = setTimeout(() => {
|
|
8662
|
+
ws.close();
|
|
8663
|
+
reject(new Error("CDP timeout"));
|
|
8664
|
+
}, 15e3);
|
|
8665
|
+
let id = 1;
|
|
8666
|
+
ws.on("open", () => {
|
|
8667
|
+
const stripped = expression.replace(/\/\*[\s\S]*?\*\//g, "").trimStart();
|
|
8668
|
+
const isAsync = stripped.startsWith("(async");
|
|
8669
|
+
ws.send(JSON.stringify({
|
|
8670
|
+
id: id++,
|
|
8671
|
+
method: "Runtime.evaluate",
|
|
8672
|
+
params: { expression, returnByValue: true, awaitPromise: isAsync }
|
|
8673
|
+
}));
|
|
8674
|
+
});
|
|
8675
|
+
ws.on("message", (data) => {
|
|
8676
|
+
const msg = JSON.parse(data.toString());
|
|
8677
|
+
if (msg.id) {
|
|
8678
|
+
clearTimeout(timeout);
|
|
8679
|
+
ws.close();
|
|
8680
|
+
if (msg.result?.result?.value !== void 0) {
|
|
8681
|
+
resolve3(msg.result.result.value);
|
|
8682
|
+
} else if (msg.result?.exceptionDetails) {
|
|
8683
|
+
reject(new Error(msg.result.exceptionDetails.text));
|
|
8684
|
+
} else {
|
|
8685
|
+
resolve3(msg.result);
|
|
8686
|
+
}
|
|
8687
|
+
}
|
|
8688
|
+
});
|
|
8689
|
+
ws.on("error", (e) => {
|
|
8690
|
+
clearTimeout(timeout);
|
|
8691
|
+
reject(new Error(`CDP connection failed: ${e.message}`));
|
|
8692
|
+
});
|
|
8693
|
+
});
|
|
8694
|
+
}
|
|
8695
|
+
var cdp = program.command("cdp").description("\u{1F50D} CDP debugging tools \u2014 analyze IDE DOM for script development");
|
|
8696
|
+
cdp.command("debug").description("Analyze IDE AI panel \u2014 find inputs, buttons, textareas, iframes").option("-p, --port <port>", "CDP port (direct mode)", "9222").option("-d, --daemon", "Use running daemon instead of direct CDP").option("--session <id>", "Agent webview session ID").option("-j, --json", "Output raw JSON").action(async (options) => {
|
|
8697
|
+
try {
|
|
8698
|
+
let result;
|
|
8699
|
+
if (options.daemon) {
|
|
8700
|
+
result = await sendDaemonCommand("cdp_dom_debug", { sessionId: options.session });
|
|
8701
|
+
} else {
|
|
8702
|
+
const expression = `(() => {
|
|
8703
|
+
const result = { url: location.href, title: document.title, viewport: { w: window.innerWidth, h: window.innerHeight }, inputs: [], textareas: [], editables: [], buttons: [], iframes: [], textboxes: [] };
|
|
8704
|
+
document.querySelectorAll('input[type="text"], input:not([type])').forEach((el, i) => { if (i >= 10) return; result.inputs.push({ id: el.id||null, class: (el.className||'').toString().slice(0,150), placeholder: el.getAttribute('placeholder')||null, visible: el.offsetParent!==null, rect: (() => { try { const r=el.getBoundingClientRect(); return {x:Math.round(r.x),y:Math.round(r.y),w:Math.round(r.width),h:Math.round(r.height)}; } catch{return null;} })() }); });
|
|
8705
|
+
document.querySelectorAll('textarea').forEach((el, i) => { if (i >= 10) return; result.textareas.push({ id: el.id||null, class: (el.className||'').toString().slice(0,150), placeholder: el.getAttribute('placeholder')||null, rows: el.rows, visible: el.offsetParent!==null }); });
|
|
8706
|
+
document.querySelectorAll('[contenteditable="true"]').forEach((el, i) => { if (i >= 10) return; result.editables.push({ tag: el.tagName?.toLowerCase(), id: el.id||null, class: (el.className||'').toString().slice(0,150), role: el.getAttribute('role')||null, text: (el.textContent||'').trim().slice(0,100), visible: el.offsetParent!==null }); });
|
|
8707
|
+
document.querySelectorAll('[role="textbox"]').forEach((el, i) => { if (i >= 10) return; result.textboxes.push({ tag: el.tagName?.toLowerCase(), id: el.id||null, class: (el.className||'').toString().slice(0,150), text: (el.textContent||'').trim().slice(0,100), visible: el.offsetParent!==null }); });
|
|
8708
|
+
const btnKw = /send|submit|accept|reject|approve|deny|cancel|confirm|run|execute|apply/i;
|
|
8709
|
+
document.querySelectorAll('button, [role="button"]').forEach((el, i) => { const text=(el.textContent||el.getAttribute('aria-label')||'').trim(); if(i<30&&(text.length<30||btnKw.test(text))) result.buttons.push({ tag: el.tagName?.toLowerCase(), text: text.slice(0,80), disabled: el.disabled||el.getAttribute('disabled')!==null, visible: el.offsetParent!==null }); });
|
|
8710
|
+
document.querySelectorAll('iframe, webview').forEach((el, i) => { if (i >= 20) return; result.iframes.push({ tag: el.tagName?.toLowerCase(), src: el.getAttribute('src')?.slice(0,200)||null, title: el.getAttribute('title')||null }); });
|
|
8711
|
+
return JSON.stringify(result);
|
|
8712
|
+
})()`;
|
|
8713
|
+
const raw = await directCdpEval(expression, parseInt(options.port));
|
|
8714
|
+
result = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
8715
|
+
}
|
|
8716
|
+
if (options.json) {
|
|
8717
|
+
console.log(JSON.stringify(result, null, 2));
|
|
8718
|
+
return;
|
|
8719
|
+
}
|
|
8720
|
+
console.log(import_chalk4.default.bold("\n\u{1F50D} CDP DOM Debug\n"));
|
|
8721
|
+
console.log(` ${import_chalk4.default.bold("URL:")} ${result.url || "N/A"}`);
|
|
8722
|
+
console.log(` ${import_chalk4.default.bold("Title:")} ${result.title || "N/A"}`);
|
|
8723
|
+
if (result.viewport) console.log(` ${import_chalk4.default.bold("Viewport:")} ${result.viewport.w}\xD7${result.viewport.h}`);
|
|
8724
|
+
const sections = [
|
|
8725
|
+
{ key: "inputs", label: "\u{1F4DD} Inputs", fields: ["id", "placeholder", "class"] },
|
|
8726
|
+
{ key: "textareas", label: "\u{1F4DD} Textareas", fields: ["id", "placeholder", "rows"] },
|
|
8727
|
+
{ key: "editables", label: "\u270F\uFE0F ContentEditable", fields: ["tag", "role", "class", "text"] },
|
|
8728
|
+
{ key: "textboxes", label: '\u{1F4E6} role="textbox"', fields: ["tag", "class", "text"] },
|
|
8729
|
+
{ key: "buttons", label: "\u{1F518} Buttons", fields: ["text", "disabled"] },
|
|
8730
|
+
{ key: "iframes", label: "\u{1F5BC} Iframes/Webviews", fields: ["tag", "src", "title"] }
|
|
8731
|
+
];
|
|
8732
|
+
for (const { key, label, fields } of sections) {
|
|
8733
|
+
const items = result[key] || [];
|
|
8734
|
+
if (items.length === 0) continue;
|
|
8735
|
+
console.log(`
|
|
8736
|
+
${import_chalk4.default.bold(label)} (${items.length})`);
|
|
8737
|
+
for (const item of items) {
|
|
8738
|
+
const vis = item.visible === false ? import_chalk4.default.red(" [hidden]") : "";
|
|
8739
|
+
const parts = fields.map((f) => item[f] ? `${f}=${import_chalk4.default.cyan(String(item[f]).slice(0, 60))}` : null).filter(Boolean);
|
|
8740
|
+
console.log(` ${import_chalk4.default.gray("\u2022")} ${parts.join(" ")}${vis}`);
|
|
8741
|
+
}
|
|
8742
|
+
}
|
|
8743
|
+
console.log();
|
|
8744
|
+
} catch (e) {
|
|
8745
|
+
console.error(import_chalk4.default.red(`
|
|
8746
|
+
\u2717 ${e.message}
|
|
8747
|
+
`));
|
|
8748
|
+
process.exit(1);
|
|
8749
|
+
}
|
|
8750
|
+
});
|
|
8751
|
+
cdp.command("dump [selector]").description("Dump DOM tree (default: body)").option("-p, --port <port>", "CDP port (direct mode)", "9222").option("-d, --daemon", "Use running daemon").option("-f, --format <fmt>", "Format: html, tree, summary", "tree").option("--depth <n>", "Max depth", "6").option("--session <id>", "Agent webview session ID").option("-o, --output <file>", "Save to file").action(async (selector, options) => {
|
|
8752
|
+
try {
|
|
8753
|
+
const sel = selector || "body";
|
|
8754
|
+
const depth = parseInt(options.depth) || 6;
|
|
8755
|
+
let result;
|
|
8756
|
+
if (options.daemon) {
|
|
8757
|
+
const resp = await sendDaemonCommand("cdp_dom_dump", {
|
|
8758
|
+
selector: sel,
|
|
8759
|
+
format: options.format,
|
|
8760
|
+
depth,
|
|
8761
|
+
sessionId: options.session
|
|
8762
|
+
});
|
|
8763
|
+
result = resp?.result || resp;
|
|
8764
|
+
} else {
|
|
8765
|
+
let expr;
|
|
8766
|
+
if (options.format === "tree") {
|
|
8767
|
+
expr = `(() => { const root = document.querySelector('${sel.replace(/'/g, "\\'")}'); if (!root) return 'Not found: ${sel}'; function tree(el, indent, d, maxD) { if (d > maxD) return indent + '...\\n'; let line = indent + '<' + (el.tagName?.toLowerCase()||'?'); if (el.id) line += ' #' + el.id; if (el.className && typeof el.className === 'string') { const cls = el.className.trim().split(' ').filter(c=>c).slice(0,3).join('.'); if(cls) line += ' .' + cls; } const role = el.getAttribute?.('role'); if(role) line += ' [role=' + role + ']'; line += '> (' + (el.children?.length||0) + ')\\n'; let r = line; if (el.children?.length > 0 && d < maxD) { for (let i = 0; i < Math.min(el.children.length, 30); i++) { r += tree(el.children[i], indent + ' ', d+1, maxD); } if (el.children.length > 30) r += indent + ' ... +' + (el.children.length-30) + ' more\\n'; } return r; } return tree(root, '', 0, ${depth}); })()`;
|
|
8768
|
+
} else if (options.format === "summary") {
|
|
8769
|
+
expr = `(() => { const root = document.querySelector('${sel.replace(/'/g, "\\'")}'); if (!root) return JSON.stringify({error:'Not found'}); function s(el, d, maxD) { if (d > maxD) return {tag:'...',note:'max depth'}; const n = {tag:el.tagName?.toLowerCase(),id:el.id||undefined,class:el.className&&typeof el.className==='string'?el.className.split(' ').filter(c=>c).slice(0,5).join(' '):undefined,role:el.getAttribute?.('role')||undefined,childCount:el.children?.length||0}; if (el.children?.length > 0 && d < maxD) { n.children = Array.from(el.children).slice(0,30).map(c=>s(c,d+1,maxD)); } return n; } return JSON.stringify(s(root,0,${depth})); })()`;
|
|
8770
|
+
} else {
|
|
8771
|
+
expr = `(() => { const root = document.querySelector('${sel.replace(/'/g, "\\'")}'); if (!root) return 'Not found: ${sel}'; let html = root.outerHTML; if (html.length > 200000) html = html.slice(0, 200000) + '\\n<!-- TRUNCATED -->'; return html; })()`;
|
|
8772
|
+
}
|
|
8773
|
+
result = await directCdpEval(expr, parseInt(options.port));
|
|
8774
|
+
if (options.format === "summary" && typeof result === "string") {
|
|
8775
|
+
try {
|
|
8776
|
+
result = JSON.parse(result);
|
|
8777
|
+
} catch {
|
|
8778
|
+
}
|
|
8779
|
+
}
|
|
8780
|
+
}
|
|
8781
|
+
const output = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
8782
|
+
if (options.output) {
|
|
8783
|
+
const fs6 = await import("fs");
|
|
8784
|
+
fs6.writeFileSync(options.output, output, "utf-8");
|
|
8785
|
+
console.log(import_chalk4.default.green(`
|
|
8786
|
+
\u2713 Saved to ${options.output} (${output.length} chars)
|
|
8787
|
+
`));
|
|
8788
|
+
} else {
|
|
8789
|
+
console.log(output);
|
|
8790
|
+
}
|
|
8791
|
+
} catch (e) {
|
|
8792
|
+
console.error(import_chalk4.default.red(`
|
|
8793
|
+
\u2717 ${e.message}
|
|
8794
|
+
`));
|
|
8795
|
+
process.exit(1);
|
|
8796
|
+
}
|
|
8797
|
+
});
|
|
8798
|
+
cdp.command("query <selector>").description("Test a CSS selector \u2014 count matches, show elements").option("-p, --port <port>", "CDP port (direct mode)", "9222").option("-d, --daemon", "Use running daemon").option("-l, --limit <n>", "Max results", "10").option("--session <id>", "Agent webview session ID").option("-j, --json", "Output raw JSON").action(async (selector, options) => {
|
|
8799
|
+
try {
|
|
8800
|
+
const limit = parseInt(options.limit) || 10;
|
|
8801
|
+
let result;
|
|
8802
|
+
if (options.daemon) {
|
|
8803
|
+
result = await sendDaemonCommand("cdp_dom_query", {
|
|
8804
|
+
selector,
|
|
8805
|
+
limit,
|
|
8806
|
+
sessionId: options.session
|
|
8807
|
+
});
|
|
8808
|
+
} else {
|
|
8809
|
+
const expr = `(() => { try { const els = document.querySelectorAll('${selector.replace(/'/g, "\\'")}'); const results = []; for (let i = 0; i < Math.min(els.length, ${limit}); i++) { const el = els[i]; results.push({ index: i, tag: el.tagName?.toLowerCase(), id: el.id||null, class: el.className&&typeof el.className==='string'?el.className.trim().slice(0,200):null, role: el.getAttribute?.('role')||null, text: (el.textContent||'').trim().slice(0,100), visible: el.offsetParent!==null||el.offsetWidth>0, rect: (()=>{try{const r=el.getBoundingClientRect();return{x:Math.round(r.x),y:Math.round(r.y),w:Math.round(r.width),h:Math.round(r.height)};}catch{return null;}})() }); } return JSON.stringify({ total: els.length, results }); } catch(e) { return JSON.stringify({ error: e.message }); } })()`;
|
|
8810
|
+
const raw = await directCdpEval(expr, parseInt(options.port));
|
|
8811
|
+
result = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
8812
|
+
}
|
|
8813
|
+
if (options.json) {
|
|
8814
|
+
console.log(JSON.stringify(result, null, 2));
|
|
8815
|
+
return;
|
|
8816
|
+
}
|
|
8817
|
+
const total = result.total || 0;
|
|
8818
|
+
console.log(import_chalk4.default.bold(`
|
|
8819
|
+
\u{1F50D} Selector: ${import_chalk4.default.cyan(selector)}`));
|
|
8820
|
+
console.log(` Matches: ${total === 0 ? import_chalk4.default.red("0") : import_chalk4.default.green(total)}
|
|
8821
|
+
`);
|
|
8822
|
+
for (const item of result.results || []) {
|
|
8823
|
+
const vis = item.visible === false ? import_chalk4.default.red(" [hidden]") : import_chalk4.default.green(" [visible]");
|
|
8824
|
+
const rect = item.rect ? import_chalk4.default.gray(` ${item.rect.x},${item.rect.y} ${item.rect.w}\xD7${item.rect.h}`) : "";
|
|
8825
|
+
console.log(` ${import_chalk4.default.yellow(`[${item.index}]`)} <${import_chalk4.default.bold(item.tag)}>${vis}${rect}`);
|
|
8826
|
+
if (item.id) console.log(` id: ${import_chalk4.default.cyan(item.id)}`);
|
|
8827
|
+
if (item.class) console.log(` class: ${import_chalk4.default.gray(item.class.slice(0, 80))}`);
|
|
8828
|
+
if (item.text) console.log(` text: "${import_chalk4.default.white(item.text.slice(0, 80))}"`);
|
|
8829
|
+
if (item.role) console.log(` role: ${import_chalk4.default.magenta(item.role)}`);
|
|
8830
|
+
}
|
|
8831
|
+
console.log();
|
|
8832
|
+
} catch (e) {
|
|
8833
|
+
console.error(import_chalk4.default.red(`
|
|
8834
|
+
\u2717 ${e.message}
|
|
8835
|
+
`));
|
|
8836
|
+
process.exit(1);
|
|
8837
|
+
}
|
|
8838
|
+
});
|
|
8839
|
+
cdp.command("eval <expression>").description("Execute JavaScript expression via CDP").option("-p, --port <port>", "CDP port", "9222").option("-d, --daemon", "Use running daemon").action(async (expression, options) => {
|
|
8840
|
+
try {
|
|
8841
|
+
let result;
|
|
8842
|
+
if (options.daemon) {
|
|
8843
|
+
const resp = await sendDaemonCommand("cdp_eval", { expression });
|
|
8844
|
+
result = resp?.result !== void 0 ? resp.result : resp;
|
|
8845
|
+
} else {
|
|
8846
|
+
result = await directCdpEval(expression, parseInt(options.port));
|
|
8847
|
+
}
|
|
8848
|
+
const output = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
8849
|
+
console.log(output);
|
|
8850
|
+
} catch (e) {
|
|
8851
|
+
console.error(import_chalk4.default.red(`
|
|
8852
|
+
\u2717 ${e.message}
|
|
8853
|
+
`));
|
|
8854
|
+
process.exit(1);
|
|
8855
|
+
}
|
|
8856
|
+
});
|
|
8857
|
+
cdp.command("screenshot").description("Capture IDE screenshot").option("-p, --port <port>", "CDP port", "9222").option("-o, --output <file>", "Output file path", "/tmp/cdp_screenshot.jpg").action(async (options) => {
|
|
8858
|
+
try {
|
|
8859
|
+
const http3 = await import("http");
|
|
8860
|
+
const targets = await new Promise((resolve3, reject) => {
|
|
8861
|
+
http3.get(`http://127.0.0.1:${options.port}/json`, (res) => {
|
|
8862
|
+
let data = "";
|
|
8863
|
+
res.on("data", (c) => data += c);
|
|
8864
|
+
res.on("end", () => {
|
|
8865
|
+
try {
|
|
8866
|
+
resolve3(JSON.parse(data));
|
|
8867
|
+
} catch {
|
|
8868
|
+
reject(new Error("Invalid JSON"));
|
|
8869
|
+
}
|
|
8870
|
+
});
|
|
8871
|
+
}).on("error", (e) => reject(e));
|
|
8872
|
+
});
|
|
8873
|
+
const isNonMain = (title) => !title || /extension-output|ADHDev CDP|Debug Console|Output\s*$|Launchpad/i.test(title);
|
|
8874
|
+
const pages = targets.filter((t) => (t.type === "page" || t.type === "Page") && t.webSocketDebuggerUrl);
|
|
8875
|
+
const mainPages = pages.filter((t) => !isNonMain(t.title || ""));
|
|
8876
|
+
const target = (mainPages.length > 0 ? mainPages[0] : pages[0]) || targets[0];
|
|
8877
|
+
if (!target?.webSocketDebuggerUrl) throw new Error("No CDP target");
|
|
8878
|
+
const WebSocket4 = (await import("ws")).default;
|
|
8879
|
+
const ws = new WebSocket4(target.webSocketDebuggerUrl);
|
|
8880
|
+
await new Promise((resolve3, reject) => {
|
|
8881
|
+
ws.on("open", () => {
|
|
8882
|
+
ws.send(JSON.stringify({ id: 1, method: "Page.captureScreenshot", params: { format: "jpeg", quality: 50 } }));
|
|
8883
|
+
});
|
|
8884
|
+
ws.on("message", async (data) => {
|
|
8885
|
+
const msg = JSON.parse(data.toString());
|
|
8886
|
+
if (msg.id === 1 && msg.result?.data) {
|
|
8887
|
+
const fs6 = await import("fs");
|
|
8888
|
+
fs6.writeFileSync(options.output, Buffer.from(msg.result.data, "base64"));
|
|
8889
|
+
console.log(import_chalk4.default.green(`
|
|
8890
|
+
\u2713 Screenshot saved to ${options.output}
|
|
8891
|
+
`));
|
|
8892
|
+
ws.close();
|
|
8893
|
+
resolve3();
|
|
8894
|
+
}
|
|
8895
|
+
});
|
|
8896
|
+
ws.on("error", (e) => reject(e));
|
|
8897
|
+
});
|
|
8898
|
+
} catch (e) {
|
|
8899
|
+
console.error(import_chalk4.default.red(`
|
|
8900
|
+
\u2717 ${e.message}
|
|
8901
|
+
`));
|
|
8902
|
+
process.exit(1);
|
|
8903
|
+
}
|
|
8904
|
+
});
|
|
6996
8905
|
program.command("bridge").description('[Legacy] Start CLI bridge (use "adhdev daemon" instead)').option("--foreground", "Run in foreground mode").option("-d, --dir <path>", "Working directory for CLI sessions", process.cwd()).option("--cli <type>", "CLI type to use", "gemini-cli").option("--server <url>", "Override server URL for testing").action(async (options) => {
|
|
6997
8906
|
console.log(import_chalk4.default.yellow(' \u26A0 "adhdev bridge" is deprecated. Use "adhdev daemon --cli <type>" instead.\n'));
|
|
6998
8907
|
const { CliDaemon: CliDaemon2 } = await Promise.resolve().then(() => (init_cli_daemon(), cli_daemon_exports));
|
|
@@ -7057,8 +8966,8 @@ program.command("bridge:logs").description("[Legacy] Show CLI bridge service log
|
|
|
7057
8966
|
const logPath = getLogPath2();
|
|
7058
8967
|
const { execSync: execSync8, spawn: spawnProc } = await import("child_process");
|
|
7059
8968
|
try {
|
|
7060
|
-
const
|
|
7061
|
-
if (!
|
|
8969
|
+
const fs6 = await import("fs");
|
|
8970
|
+
if (!fs6.existsSync(logPath)) {
|
|
7062
8971
|
console.log(import_chalk4.default.gray("\n No logs yet. Start the bridge first.\n"));
|
|
7063
8972
|
return;
|
|
7064
8973
|
}
|