adhdev 0.1.35 → 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 +1884 -266
- package/package.json +56 -56
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"() {
|
|
@@ -539,7 +1020,7 @@ function detectCurrentWorkspace(ideId) {
|
|
|
539
1020
|
}
|
|
540
1021
|
} else if (plat === "win32") {
|
|
541
1022
|
try {
|
|
542
|
-
const
|
|
1023
|
+
const fs6 = require("fs");
|
|
543
1024
|
const appNameMap = {
|
|
544
1025
|
vscode: "Code",
|
|
545
1026
|
cursor: "Cursor",
|
|
@@ -554,8 +1035,8 @@ function detectCurrentWorkspace(ideId) {
|
|
|
554
1035
|
appName,
|
|
555
1036
|
"storage.json"
|
|
556
1037
|
);
|
|
557
|
-
if (
|
|
558
|
-
const data = JSON.parse(
|
|
1038
|
+
if (fs6.existsSync(storagePath)) {
|
|
1039
|
+
const data = JSON.parse(fs6.readFileSync(storagePath, "utf-8"));
|
|
559
1040
|
const workspaces = data?.openedPathsList?.workspaces3 || data?.openedPathsList?.entries || [];
|
|
560
1041
|
if (workspaces.length > 0) {
|
|
561
1042
|
const recent = workspaces[0];
|
|
@@ -2024,17 +2505,16 @@ var init_local_server = __esm({
|
|
|
2024
2505
|
});
|
|
2025
2506
|
|
|
2026
2507
|
// src/daemon-cdp.ts
|
|
2027
|
-
var import_ws3, http,
|
|
2508
|
+
var import_ws3, http, KNOWN_AGENTS, DaemonCdpManager;
|
|
2028
2509
|
var init_daemon_cdp = __esm({
|
|
2029
2510
|
"src/daemon-cdp.ts"() {
|
|
2030
2511
|
"use strict";
|
|
2031
2512
|
import_ws3 = __toESM(require("ws"));
|
|
2032
2513
|
http = __toESM(require("http"));
|
|
2033
|
-
fs = __toESM(require("fs"));
|
|
2034
2514
|
KNOWN_AGENTS = [
|
|
2035
|
-
{ agentType: "cline", extensionId: "saoudrizwan.claude-dev", extensionIdPattern: /saoudrizwan\.claude-dev/ },
|
|
2036
|
-
{ agentType: "roo-code", extensionId: "rooveterinaryinc.roo-cline", extensionIdPattern: /rooveterinaryinc\.roo-cline/ },
|
|
2037
|
-
{ 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 }
|
|
2038
2518
|
];
|
|
2039
2519
|
DaemonCdpManager = class {
|
|
2040
2520
|
ws = null;
|
|
@@ -2061,10 +2541,6 @@ var init_daemon_cdp = __esm({
|
|
|
2061
2541
|
this.disableFallback = disableFallback;
|
|
2062
2542
|
this.logFn = logFn || ((msg) => {
|
|
2063
2543
|
console.log(msg);
|
|
2064
|
-
try {
|
|
2065
|
-
fs.appendFileSync("/tmp/adhdev_daemon_cdp.log", msg + "\n");
|
|
2066
|
-
} catch {
|
|
2067
|
-
}
|
|
2068
2544
|
});
|
|
2069
2545
|
}
|
|
2070
2546
|
setPort(port) {
|
|
@@ -2373,6 +2849,62 @@ var init_daemon_cdp = __esm({
|
|
|
2373
2849
|
})()
|
|
2374
2850
|
`);
|
|
2375
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
|
+
}
|
|
2376
2908
|
// ─── Agent Webview Multi-Session ─────────────────────────
|
|
2377
2909
|
async discoverAgentWebviews() {
|
|
2378
2910
|
if (!this.isConnected) return [];
|
|
@@ -2805,21 +3337,17 @@ var require_lib = __commonJS({
|
|
|
2805
3337
|
});
|
|
2806
3338
|
|
|
2807
3339
|
// src/daemon-p2p.ts
|
|
2808
|
-
var
|
|
3340
|
+
var fs, path2, os6, logFile, log, DaemonP2PSender;
|
|
2809
3341
|
var init_daemon_p2p = __esm({
|
|
2810
3342
|
"src/daemon-p2p.ts"() {
|
|
2811
3343
|
"use strict";
|
|
2812
|
-
|
|
3344
|
+
fs = __toESM(require("fs"));
|
|
2813
3345
|
path2 = __toESM(require("path"));
|
|
2814
3346
|
os6 = __toESM(require("os"));
|
|
2815
3347
|
logFile = path2.join(os6.tmpdir(), "adhdev_daemon_p2p.log");
|
|
2816
3348
|
log = (msg) => {
|
|
2817
3349
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [P2P] ${msg}`;
|
|
2818
3350
|
console.log(line);
|
|
2819
|
-
try {
|
|
2820
|
-
fs2.appendFileSync(logFile, line + "\n");
|
|
2821
|
-
} catch {
|
|
2822
|
-
}
|
|
2823
3351
|
};
|
|
2824
3352
|
DaemonP2PSender = class {
|
|
2825
3353
|
bridge;
|
|
@@ -2876,11 +3404,11 @@ var init_daemon_p2p = __esm({
|
|
|
2876
3404
|
];
|
|
2877
3405
|
for (const candidate of candidates) {
|
|
2878
3406
|
const prebuildPath = path2.join(candidate, "prebuilds", prebuildKey, "node_datachannel.node");
|
|
2879
|
-
if (
|
|
3407
|
+
if (fs.existsSync(prebuildPath)) {
|
|
2880
3408
|
const targetDir = path2.join(candidate, "build", "Release");
|
|
2881
3409
|
const targetPath = path2.join(targetDir, "node_datachannel.node");
|
|
2882
|
-
|
|
2883
|
-
|
|
3410
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
3411
|
+
fs.copyFileSync(prebuildPath, targetPath);
|
|
2884
3412
|
try {
|
|
2885
3413
|
delete require.cache[require.resolve("node-datachannel")];
|
|
2886
3414
|
} catch {
|
|
@@ -2941,7 +3469,7 @@ var init_daemon_p2p = __esm({
|
|
|
2941
3469
|
const configPath = path2.join(os6.homedir(), ".adhdev", "config.json");
|
|
2942
3470
|
let token = "";
|
|
2943
3471
|
try {
|
|
2944
|
-
const config = JSON.parse(
|
|
3472
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
2945
3473
|
token = config.connectionToken || "";
|
|
2946
3474
|
} catch {
|
|
2947
3475
|
}
|
|
@@ -3354,17 +3882,12 @@ var init_daemon_p2p = __esm({
|
|
|
3354
3882
|
|
|
3355
3883
|
// src/daemon-script-loader.ts
|
|
3356
3884
|
function log2(msg) {
|
|
3357
|
-
|
|
3358
|
-
fs3.appendFileSync(`${require("os").tmpdir()}/adhdev_daemon_scripts.log`, `${msg}
|
|
3359
|
-
`);
|
|
3360
|
-
} catch {
|
|
3361
|
-
}
|
|
3885
|
+
console.log(msg);
|
|
3362
3886
|
}
|
|
3363
|
-
var
|
|
3887
|
+
var http2, https, DaemonScriptLoader;
|
|
3364
3888
|
var init_daemon_script_loader = __esm({
|
|
3365
3889
|
"src/daemon-script-loader.ts"() {
|
|
3366
3890
|
"use strict";
|
|
3367
|
-
fs3 = __toESM(require("fs"));
|
|
3368
3891
|
http2 = __toESM(require("http"));
|
|
3369
3892
|
https = __toESM(require("https"));
|
|
3370
3893
|
DaemonScriptLoader = class {
|
|
@@ -3374,9 +3897,13 @@ var init_daemon_script_loader = __esm({
|
|
|
3374
3897
|
// name → code
|
|
3375
3898
|
fallbacks = /* @__PURE__ */ new Map();
|
|
3376
3899
|
// name → bundled fallback code
|
|
3900
|
+
capabilityMap = /* @__PURE__ */ new Map();
|
|
3901
|
+
// capability → { script, params }
|
|
3377
3902
|
manifestVersion = "0";
|
|
3378
3903
|
refreshTimer = null;
|
|
3379
3904
|
token = "";
|
|
3905
|
+
platform = "";
|
|
3906
|
+
// darwin, win32, linux
|
|
3380
3907
|
constructor(baseUrl, ideType) {
|
|
3381
3908
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
3382
3909
|
this.ideType = ideType;
|
|
@@ -3384,6 +3911,9 @@ var init_daemon_script_loader = __esm({
|
|
|
3384
3911
|
setToken(token) {
|
|
3385
3912
|
this.token = token;
|
|
3386
3913
|
}
|
|
3914
|
+
setPlatform(platform9) {
|
|
3915
|
+
this.platform = platform9;
|
|
3916
|
+
}
|
|
3387
3917
|
registerFallback(name, code) {
|
|
3388
3918
|
this.fallbacks.set(name, code);
|
|
3389
3919
|
}
|
|
@@ -3400,6 +3930,46 @@ var init_daemon_script_loader = __esm({
|
|
|
3400
3930
|
has(name) {
|
|
3401
3931
|
return this.cache.has(name) || this.fallbacks.has(name);
|
|
3402
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
|
+
}
|
|
3403
3973
|
async start() {
|
|
3404
3974
|
log2(`[DaemonScriptLoader] Starting: baseUrl=${this.baseUrl} ide=${this.ideType}`);
|
|
3405
3975
|
await this.refresh();
|
|
@@ -3480,6 +4050,22 @@ var init_daemon_script_loader = __esm({
|
|
|
3480
4050
|
log2(`[DaemonScriptLoader] Downloading ${scriptNames.length} scripts for ${this.ideType}/${version}`);
|
|
3481
4051
|
const downloads = scriptNames.map((name) => this.downloadScript(version, name));
|
|
3482
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
|
+
}
|
|
3483
4069
|
log2(`[DaemonScriptLoader] Loaded ${this.cache.size} scripts`);
|
|
3484
4070
|
} catch (e) {
|
|
3485
4071
|
log2(`[DaemonScriptLoader] Refresh error: ${e?.message}`);
|
|
@@ -3503,28 +4089,47 @@ var init_daemon_script_loader = __esm({
|
|
|
3503
4089
|
log2(`[DaemonScriptLoader] Download error ${name}: ${e?.message}`);
|
|
3504
4090
|
}
|
|
3505
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
|
+
}
|
|
3506
4105
|
};
|
|
3507
4106
|
}
|
|
3508
4107
|
});
|
|
3509
4108
|
|
|
3510
4109
|
// src/daemon-commands.ts
|
|
3511
|
-
var
|
|
4110
|
+
var fs2, path3, os7, DaemonCommandHandler;
|
|
3512
4111
|
var init_daemon_commands = __esm({
|
|
3513
4112
|
"src/daemon-commands.ts"() {
|
|
3514
4113
|
"use strict";
|
|
3515
|
-
|
|
4114
|
+
fs2 = __toESM(require("fs"));
|
|
3516
4115
|
path3 = __toESM(require("path"));
|
|
3517
4116
|
os7 = __toESM(require("os"));
|
|
3518
4117
|
init_config();
|
|
3519
4118
|
DaemonCommandHandler = class {
|
|
3520
4119
|
ctx;
|
|
3521
4120
|
agentStream = null;
|
|
3522
|
-
/** 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
|
+
*/
|
|
3523
4126
|
getCdp(ideType) {
|
|
3524
4127
|
const key = ideType || this._currentIdeType;
|
|
3525
4128
|
if (key) {
|
|
3526
4129
|
const m = this.ctx.cdpManagers.get(key.toLowerCase());
|
|
3527
4130
|
if (m?.isConnected) return m;
|
|
4131
|
+
console.log(`[getCdp] \u26A0 Target IDE "${key}" CDP not available, refusing fallback`);
|
|
4132
|
+
return null;
|
|
3528
4133
|
}
|
|
3529
4134
|
for (const m of this.ctx.cdpManagers.values()) {
|
|
3530
4135
|
if (m.isConnected) return m;
|
|
@@ -3533,10 +4138,17 @@ var init_daemon_commands = __esm({
|
|
|
3533
4138
|
}
|
|
3534
4139
|
/** Current IDE type extracted from command args (per-request) */
|
|
3535
4140
|
_currentIdeType;
|
|
3536
|
-
/** Extract ideType from _targetInstance
|
|
4141
|
+
/** Extract ideType from _targetInstance
|
|
4142
|
+
* Handles both simple ('vscode_abc123') and composite ('doId:ide:vscode_abc123') formats.
|
|
4143
|
+
*/
|
|
3537
4144
|
extractIdeType(args) {
|
|
3538
4145
|
if (args?._targetInstance) {
|
|
3539
|
-
|
|
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("_");
|
|
3540
4152
|
if (parts.length >= 2) return parts[0];
|
|
3541
4153
|
}
|
|
3542
4154
|
return void 0;
|
|
@@ -3559,6 +4171,7 @@ var init_daemon_commands = __esm({
|
|
|
3559
4171
|
}
|
|
3560
4172
|
async handle(cmd, args) {
|
|
3561
4173
|
this._currentIdeType = this.extractIdeType(args);
|
|
4174
|
+
console.log(`[Cmd] ${cmd} \u2192 ideType=${this._currentIdeType || "none"} _targetInstance=${args?._targetInstance || "none"}`);
|
|
3562
4175
|
switch (cmd) {
|
|
3563
4176
|
// ─── CDP 직접 처리 ───────────────────
|
|
3564
4177
|
case "read_chat":
|
|
@@ -3590,6 +4203,12 @@ var init_daemon_commands = __esm({
|
|
|
3590
4203
|
return this.handleCdpRemoteAction(args);
|
|
3591
4204
|
case "cdp_discover_agents":
|
|
3592
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);
|
|
3593
4212
|
// ─── 파일 직접 처리 ──────────────────
|
|
3594
4213
|
case "file_read":
|
|
3595
4214
|
return this.handleFileRead(args);
|
|
@@ -3655,29 +4274,66 @@ var init_daemon_commands = __esm({
|
|
|
3655
4274
|
}
|
|
3656
4275
|
// ─── CDP 기반 채팅 명령 ──────────────────────
|
|
3657
4276
|
async handleReadChat(args) {
|
|
3658
|
-
|
|
4277
|
+
const cdp2 = this.getCdp();
|
|
4278
|
+
if (!cdp2?.isConnected) return { success: false, error: "CDP not connected" };
|
|
3659
4279
|
const script = this.getScriptLoader()?.get("read_chat");
|
|
3660
|
-
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
|
+
}
|
|
3661
4294
|
try {
|
|
3662
|
-
const
|
|
3663
|
-
|
|
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
|
+
}
|
|
3664
4318
|
} catch (e) {
|
|
3665
|
-
|
|
4319
|
+
console.log(`[read_chat] Inline fallback error: ${e.message}`);
|
|
3666
4320
|
}
|
|
4321
|
+
return { success: true, messages: [], status: "idle" };
|
|
3667
4322
|
}
|
|
3668
4323
|
async handleSendChat(args) {
|
|
3669
4324
|
const text = args?.text || args?.message;
|
|
3670
4325
|
if (!text) return { success: false, error: "text required" };
|
|
4326
|
+
const _log = (msg) => console.log(`[send_chat] ${msg}`);
|
|
3671
4327
|
const targetAgent = args?.agentType || args?.cliType || this._currentIdeType;
|
|
3672
4328
|
if (targetAgent) {
|
|
3673
4329
|
for (const [key, adapter] of this.ctx.adapters.entries()) {
|
|
3674
4330
|
if (adapter.cliType === targetAgent || key.startsWith(targetAgent)) {
|
|
3675
|
-
|
|
4331
|
+
_log(`Routing to CLI adapter: ${adapter.cliType} (${key})`);
|
|
3676
4332
|
try {
|
|
3677
4333
|
await adapter.sendMessage(text);
|
|
3678
4334
|
return { success: true, sent: true, targetAgent: adapter.cliType };
|
|
3679
4335
|
} catch (e) {
|
|
3680
|
-
|
|
4336
|
+
_log(`CLI adapter failed: ${e.message}`);
|
|
3681
4337
|
return { success: false, error: `CLI send failed: ${e.message}` };
|
|
3682
4338
|
}
|
|
3683
4339
|
}
|
|
@@ -3685,49 +4341,86 @@ var init_daemon_commands = __esm({
|
|
|
3685
4341
|
}
|
|
3686
4342
|
const targetCdp = this.getCdp();
|
|
3687
4343
|
const targetLoader = this.getScriptLoader();
|
|
3688
|
-
if (targetCdp?.isConnected
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
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" };
|
|
4352
|
+
}
|
|
4353
|
+
} catch (e) {
|
|
4354
|
+
_log(`typeAndSend failed: ${e.message}`);
|
|
4355
|
+
}
|
|
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
|
+
}
|
|
3697
4376
|
}
|
|
3698
4377
|
}
|
|
4378
|
+
} else {
|
|
4379
|
+
_log(`No CDP (connected=${targetCdp?.isConnected}) for ${this._currentIdeType}`);
|
|
3699
4380
|
}
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
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
|
+
}
|
|
3703
4396
|
const loader = this.ctx.scriptLoaders.get(ideType);
|
|
3704
4397
|
if (!loader) continue;
|
|
3705
4398
|
const script = loader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
|
|
3706
4399
|
if (!script) continue;
|
|
3707
|
-
|
|
4400
|
+
_log(`Trying script on ${ideType}`);
|
|
3708
4401
|
try {
|
|
3709
|
-
await
|
|
3710
|
-
|
|
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" };
|
|
3711
4405
|
} catch (e) {
|
|
3712
|
-
|
|
4406
|
+
_log(`Failed on ${ideType}: ${e.message}`);
|
|
3713
4407
|
}
|
|
3714
4408
|
}
|
|
4409
|
+
_log("All methods failed");
|
|
3715
4410
|
return { success: false, error: "No CDP or CLI adapter could send the message" };
|
|
3716
4411
|
}
|
|
3717
4412
|
async handleListChats(args) {
|
|
3718
|
-
const
|
|
3719
|
-
if (!
|
|
4413
|
+
const cdp2 = this.getCdp();
|
|
4414
|
+
if (!cdp2?.isConnected) {
|
|
3720
4415
|
console.log(`[list_chats] CDP not connected, ideType=${this._currentIdeType}`);
|
|
3721
4416
|
return { success: false, error: "CDP not connected" };
|
|
3722
4417
|
}
|
|
3723
|
-
const
|
|
3724
|
-
if (!
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
}
|
|
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" };
|
|
3728
4422
|
try {
|
|
3729
|
-
|
|
3730
|
-
const result = await cdp.evaluate(script, 3e4);
|
|
4423
|
+
const result = await cdp2.evaluate(script, 3e4);
|
|
3731
4424
|
let parsed = result;
|
|
3732
4425
|
if (typeof result === "string") {
|
|
3733
4426
|
try {
|
|
@@ -3735,10 +4428,13 @@ var init_daemon_commands = __esm({
|
|
|
3735
4428
|
} catch {
|
|
3736
4429
|
}
|
|
3737
4430
|
}
|
|
3738
|
-
|
|
3739
|
-
|
|
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: [] };
|
|
3740
4436
|
} catch (e) {
|
|
3741
|
-
console.log(`[list_chats]
|
|
4437
|
+
console.log(`[list_chats] error: ${e.message}`);
|
|
3742
4438
|
return { success: false, error: e.message };
|
|
3743
4439
|
}
|
|
3744
4440
|
}
|
|
@@ -3754,15 +4450,22 @@ var init_daemon_commands = __esm({
|
|
|
3754
4450
|
}
|
|
3755
4451
|
}
|
|
3756
4452
|
async handleSwitchChat(args) {
|
|
3757
|
-
|
|
4453
|
+
const ideType = this._currentIdeType;
|
|
4454
|
+
const cdp2 = this.getCdp(ideType);
|
|
4455
|
+
if (!cdp2?.isConnected) return { success: false, error: "CDP not connected" };
|
|
3758
4456
|
const sessionId = args?.sessionId || args?.id || args?.chatId;
|
|
3759
4457
|
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
3760
|
-
|
|
3761
|
-
|
|
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" };
|
|
3762
4463
|
try {
|
|
3763
|
-
await
|
|
3764
|
-
|
|
4464
|
+
const result = await cdp2.evaluate(script, 15e3);
|
|
4465
|
+
console.log(`[switch_chat] result:`, result);
|
|
4466
|
+
return { success: true, result };
|
|
3765
4467
|
} catch (e) {
|
|
4468
|
+
console.error(`[switch_chat] error:`, e.message);
|
|
3766
4469
|
return { success: false, error: e.message };
|
|
3767
4470
|
}
|
|
3768
4471
|
}
|
|
@@ -3950,35 +4653,328 @@ var init_daemon_commands = __esm({
|
|
|
3950
4653
|
const agents = await this.getCdp().discoverAgentWebviews();
|
|
3951
4654
|
return { success: true, agents };
|
|
3952
4655
|
}
|
|
3953
|
-
// ───
|
|
3954
|
-
|
|
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;
|
|
3955
4676
|
try {
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
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 };
|
|
3959
4755
|
} catch (e) {
|
|
3960
4756
|
return { success: false, error: e.message };
|
|
3961
4757
|
}
|
|
3962
4758
|
}
|
|
3963
|
-
|
|
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
|
+
})()`;
|
|
3964
4801
|
try {
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
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 };
|
|
3969
4810
|
} catch (e) {
|
|
3970
4811
|
return { success: false, error: e.message };
|
|
3971
4812
|
}
|
|
3972
4813
|
}
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
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
|
+
}
|
|
4949
|
+
// ─── 파일 직접 처리 ─────────────────────────
|
|
4950
|
+
async handleFileRead(args) {
|
|
4951
|
+
try {
|
|
4952
|
+
const filePath = this.resolveSafePath(args?.path);
|
|
4953
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
4954
|
+
return { success: true, content, path: filePath };
|
|
4955
|
+
} catch (e) {
|
|
4956
|
+
return { success: false, error: e.message };
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
async handleFileWrite(args) {
|
|
4960
|
+
try {
|
|
4961
|
+
const filePath = this.resolveSafePath(args?.path);
|
|
4962
|
+
fs2.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
4963
|
+
fs2.writeFileSync(filePath, args?.content || "", "utf-8");
|
|
4964
|
+
return { success: true, path: filePath };
|
|
4965
|
+
} catch (e) {
|
|
4966
|
+
return { success: false, error: e.message };
|
|
4967
|
+
}
|
|
4968
|
+
}
|
|
4969
|
+
async handleFileList(args) {
|
|
4970
|
+
try {
|
|
4971
|
+
const dirPath = this.resolveSafePath(args?.path || ".");
|
|
4972
|
+
const entries = fs2.readdirSync(dirPath, { withFileTypes: true });
|
|
4973
|
+
const files = entries.map((e) => ({
|
|
4974
|
+
name: e.name,
|
|
4975
|
+
type: e.isDirectory() ? "directory" : "file",
|
|
4976
|
+
size: e.isFile() ? fs2.statSync(path3.join(dirPath, e.name)).size : void 0
|
|
4977
|
+
}));
|
|
3982
4978
|
return { success: true, files, path: dirPath };
|
|
3983
4979
|
} catch (e) {
|
|
3984
4980
|
return { success: false, error: e.message };
|
|
@@ -4015,7 +5011,7 @@ var init_daemon_commands = __esm({
|
|
|
4015
5011
|
const candidates = ["Cursor", "Code", "VSCodium"];
|
|
4016
5012
|
for (const app of candidates) {
|
|
4017
5013
|
const stateFile = path3.join(storageDir, app, "User", "globalStorage", "state.vscdb");
|
|
4018
|
-
if (
|
|
5014
|
+
if (fs2.existsSync(stateFile)) {
|
|
4019
5015
|
const result = await this.delegateToExtension("adhdev.getRecentWorkspaces", []);
|
|
4020
5016
|
if (result.success && Array.isArray(result.result)) {
|
|
4021
5017
|
const merged = Array.from(/* @__PURE__ */ new Set([...cliRecent, ...result.result])).slice(0, 20);
|
|
@@ -4124,13 +5120,18 @@ var init_daemon_commands = __esm({
|
|
|
4124
5120
|
return { success: false, error: `CLI adapter not found: ${cliType}` };
|
|
4125
5121
|
}
|
|
4126
5122
|
handlePtyResize(args) {
|
|
4127
|
-
const { cliType, cols, rows } = args || {};
|
|
5123
|
+
const { cliType, cols, rows, force } = args || {};
|
|
4128
5124
|
if (!cols || !rows) return { success: false, error: "cols and rows required" };
|
|
4129
5125
|
if (this.ctx.adapters) {
|
|
4130
5126
|
const targetCli = cliType || "gemini-cli";
|
|
4131
5127
|
for (const [, adapter] of this.ctx.adapters) {
|
|
4132
5128
|
if (adapter.cliType === targetCli && typeof adapter.resize === "function") {
|
|
4133
|
-
|
|
5129
|
+
if (force) {
|
|
5130
|
+
adapter.resize(cols - 1, rows);
|
|
5131
|
+
setTimeout(() => adapter.resize(cols, rows), 50);
|
|
5132
|
+
} else {
|
|
5133
|
+
adapter.resize(cols, rows);
|
|
5134
|
+
}
|
|
4134
5135
|
return { success: true };
|
|
4135
5136
|
}
|
|
4136
5137
|
}
|
|
@@ -4435,13 +5436,13 @@ var init_manager = __esm({
|
|
|
4435
5436
|
}
|
|
4436
5437
|
}
|
|
4437
5438
|
}
|
|
4438
|
-
async switchActiveAgent(
|
|
5439
|
+
async switchActiveAgent(cdp2, agentType) {
|
|
4439
5440
|
if (this._activeAgentType === agentType) return;
|
|
4440
5441
|
if (this._activeAgentType) {
|
|
4441
5442
|
const prev = this.managed.get(this._activeAgentType);
|
|
4442
5443
|
if (prev) {
|
|
4443
5444
|
try {
|
|
4444
|
-
await
|
|
5445
|
+
await cdp2.detachAgent(prev.sessionId);
|
|
4445
5446
|
} catch {
|
|
4446
5447
|
}
|
|
4447
5448
|
this.managed.delete(this._activeAgentType);
|
|
@@ -4453,7 +5454,7 @@ var init_manager = __esm({
|
|
|
4453
5454
|
this.logFn(`[AgentStream] Active agent: ${agentType || "none"}`);
|
|
4454
5455
|
}
|
|
4455
5456
|
/** Agent webview 발견 + 세션 연결 */
|
|
4456
|
-
async syncAgentSessions(
|
|
5457
|
+
async syncAgentSessions(cdp2) {
|
|
4457
5458
|
if (!this.enabled || !this._activeAgentType) return;
|
|
4458
5459
|
const now = Date.now();
|
|
4459
5460
|
if (this.managed.has(this._activeAgentType) && now - this.lastDiscoveryTime < this.discoveryIntervalMs) {
|
|
@@ -4461,12 +5462,12 @@ var init_manager = __esm({
|
|
|
4461
5462
|
}
|
|
4462
5463
|
this.lastDiscoveryTime = now;
|
|
4463
5464
|
try {
|
|
4464
|
-
const targets = await
|
|
5465
|
+
const targets = await cdp2.discoverAgentWebviews();
|
|
4465
5466
|
const activeTarget = targets.find((t) => t.agentType === this._activeAgentType);
|
|
4466
5467
|
if (activeTarget && !this.managed.has(this._activeAgentType)) {
|
|
4467
5468
|
const adapter = this.allAdapters.find((a) => a.agentType === this._activeAgentType);
|
|
4468
5469
|
if (adapter) {
|
|
4469
|
-
const sessionId = await
|
|
5470
|
+
const sessionId = await cdp2.attachToAgent(activeTarget);
|
|
4470
5471
|
if (sessionId) {
|
|
4471
5472
|
this.managed.set(this._activeAgentType, {
|
|
4472
5473
|
adapter,
|
|
@@ -4482,7 +5483,7 @@ var init_manager = __esm({
|
|
|
4482
5483
|
}
|
|
4483
5484
|
for (const [type, agent] of this.managed) {
|
|
4484
5485
|
if (type !== this._activeAgentType) {
|
|
4485
|
-
await
|
|
5486
|
+
await cdp2.detachAgent(agent.sessionId);
|
|
4486
5487
|
this.managed.delete(type);
|
|
4487
5488
|
}
|
|
4488
5489
|
}
|
|
@@ -4492,7 +5493,7 @@ var init_manager = __esm({
|
|
|
4492
5493
|
}
|
|
4493
5494
|
}
|
|
4494
5495
|
/** 활성 에이전트 상태 수집 */
|
|
4495
|
-
async collectAgentStreams(
|
|
5496
|
+
async collectAgentStreams(cdp2) {
|
|
4496
5497
|
if (!this.enabled) return [];
|
|
4497
5498
|
const results = [];
|
|
4498
5499
|
if (this._activeAgentType && this.managed.has(this._activeAgentType)) {
|
|
@@ -4504,7 +5505,7 @@ var init_manager = __esm({
|
|
|
4504
5505
|
results.push(agent.lastState);
|
|
4505
5506
|
} else {
|
|
4506
5507
|
try {
|
|
4507
|
-
const evaluate = (expr, timeout) =>
|
|
5508
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4508
5509
|
const state = await agent.adapter.readChat(evaluate);
|
|
4509
5510
|
agent.lastState = state;
|
|
4510
5511
|
agent.lastError = null;
|
|
@@ -4526,7 +5527,7 @@ var init_manager = __esm({
|
|
|
4526
5527
|
});
|
|
4527
5528
|
if (errorMsg.includes("timeout") || errorMsg.includes("not connected") || errorMsg.includes("Session")) {
|
|
4528
5529
|
try {
|
|
4529
|
-
await
|
|
5530
|
+
await cdp2.detachAgent(agent.sessionId);
|
|
4530
5531
|
} catch {
|
|
4531
5532
|
}
|
|
4532
5533
|
this.managed.delete(type);
|
|
@@ -4537,12 +5538,12 @@ var init_manager = __esm({
|
|
|
4537
5538
|
}
|
|
4538
5539
|
return results;
|
|
4539
5540
|
}
|
|
4540
|
-
async sendToAgent(
|
|
5541
|
+
async sendToAgent(cdp2, agentType, text, targetIdeType) {
|
|
4541
5542
|
await this.ensureAgentPanelOpen(agentType, targetIdeType);
|
|
4542
5543
|
const agent = this.managed.get(agentType);
|
|
4543
5544
|
if (!agent) return false;
|
|
4544
5545
|
try {
|
|
4545
|
-
const evaluate = (expr, timeout) =>
|
|
5546
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4546
5547
|
await agent.adapter.sendMessage(evaluate, text);
|
|
4547
5548
|
return true;
|
|
4548
5549
|
} catch (e) {
|
|
@@ -4550,24 +5551,24 @@ var init_manager = __esm({
|
|
|
4550
5551
|
return false;
|
|
4551
5552
|
}
|
|
4552
5553
|
}
|
|
4553
|
-
async resolveAgentAction(
|
|
5554
|
+
async resolveAgentAction(cdp2, agentType, action, targetIdeType) {
|
|
4554
5555
|
await this.ensureAgentPanelOpen(agentType, targetIdeType);
|
|
4555
5556
|
const agent = this.managed.get(agentType);
|
|
4556
5557
|
if (!agent) return false;
|
|
4557
5558
|
try {
|
|
4558
|
-
const evaluate = (expr, timeout) =>
|
|
5559
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4559
5560
|
return await agent.adapter.resolveAction(evaluate, action);
|
|
4560
5561
|
} catch (e) {
|
|
4561
5562
|
this.logFn(`[AgentStream] resolveAction(${agentType}) error: ${e.message}`);
|
|
4562
5563
|
return false;
|
|
4563
5564
|
}
|
|
4564
5565
|
}
|
|
4565
|
-
async newAgentSession(
|
|
5566
|
+
async newAgentSession(cdp2, agentType, targetIdeType) {
|
|
4566
5567
|
await this.ensureAgentPanelOpen(agentType, targetIdeType);
|
|
4567
5568
|
const agent = this.managed.get(agentType);
|
|
4568
5569
|
if (!agent) return false;
|
|
4569
5570
|
try {
|
|
4570
|
-
const evaluate = (expr, timeout) =>
|
|
5571
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4571
5572
|
await agent.adapter.newSession(evaluate);
|
|
4572
5573
|
return true;
|
|
4573
5574
|
} catch (e) {
|
|
@@ -4575,33 +5576,45 @@ var init_manager = __esm({
|
|
|
4575
5576
|
return false;
|
|
4576
5577
|
}
|
|
4577
5578
|
}
|
|
4578
|
-
async listAgentChats(
|
|
4579
|
-
|
|
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
|
+
}
|
|
4580
5587
|
if (!agent || typeof agent.adapter.listChats !== "function") return [];
|
|
4581
5588
|
try {
|
|
4582
|
-
const evaluate = (expr, timeout) =>
|
|
5589
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4583
5590
|
return await agent.adapter.listChats(evaluate);
|
|
4584
5591
|
} catch (e) {
|
|
4585
5592
|
this.logFn(`[AgentStream] listChats(${agentType}) error: ${e.message}`);
|
|
4586
5593
|
return [];
|
|
4587
5594
|
}
|
|
4588
5595
|
}
|
|
4589
|
-
async switchAgentSession(
|
|
4590
|
-
|
|
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
|
+
}
|
|
4591
5604
|
if (!agent || typeof agent.adapter.switchSession !== "function") return false;
|
|
4592
5605
|
try {
|
|
4593
|
-
const evaluate = (expr, timeout) =>
|
|
5606
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4594
5607
|
return await agent.adapter.switchSession(evaluate, sessionId);
|
|
4595
5608
|
} catch (e) {
|
|
4596
5609
|
this.logFn(`[AgentStream] switchSession(${agentType}) error: ${e.message}`);
|
|
4597
5610
|
return false;
|
|
4598
5611
|
}
|
|
4599
5612
|
}
|
|
4600
|
-
async focusAgentEditor(
|
|
5613
|
+
async focusAgentEditor(cdp2, agentType) {
|
|
4601
5614
|
const agent = this.managed.get(agentType);
|
|
4602
5615
|
if (!agent || typeof agent.adapter.focusEditor !== "function") return false;
|
|
4603
5616
|
try {
|
|
4604
|
-
const evaluate = (expr, timeout) =>
|
|
5617
|
+
const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
|
|
4605
5618
|
await agent.adapter.focusEditor(evaluate);
|
|
4606
5619
|
return true;
|
|
4607
5620
|
} catch (e) {
|
|
@@ -4615,12 +5628,12 @@ var init_manager = __esm({
|
|
|
4615
5628
|
getManagedAgent(agentType) {
|
|
4616
5629
|
return this.managed.get(agentType);
|
|
4617
5630
|
}
|
|
4618
|
-
async dispose(
|
|
5631
|
+
async dispose(cdp2) {
|
|
4619
5632
|
for (const loader of this.scriptLoaders.values()) loader.stop();
|
|
4620
5633
|
this.scriptLoaders.clear();
|
|
4621
5634
|
for (const [, agent] of this.managed) {
|
|
4622
5635
|
try {
|
|
4623
|
-
await
|
|
5636
|
+
await cdp2.detachAgent(agent.sessionId);
|
|
4624
5637
|
} catch {
|
|
4625
5638
|
}
|
|
4626
5639
|
}
|
|
@@ -4649,6 +5662,107 @@ var init_agent_stream = __esm({
|
|
|
4649
5662
|
}
|
|
4650
5663
|
});
|
|
4651
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
|
+
|
|
4652
5766
|
// src/adhdev-daemon.ts
|
|
4653
5767
|
var adhdev_daemon_exports = {};
|
|
4654
5768
|
__export(adhdev_daemon_exports, {
|
|
@@ -4657,24 +5771,24 @@ __export(adhdev_daemon_exports, {
|
|
|
4657
5771
|
stopDaemon: () => stopDaemon
|
|
4658
5772
|
});
|
|
4659
5773
|
function getDaemonPidFile() {
|
|
4660
|
-
const dir =
|
|
4661
|
-
if (!
|
|
4662
|
-
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");
|
|
4663
5777
|
}
|
|
4664
5778
|
function writeDaemonPid(pid) {
|
|
4665
|
-
|
|
5779
|
+
fs4.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
|
|
4666
5780
|
}
|
|
4667
5781
|
function removeDaemonPid() {
|
|
4668
5782
|
try {
|
|
4669
|
-
|
|
5783
|
+
fs4.unlinkSync(getDaemonPidFile());
|
|
4670
5784
|
} catch {
|
|
4671
5785
|
}
|
|
4672
5786
|
}
|
|
4673
5787
|
function isDaemonRunning() {
|
|
4674
5788
|
const pidFile = getDaemonPidFile();
|
|
4675
5789
|
try {
|
|
4676
|
-
if (!
|
|
4677
|
-
const pid = parseInt(
|
|
5790
|
+
if (!fs4.existsSync(pidFile)) return false;
|
|
5791
|
+
const pid = parseInt(fs4.readFileSync(pidFile, "utf-8").trim());
|
|
4678
5792
|
process.kill(pid, 0);
|
|
4679
5793
|
return true;
|
|
4680
5794
|
} catch {
|
|
@@ -4685,8 +5799,8 @@ function isDaemonRunning() {
|
|
|
4685
5799
|
function stopDaemon() {
|
|
4686
5800
|
const pidFile = getDaemonPidFile();
|
|
4687
5801
|
try {
|
|
4688
|
-
if (!
|
|
4689
|
-
const pid = parseInt(
|
|
5802
|
+
if (!fs4.existsSync(pidFile)) return false;
|
|
5803
|
+
const pid = parseInt(fs4.readFileSync(pidFile, "utf-8").trim());
|
|
4690
5804
|
process.kill(pid, "SIGTERM");
|
|
4691
5805
|
removeDaemonPid();
|
|
4692
5806
|
return true;
|
|
@@ -4695,7 +5809,7 @@ function stopDaemon() {
|
|
|
4695
5809
|
return false;
|
|
4696
5810
|
}
|
|
4697
5811
|
}
|
|
4698
|
-
var
|
|
5812
|
+
var os9, fs4, path5, crypto2, import_chalk2, DANGEROUS_PATTERNS, AdhdevDaemon;
|
|
4699
5813
|
var init_adhdev_daemon = __esm({
|
|
4700
5814
|
"src/adhdev-daemon.ts"() {
|
|
4701
5815
|
"use strict";
|
|
@@ -4714,9 +5828,9 @@ var init_adhdev_daemon = __esm({
|
|
|
4714
5828
|
init_daemon_commands();
|
|
4715
5829
|
init_agent_stream();
|
|
4716
5830
|
init_dist();
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
5831
|
+
os9 = __toESM(require("os"));
|
|
5832
|
+
fs4 = __toESM(require("fs"));
|
|
5833
|
+
path5 = __toESM(require("path"));
|
|
4720
5834
|
crypto2 = __toESM(require("crypto"));
|
|
4721
5835
|
import_chalk2 = __toESM(require("chalk"));
|
|
4722
5836
|
DANGEROUS_PATTERNS = [
|
|
@@ -4766,6 +5880,11 @@ var init_adhdev_daemon = __esm({
|
|
|
4766
5880
|
this.localPort = DEFAULT_DAEMON_PORT;
|
|
4767
5881
|
}
|
|
4768
5882
|
async start(options = {}) {
|
|
5883
|
+
try {
|
|
5884
|
+
const { installGlobalInterceptor: installGlobalInterceptor2 } = (init_daemon_logger(), __toCommonJS(daemon_logger_exports));
|
|
5885
|
+
installGlobalInterceptor2();
|
|
5886
|
+
} catch {
|
|
5887
|
+
}
|
|
4769
5888
|
this.localPort = options.localPort || DEFAULT_DAEMON_PORT;
|
|
4770
5889
|
const workingDir = options.workingDir || process.cwd();
|
|
4771
5890
|
if (isDaemonRunning()) {
|
|
@@ -4839,6 +5958,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4839
5958
|
for (const ideType of this.cdpManagers.keys()) {
|
|
4840
5959
|
const loader = new DaemonScriptLoader(serverUrl, ideType);
|
|
4841
5960
|
loader.setToken(config.connectionToken);
|
|
5961
|
+
loader.setPlatform(os9.platform());
|
|
4842
5962
|
await loader.start().catch((e) => {
|
|
4843
5963
|
console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${ideType}: ${e.message}`));
|
|
4844
5964
|
});
|
|
@@ -4848,6 +5968,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4848
5968
|
const fallbackType = this.detectedIdes.find((ide) => ide.installed)?.id || "cursor";
|
|
4849
5969
|
const loader = new DaemonScriptLoader(serverUrl, fallbackType);
|
|
4850
5970
|
loader.setToken(config.connectionToken);
|
|
5971
|
+
loader.setPlatform(os9.platform());
|
|
4851
5972
|
await loader.start().catch((e) => {
|
|
4852
5973
|
console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed: ${e.message}`));
|
|
4853
5974
|
});
|
|
@@ -4873,8 +5994,8 @@ var init_adhdev_daemon = __esm({
|
|
|
4873
5994
|
console.log(import_chalk2.default.yellow(` \u26A0 Failed to start CLI ${options.cliType}: ${e.message}`));
|
|
4874
5995
|
});
|
|
4875
5996
|
}
|
|
4876
|
-
const machineId =
|
|
4877
|
-
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);
|
|
4878
5999
|
const instanceId = `daemon_${machineId}_${machineHash}`;
|
|
4879
6000
|
this.bridge = new CliBridgeConnection({
|
|
4880
6001
|
serverUrl: options.serverUrl || config.serverUrl,
|
|
@@ -4882,7 +6003,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4882
6003
|
cliInfo: {
|
|
4883
6004
|
type: "adhdev-daemon",
|
|
4884
6005
|
version: "0.2.0",
|
|
4885
|
-
platform:
|
|
6006
|
+
platform: os9.platform(),
|
|
4886
6007
|
instanceId
|
|
4887
6008
|
}
|
|
4888
6009
|
});
|
|
@@ -4894,8 +6015,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4894
6015
|
return this.commandHandler.handle("cdp_remote_action", event);
|
|
4895
6016
|
});
|
|
4896
6017
|
this.p2p.onCommand(async (cmdType, data, id) => {
|
|
4897
|
-
|
|
4898
|
-
return this.commandHandler.handle(cmdType, data);
|
|
6018
|
+
return this.handleP2PCommand(cmdType, data);
|
|
4899
6019
|
});
|
|
4900
6020
|
this.p2p.onFileRequest(async (req) => {
|
|
4901
6021
|
if (req.type === "read") {
|
|
@@ -4929,14 +6049,14 @@ var init_adhdev_daemon = __esm({
|
|
|
4929
6049
|
this.screenshotTimer = setInterval(async () => {
|
|
4930
6050
|
const active = this.p2p?.screenshotActive;
|
|
4931
6051
|
const ssIdeType = this.p2p?.screenshotIdeType;
|
|
4932
|
-
const
|
|
4933
|
-
if (!active || !
|
|
6052
|
+
const cdp2 = ssIdeType ? this.getCdpFor(ssIdeType) : this.getAnyCdp();
|
|
6053
|
+
if (!active || !cdp2) return;
|
|
4934
6054
|
ssDebugCount++;
|
|
4935
6055
|
if (ssDebugCount <= 3 || ssDebugCount % 25 === 0) {
|
|
4936
6056
|
console.log(`[SS] Capturing screenshot... (tick ${ssDebugCount}, active=${active}, cdp=true)`);
|
|
4937
6057
|
}
|
|
4938
6058
|
try {
|
|
4939
|
-
const buf = await
|
|
6059
|
+
const buf = await cdp2.captureScreenshot();
|
|
4940
6060
|
if (buf) {
|
|
4941
6061
|
const sent = this.p2p.sendScreenshot(buf.toString("base64"));
|
|
4942
6062
|
if (ssDebugCount <= 3) console.log(`[SS] Screenshot sent: ${buf.length} bytes, delivered=${sent}`);
|
|
@@ -5188,7 +6308,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5188
6308
|
if (result.success && result.port && result.ideId && !this.cdpManagers.has(result.ideId)) {
|
|
5189
6309
|
console.log(import_chalk2.default.cyan(`[launch_ide] Connecting CDP for ${result.ideId} on port ${result.port}...`));
|
|
5190
6310
|
const manager = new DaemonCdpManager(result.port, (msg2) => {
|
|
5191
|
-
console.log(
|
|
6311
|
+
console.log(`[CDP:${result.ideId}] ${msg2}`);
|
|
5192
6312
|
}, true);
|
|
5193
6313
|
const connected = await manager.connect();
|
|
5194
6314
|
if (connected) {
|
|
@@ -5200,6 +6320,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5200
6320
|
const serverUrl = config.serverUrl || "https://api.adhf.dev";
|
|
5201
6321
|
const loader = new DaemonScriptLoader(serverUrl, result.ideId);
|
|
5202
6322
|
loader.setToken(config.connectionToken || "");
|
|
6323
|
+
loader.setPlatform(os9.platform());
|
|
5203
6324
|
await loader.start().catch((e) => {
|
|
5204
6325
|
console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${result.ideId}: ${e?.message}`));
|
|
5205
6326
|
});
|
|
@@ -5234,6 +6355,13 @@ var init_adhdev_daemon = __esm({
|
|
|
5234
6355
|
if (this.commandHandler) {
|
|
5235
6356
|
const result = await this.commandHandler.handle(cmd, args);
|
|
5236
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
|
+
}
|
|
5237
6365
|
} else {
|
|
5238
6366
|
this.sendResult(msg, false, { error: `Command handler not initialized: ${cmd}` });
|
|
5239
6367
|
}
|
|
@@ -5242,6 +6370,87 @@ var init_adhdev_daemon = __esm({
|
|
|
5242
6370
|
this.sendResult(msg, false, { error: e.message });
|
|
5243
6371
|
}
|
|
5244
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
|
+
}
|
|
5245
6454
|
// ─── CLI 세션 관리 ──────────────────────────────
|
|
5246
6455
|
createAdapter(cliType, workingDir) {
|
|
5247
6456
|
if (cliType === "claude-cli" || cliType === "claude-code") {
|
|
@@ -5253,7 +6462,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5253
6462
|
}
|
|
5254
6463
|
}
|
|
5255
6464
|
async startCliSession(cliType, workingDir) {
|
|
5256
|
-
const resolvedDir = workingDir.startsWith("~") ? workingDir.replace(/^~/,
|
|
6465
|
+
const resolvedDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os9.homedir()) : workingDir;
|
|
5257
6466
|
const cliInfo = await detectCLI(cliType);
|
|
5258
6467
|
if (!cliInfo) throw new Error(`${cliType} not found`);
|
|
5259
6468
|
const key = this.getCliKey(cliType, resolvedDir);
|
|
@@ -5270,7 +6479,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5270
6479
|
adapter.setOnStatusChange(() => this.sendUnifiedStatusReport());
|
|
5271
6480
|
if (typeof adapter.setOnPtyData === "function") {
|
|
5272
6481
|
adapter.setOnPtyData((data) => {
|
|
5273
|
-
const sentViaP2P = this.
|
|
6482
|
+
const sentViaP2P = this.p2p?.broadcastPtyOutput(adapter.cliType, data);
|
|
5274
6483
|
if (!sentViaP2P && this.bridge) {
|
|
5275
6484
|
this.bridge.sendMessage("pty_output", {
|
|
5276
6485
|
cliType: adapter.cliType,
|
|
@@ -5302,7 +6511,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5302
6511
|
this.sendUnifiedStatusReport().catch(() => {
|
|
5303
6512
|
});
|
|
5304
6513
|
scheduleNext();
|
|
5305
|
-
},
|
|
6514
|
+
}, 5e3);
|
|
5306
6515
|
};
|
|
5307
6516
|
scheduleNext();
|
|
5308
6517
|
}
|
|
@@ -5325,16 +6534,17 @@ var init_adhdev_daemon = __esm({
|
|
|
5325
6534
|
async sendUnifiedStatusReport() {
|
|
5326
6535
|
if (!this.bridge?.isConnected()) return;
|
|
5327
6536
|
this.lastStatusSentAt = Date.now();
|
|
6537
|
+
console.log(`[StatusReport] Starting... cdp=${this.cdpManagers.size} scripts=${this.scriptLoaders.size} busy=${this._cdpChatBusy}`);
|
|
5328
6538
|
if (this.cdpManagers.size > 0 && this.scriptLoaders.size > 0 && !this._cdpChatBusy) {
|
|
5329
6539
|
this._cdpChatBusy = true;
|
|
5330
|
-
for (const [ideType,
|
|
5331
|
-
if (!
|
|
6540
|
+
for (const [ideType, cdp2] of this.cdpManagers) {
|
|
6541
|
+
if (!cdp2.isConnected) continue;
|
|
5332
6542
|
const loader = this.scriptLoaders.get(ideType);
|
|
5333
6543
|
if (!loader) continue;
|
|
5334
6544
|
const readChatScript = loader.get("read_chat");
|
|
5335
6545
|
if (!readChatScript) continue;
|
|
5336
6546
|
try {
|
|
5337
|
-
const raw = await
|
|
6547
|
+
const raw = await cdp2.evaluate(readChatScript, 3e4);
|
|
5338
6548
|
if (raw && typeof raw === "object") {
|
|
5339
6549
|
let { activeModal } = raw;
|
|
5340
6550
|
if (activeModal) {
|
|
@@ -5346,9 +6556,54 @@ var init_adhdev_daemon = __esm({
|
|
|
5346
6556
|
buttons: (activeModal.buttons ?? []).filter((t) => t.length < 30)
|
|
5347
6557
|
};
|
|
5348
6558
|
}
|
|
5349
|
-
|
|
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`);
|
|
5350
6604
|
}
|
|
5351
|
-
} catch {
|
|
6605
|
+
} catch (e1) {
|
|
6606
|
+
console.log(`[StatusReport] ${ideType} read_chat error: ${e1.message}`);
|
|
5352
6607
|
}
|
|
5353
6608
|
}
|
|
5354
6609
|
this._cdpChatBusy = false;
|
|
@@ -5399,6 +6654,29 @@ var init_adhdev_daemon = __esm({
|
|
|
5399
6654
|
cdpConnected: isMainCdpIde ? this.cdpManagers.get(ext.ideType.toLowerCase())?.isConnected || false : false
|
|
5400
6655
|
};
|
|
5401
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
|
+
}
|
|
5402
6680
|
const managedClis = [];
|
|
5403
6681
|
for (const [key, adapter] of this.adapters.entries()) {
|
|
5404
6682
|
const adapterStatus = adapter.getStatus();
|
|
@@ -5457,15 +6735,15 @@ var init_adhdev_daemon = __esm({
|
|
|
5457
6735
|
// Machine 정보
|
|
5458
6736
|
daemonMode: true,
|
|
5459
6737
|
machine: {
|
|
5460
|
-
hostname:
|
|
5461
|
-
platform:
|
|
5462
|
-
release:
|
|
5463
|
-
arch:
|
|
5464
|
-
cpus:
|
|
5465
|
-
totalMem:
|
|
5466
|
-
freeMem:
|
|
5467
|
-
loadavg:
|
|
5468
|
-
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()
|
|
5469
6747
|
},
|
|
5470
6748
|
// 관리 중인 IDE/CLI (계층 데이터)
|
|
5471
6749
|
managedIdes,
|
|
@@ -5488,7 +6766,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5488
6766
|
timestamp: now,
|
|
5489
6767
|
// ─── Legacy compat (서버 기존 로직용, 점진적 제거 예정) ───
|
|
5490
6768
|
activeFile: extSummary.activeFile,
|
|
5491
|
-
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 })),
|
|
5492
6770
|
terminals: extSummary.terminals,
|
|
5493
6771
|
// Legacy aiAgents: managedIdes에서 각 IDE의 aiAgents를 통합
|
|
5494
6772
|
aiAgents: [
|
|
@@ -5507,15 +6785,15 @@ var init_adhdev_daemon = __esm({
|
|
|
5507
6785
|
],
|
|
5508
6786
|
connectedExtensions: extSummary.connectedIdes,
|
|
5509
6787
|
system: {
|
|
5510
|
-
platform:
|
|
5511
|
-
release:
|
|
5512
|
-
arch:
|
|
5513
|
-
cpus:
|
|
5514
|
-
totalMem:
|
|
5515
|
-
freeMem:
|
|
5516
|
-
loadavg:
|
|
5517
|
-
uptime:
|
|
5518
|
-
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()
|
|
5519
6797
|
}
|
|
5520
6798
|
};
|
|
5521
6799
|
const { timestamp: _ts, system: _sys, ...hashTarget } = payload;
|
|
@@ -5530,7 +6808,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5530
6808
|
}
|
|
5531
6809
|
const p2pConnected = (this.p2p?.connectedPeerCount || 0) > 0;
|
|
5532
6810
|
this.statusReportCount = (this.statusReportCount || 0) + 1;
|
|
5533
|
-
const forceFullSync = this.statusReportCount %
|
|
6811
|
+
const forceFullSync = this.statusReportCount % 12 === 0;
|
|
5534
6812
|
if (p2pConnected && this.lastStatusSentFull && !forceFullSync) {
|
|
5535
6813
|
this.bridge.sendMessage("status_heartbeat", {
|
|
5536
6814
|
timestamp: now,
|
|
@@ -5611,17 +6889,41 @@ var init_adhdev_daemon = __esm({
|
|
|
5611
6889
|
return this.cdpManagers.get(ideType.toLowerCase()) || null;
|
|
5612
6890
|
}
|
|
5613
6891
|
/** Agent stream polling 시작 (idempotent — 이미 시작됐으면 무시) */
|
|
6892
|
+
_agentStreamCdpIdeType = null;
|
|
6893
|
+
// agent가 연결된 IDE
|
|
5614
6894
|
startAgentStreamPolling() {
|
|
5615
6895
|
if (this.agentStreamTimer) return;
|
|
5616
6896
|
this.agentStreamTimer = setInterval(async () => {
|
|
5617
6897
|
if (!this.agentStreamManager || this.cdpManagers.size === 0) return;
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
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
|
+
}
|
|
5625
6927
|
}
|
|
5626
6928
|
}
|
|
5627
6929
|
}, 5e3);
|
|
@@ -5648,7 +6950,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5648
6950
|
const filteredPorts = enabledIdes.length > 0 ? portsToTry.filter((p) => enabledIdes.includes(p.ide)) : portsToTry;
|
|
5649
6951
|
for (const { port, ide } of filteredPorts) {
|
|
5650
6952
|
const manager = new DaemonCdpManager(port, (msg) => {
|
|
5651
|
-
console.log(
|
|
6953
|
+
console.log(`[CDP:${ide}] ${msg}`);
|
|
5652
6954
|
}, true);
|
|
5653
6955
|
const connected = await manager.connect();
|
|
5654
6956
|
if (connected) {
|
|
@@ -5691,27 +6993,27 @@ __export(cli_daemon_exports, {
|
|
|
5691
6993
|
uninstallService: () => uninstallService
|
|
5692
6994
|
});
|
|
5693
6995
|
function getPidFile(workingDir, cliType) {
|
|
5694
|
-
const absPath =
|
|
6996
|
+
const absPath = path6.resolve(workingDir);
|
|
5695
6997
|
const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
|
|
5696
|
-
const dir =
|
|
5697
|
-
if (!
|
|
5698
|
-
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`);
|
|
5699
7001
|
}
|
|
5700
7002
|
function writePidFile(pid, workingDir, cliType) {
|
|
5701
7003
|
const pidFile = getPidFile(workingDir, cliType);
|
|
5702
|
-
|
|
7004
|
+
fs5.writeFileSync(pidFile, String(pid), "utf-8");
|
|
5703
7005
|
}
|
|
5704
7006
|
function removePidFile(workingDir, cliType) {
|
|
5705
7007
|
try {
|
|
5706
|
-
|
|
7008
|
+
fs5.unlinkSync(getPidFile(workingDir, cliType));
|
|
5707
7009
|
} catch {
|
|
5708
7010
|
}
|
|
5709
7011
|
}
|
|
5710
7012
|
function isDaemonRunning2(workingDir = process.cwd(), cliType = "gemini-cli") {
|
|
5711
7013
|
const pidFile = getPidFile(workingDir, cliType);
|
|
5712
7014
|
try {
|
|
5713
|
-
if (!
|
|
5714
|
-
const pid = parseInt(
|
|
7015
|
+
if (!fs5.existsSync(pidFile)) return false;
|
|
7016
|
+
const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
|
|
5715
7017
|
process.kill(pid, 0);
|
|
5716
7018
|
return true;
|
|
5717
7019
|
} catch {
|
|
@@ -5722,8 +7024,8 @@ function isDaemonRunning2(workingDir = process.cwd(), cliType = "gemini-cli") {
|
|
|
5722
7024
|
function stopDaemon2(workingDir = process.cwd(), cliType = "gemini-cli") {
|
|
5723
7025
|
const pidFile = getPidFile(workingDir, cliType);
|
|
5724
7026
|
try {
|
|
5725
|
-
if (!
|
|
5726
|
-
const pid = parseInt(
|
|
7027
|
+
if (!fs5.existsSync(pidFile)) return false;
|
|
7028
|
+
const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
|
|
5727
7029
|
process.kill(pid, "SIGTERM");
|
|
5728
7030
|
removePidFile(workingDir, cliType);
|
|
5729
7031
|
return true;
|
|
@@ -5733,40 +7035,40 @@ function stopDaemon2(workingDir = process.cwd(), cliType = "gemini-cli") {
|
|
|
5733
7035
|
}
|
|
5734
7036
|
}
|
|
5735
7037
|
function getServiceLabel(workingDir, cliType) {
|
|
5736
|
-
const absPath =
|
|
7038
|
+
const absPath = path6.resolve(workingDir);
|
|
5737
7039
|
const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
|
|
5738
7040
|
return `dev.adhf.cli-bridge.${cliType}.${hash}`;
|
|
5739
7041
|
}
|
|
5740
7042
|
function getLogFile(workingDir, cliType, isErr = false) {
|
|
5741
|
-
const absPath =
|
|
7043
|
+
const absPath = path6.resolve(workingDir);
|
|
5742
7044
|
const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
|
|
5743
|
-
if (!
|
|
5744
|
-
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`);
|
|
5745
7047
|
}
|
|
5746
7048
|
function getServicePaths(label) {
|
|
5747
|
-
const platform9 =
|
|
7049
|
+
const platform9 = os10.platform();
|
|
5748
7050
|
if (platform9 === "darwin") {
|
|
5749
7051
|
return {
|
|
5750
|
-
plist:
|
|
7052
|
+
plist: path6.join(os10.homedir(), "Library", "LaunchAgents", `${label}.plist`)
|
|
5751
7053
|
};
|
|
5752
7054
|
} else if (platform9 === "linux") {
|
|
5753
7055
|
return {
|
|
5754
|
-
systemd:
|
|
7056
|
+
systemd: path6.join(os10.homedir(), ".config", "systemd", "user", `${label}.service`)
|
|
5755
7057
|
};
|
|
5756
7058
|
} else if (platform9 === "win32") {
|
|
5757
7059
|
return {
|
|
5758
|
-
startup:
|
|
7060
|
+
startup: path6.join(os10.homedir(), "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs", "Startup", `${label}.vbs`)
|
|
5759
7061
|
};
|
|
5760
7062
|
}
|
|
5761
7063
|
return {};
|
|
5762
7064
|
}
|
|
5763
7065
|
function installService(options = {}) {
|
|
5764
|
-
const platform9 =
|
|
7066
|
+
const platform9 = os10.platform();
|
|
5765
7067
|
const cliType = options.cliType || "gemini-cli";
|
|
5766
|
-
const workingDir = options.workingDir ||
|
|
7068
|
+
const workingDir = options.workingDir || os10.homedir();
|
|
5767
7069
|
const adhdevBin = process.argv[1];
|
|
5768
7070
|
const nodeBin = process.execPath;
|
|
5769
|
-
if (!
|
|
7071
|
+
if (!fs5.existsSync(LOG_DIR2)) fs5.mkdirSync(LOG_DIR2, { recursive: true });
|
|
5770
7072
|
const label = getServiceLabel(workingDir, cliType);
|
|
5771
7073
|
const logFile2 = getLogFile(workingDir, cliType);
|
|
5772
7074
|
const errLogFile = getLogFile(workingDir, cliType, true);
|
|
@@ -5821,17 +7123,17 @@ function installMacService(nodeBin, adhdevBin, cliType, workingDir, label, logFi
|
|
|
5821
7123
|
<key>PATH</key>
|
|
5822
7124
|
<string>${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}</string>
|
|
5823
7125
|
<key>HOME</key>
|
|
5824
|
-
<string>${
|
|
7126
|
+
<string>${os10.homedir()}</string>
|
|
5825
7127
|
</dict>
|
|
5826
7128
|
</dict>
|
|
5827
7129
|
</plist>`;
|
|
5828
|
-
const agentsDir =
|
|
5829
|
-
if (!
|
|
7130
|
+
const agentsDir = path6.dirname(plistPath);
|
|
7131
|
+
if (!fs5.existsSync(agentsDir)) fs5.mkdirSync(agentsDir, { recursive: true });
|
|
5830
7132
|
try {
|
|
5831
7133
|
execSync8(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "pipe" });
|
|
5832
7134
|
} catch {
|
|
5833
7135
|
}
|
|
5834
|
-
|
|
7136
|
+
fs5.writeFileSync(plistPath, plistContent, "utf-8");
|
|
5835
7137
|
try {
|
|
5836
7138
|
execSync8(`launchctl load "${plistPath}"`, { stdio: "pipe" });
|
|
5837
7139
|
} catch (e) {
|
|
@@ -5857,14 +7159,14 @@ RestartSec=10
|
|
|
5857
7159
|
StandardOutput=append:${logFile2}
|
|
5858
7160
|
StandardError=append:${errLogFile}
|
|
5859
7161
|
Environment=PATH=${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}
|
|
5860
|
-
Environment=HOME=${
|
|
7162
|
+
Environment=HOME=${os10.homedir()}
|
|
5861
7163
|
|
|
5862
7164
|
[Install]
|
|
5863
7165
|
WantedBy=default.target
|
|
5864
7166
|
`;
|
|
5865
|
-
const serviceDir =
|
|
5866
|
-
if (!
|
|
5867
|
-
|
|
7167
|
+
const serviceDir = path6.dirname(servicePath);
|
|
7168
|
+
if (!fs5.existsSync(serviceDir)) fs5.mkdirSync(serviceDir, { recursive: true });
|
|
7169
|
+
fs5.writeFileSync(servicePath, serviceContent, "utf-8");
|
|
5868
7170
|
try {
|
|
5869
7171
|
execSync8("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
5870
7172
|
execSync8(`systemctl --user enable ${label}.service`, { stdio: "pipe" });
|
|
@@ -5881,9 +7183,9 @@ function installWindowsService(nodeBin, adhdevBin, cliType, workingDir, label) {
|
|
|
5881
7183
|
const vbsContent = `Set WshShell = CreateObject("WScript.Shell")
|
|
5882
7184
|
WshShell.Run """${nodeBin}"" ""${adhdevBin}"" bridge --foreground --cli ${cliType} --dir ""${workingDir}""", 0, False
|
|
5883
7185
|
`;
|
|
5884
|
-
const startupDir =
|
|
5885
|
-
if (!
|
|
5886
|
-
|
|
7186
|
+
const startupDir = path6.dirname(startupPath);
|
|
7187
|
+
if (!fs5.existsSync(startupDir)) fs5.mkdirSync(startupDir, { recursive: true });
|
|
7188
|
+
fs5.writeFileSync(startupPath, vbsContent, "utf-8");
|
|
5887
7189
|
try {
|
|
5888
7190
|
const { execSync: execSync8 } = require("child_process");
|
|
5889
7191
|
execSync8(`wscript "${startupPath}"`, { stdio: "pipe" });
|
|
@@ -5892,9 +7194,9 @@ WshShell.Run """${nodeBin}"" ""${adhdevBin}"" bridge --foreground --cli ${cliTyp
|
|
|
5892
7194
|
return true;
|
|
5893
7195
|
}
|
|
5894
7196
|
function uninstallService(options = {}) {
|
|
5895
|
-
const platform9 =
|
|
7197
|
+
const platform9 = os10.platform();
|
|
5896
7198
|
const cliType = options.cliType || "gemini-cli";
|
|
5897
|
-
const workingDir = options.workingDir ||
|
|
7199
|
+
const workingDir = options.workingDir || os10.homedir();
|
|
5898
7200
|
const label = getServiceLabel(workingDir, cliType);
|
|
5899
7201
|
const paths = getServicePaths(label);
|
|
5900
7202
|
const { execSync: execSync8 } = require("child_process");
|
|
@@ -5904,7 +7206,7 @@ function uninstallService(options = {}) {
|
|
|
5904
7206
|
} catch {
|
|
5905
7207
|
}
|
|
5906
7208
|
try {
|
|
5907
|
-
if (
|
|
7209
|
+
if (fs5.existsSync(paths.plist)) fs5.unlinkSync(paths.plist);
|
|
5908
7210
|
} catch {
|
|
5909
7211
|
}
|
|
5910
7212
|
} else if (platform9 === "linux" && paths.systemd) {
|
|
@@ -5914,7 +7216,7 @@ function uninstallService(options = {}) {
|
|
|
5914
7216
|
} catch {
|
|
5915
7217
|
}
|
|
5916
7218
|
try {
|
|
5917
|
-
if (
|
|
7219
|
+
if (fs5.existsSync(paths.systemd)) fs5.unlinkSync(paths.systemd);
|
|
5918
7220
|
} catch {
|
|
5919
7221
|
}
|
|
5920
7222
|
try {
|
|
@@ -5923,25 +7225,25 @@ function uninstallService(options = {}) {
|
|
|
5923
7225
|
}
|
|
5924
7226
|
} else if (platform9 === "win32" && paths.startup) {
|
|
5925
7227
|
try {
|
|
5926
|
-
if (
|
|
7228
|
+
if (fs5.existsSync(paths.startup)) fs5.unlinkSync(paths.startup);
|
|
5927
7229
|
} catch {
|
|
5928
7230
|
}
|
|
5929
7231
|
}
|
|
5930
7232
|
removePidFile(workingDir, cliType);
|
|
5931
7233
|
return true;
|
|
5932
7234
|
}
|
|
5933
|
-
function isServiceInstalled(workingDir =
|
|
7235
|
+
function isServiceInstalled(workingDir = os10.homedir(), cliType = "gemini-cli") {
|
|
5934
7236
|
const label = getServiceLabel(workingDir, cliType);
|
|
5935
7237
|
const paths = getServicePaths(label);
|
|
5936
|
-
if (paths.plist) return
|
|
5937
|
-
if (paths.systemd) return
|
|
5938
|
-
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);
|
|
5939
7241
|
return false;
|
|
5940
7242
|
}
|
|
5941
|
-
function getLogPath(workingDir =
|
|
7243
|
+
function getLogPath(workingDir = os10.homedir(), cliType = "gemini-cli") {
|
|
5942
7244
|
return getLogFile(workingDir, cliType);
|
|
5943
7245
|
}
|
|
5944
|
-
var
|
|
7246
|
+
var os10, fs5, path6, crypto3, import_chalk3, DANGEROUS_PATTERNS2, CliDaemon, LOG_DIR2;
|
|
5945
7247
|
var init_cli_daemon = __esm({
|
|
5946
7248
|
"src/cli-daemon.ts"() {
|
|
5947
7249
|
"use strict";
|
|
@@ -5951,9 +7253,9 @@ var init_cli_daemon = __esm({
|
|
|
5951
7253
|
init_codex_cli();
|
|
5952
7254
|
init_cli_detector();
|
|
5953
7255
|
init_config();
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
7256
|
+
os10 = __toESM(require("os"));
|
|
7257
|
+
fs5 = __toESM(require("fs"));
|
|
7258
|
+
path6 = __toESM(require("path"));
|
|
5957
7259
|
crypto3 = __toESM(require("crypto"));
|
|
5958
7260
|
import_chalk3 = __toESM(require("chalk"));
|
|
5959
7261
|
init_detector();
|
|
@@ -6022,8 +7324,8 @@ var init_cli_daemon = __esm({
|
|
|
6022
7324
|
this.adapter.setOnStatusChange(() => {
|
|
6023
7325
|
this.sendStatusReport();
|
|
6024
7326
|
});
|
|
6025
|
-
const machineId =
|
|
6026
|
-
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);
|
|
6027
7329
|
const instanceId = `${cliType}_${machineId}_${dirHash}`;
|
|
6028
7330
|
this.bridge = new CliBridgeConnection({
|
|
6029
7331
|
serverUrl: options.serverUrl || config.serverUrl,
|
|
@@ -6031,7 +7333,7 @@ var init_cli_daemon = __esm({
|
|
|
6031
7333
|
cliInfo: {
|
|
6032
7334
|
type: cliType,
|
|
6033
7335
|
version: cliInfo.version || "unknown",
|
|
6034
|
-
platform:
|
|
7336
|
+
platform: os10.platform(),
|
|
6035
7337
|
instanceId
|
|
6036
7338
|
}
|
|
6037
7339
|
});
|
|
@@ -6102,11 +7404,11 @@ var init_cli_daemon = __esm({
|
|
|
6102
7404
|
}
|
|
6103
7405
|
case "list_files": {
|
|
6104
7406
|
const targetDir = args?.dir || this.adapter?.workingDir || process.cwd();
|
|
6105
|
-
const entries =
|
|
7407
|
+
const entries = fs5.readdirSync(targetDir).map((f) => {
|
|
6106
7408
|
try {
|
|
6107
7409
|
return {
|
|
6108
7410
|
name: f,
|
|
6109
|
-
isDirectory:
|
|
7411
|
+
isDirectory: fs5.statSync(path6.join(targetDir, f)).isDirectory()
|
|
6110
7412
|
};
|
|
6111
7413
|
} catch {
|
|
6112
7414
|
return null;
|
|
@@ -6218,14 +7520,14 @@ var init_cli_daemon = __esm({
|
|
|
6218
7520
|
this.sendStatusReport();
|
|
6219
7521
|
});
|
|
6220
7522
|
if (this.bridge) {
|
|
6221
|
-
const machineId =
|
|
6222
|
-
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);
|
|
6223
7525
|
const config2 = this.bridge.options;
|
|
6224
7526
|
config2.cliInfo = {
|
|
6225
7527
|
...config2.cliInfo,
|
|
6226
7528
|
type: cliType,
|
|
6227
7529
|
version: cliInfo.version || "unknown",
|
|
6228
|
-
platform:
|
|
7530
|
+
platform: os10.platform(),
|
|
6229
7531
|
instanceId: `${cliType}_${machineId}_${dirHash}`
|
|
6230
7532
|
};
|
|
6231
7533
|
}
|
|
@@ -6292,13 +7594,13 @@ var init_cli_daemon = __esm({
|
|
|
6292
7594
|
const payload = {
|
|
6293
7595
|
activeFile: null,
|
|
6294
7596
|
workspaceFolders: [{
|
|
6295
|
-
name:
|
|
7597
|
+
name: path6.basename(adapterStatus.workingDir),
|
|
6296
7598
|
path: adapterStatus.workingDir
|
|
6297
7599
|
}],
|
|
6298
7600
|
currentConfig: {
|
|
6299
7601
|
cli: this.bridge?.getCliInfo()?.type || "unknown",
|
|
6300
7602
|
dir: adapterStatus.workingDir,
|
|
6301
|
-
homeDir:
|
|
7603
|
+
homeDir: os10.homedir()
|
|
6302
7604
|
},
|
|
6303
7605
|
terminals: 1,
|
|
6304
7606
|
aiAgents: [{
|
|
@@ -6317,19 +7619,19 @@ var init_cli_daemon = __esm({
|
|
|
6317
7619
|
currentConfig: {
|
|
6318
7620
|
cli: this.bridge?.getCliInfo()?.type || "unknown",
|
|
6319
7621
|
dir: adapterStatus.workingDir,
|
|
6320
|
-
homeDir:
|
|
7622
|
+
homeDir: os10.homedir()
|
|
6321
7623
|
}
|
|
6322
7624
|
}],
|
|
6323
7625
|
system: {
|
|
6324
|
-
platform:
|
|
6325
|
-
release:
|
|
6326
|
-
arch:
|
|
6327
|
-
cpus:
|
|
6328
|
-
totalMem:
|
|
6329
|
-
freeMem:
|
|
6330
|
-
loadavg:
|
|
6331
|
-
uptime:
|
|
6332
|
-
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()
|
|
6333
7635
|
},
|
|
6334
7636
|
detectedIdes: this.detectedIdes,
|
|
6335
7637
|
activeChat: {
|
|
@@ -6368,7 +7670,7 @@ var init_cli_daemon = __esm({
|
|
|
6368
7670
|
process.exit(0);
|
|
6369
7671
|
}
|
|
6370
7672
|
};
|
|
6371
|
-
|
|
7673
|
+
LOG_DIR2 = path6.join(os10.homedir(), ".adhdev", "logs");
|
|
6372
7674
|
}
|
|
6373
7675
|
});
|
|
6374
7676
|
|
|
@@ -6511,8 +7813,8 @@ async function installExtension(ide, extension) {
|
|
|
6511
7813
|
const res = await fetch(extension.vsixUrl);
|
|
6512
7814
|
if (res.ok) {
|
|
6513
7815
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
6514
|
-
const
|
|
6515
|
-
|
|
7816
|
+
const fs6 = await import("fs");
|
|
7817
|
+
fs6.writeFileSync(vsixPath, buffer);
|
|
6516
7818
|
return new Promise((resolve3) => {
|
|
6517
7819
|
const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
|
|
6518
7820
|
(0, import_child_process2.exec)(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
|
|
@@ -6925,18 +8227,18 @@ async function loginFlow() {
|
|
|
6925
8227
|
async function injectTokenToIDE(ide, connectionToken) {
|
|
6926
8228
|
if (!ide.cliCommand) return;
|
|
6927
8229
|
try {
|
|
6928
|
-
const
|
|
6929
|
-
const
|
|
6930
|
-
const
|
|
6931
|
-
const platform9 =
|
|
6932
|
-
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();
|
|
6933
8235
|
const getSettingsPath = (appName2) => {
|
|
6934
8236
|
if (platform9 === "darwin") {
|
|
6935
|
-
return
|
|
8237
|
+
return path7.join(home, "Library", "Application Support", appName2, "User", "settings.json");
|
|
6936
8238
|
} else if (platform9 === "win32") {
|
|
6937
|
-
return
|
|
8239
|
+
return path7.join(process.env.APPDATA || path7.join(home, "AppData", "Roaming"), appName2, "User", "settings.json");
|
|
6938
8240
|
} else {
|
|
6939
|
-
return
|
|
8241
|
+
return path7.join(home, ".config", appName2, "User", "settings.json");
|
|
6940
8242
|
}
|
|
6941
8243
|
};
|
|
6942
8244
|
const appNameMap = {
|
|
@@ -6951,18 +8253,18 @@ async function injectTokenToIDE(ide, connectionToken) {
|
|
|
6951
8253
|
if (!appName) return;
|
|
6952
8254
|
const settingsPath = getSettingsPath(appName);
|
|
6953
8255
|
let settings = {};
|
|
6954
|
-
if (
|
|
8256
|
+
if (fs6.existsSync(settingsPath)) {
|
|
6955
8257
|
try {
|
|
6956
|
-
settings = JSON.parse(
|
|
8258
|
+
settings = JSON.parse(fs6.readFileSync(settingsPath, "utf-8"));
|
|
6957
8259
|
} catch {
|
|
6958
8260
|
settings = {};
|
|
6959
8261
|
}
|
|
6960
8262
|
} else {
|
|
6961
|
-
|
|
8263
|
+
fs6.mkdirSync(path7.dirname(settingsPath), { recursive: true });
|
|
6962
8264
|
}
|
|
6963
8265
|
settings["adhdev.connectionToken"] = connectionToken;
|
|
6964
8266
|
settings["adhdev.autoConnect"] = true;
|
|
6965
|
-
|
|
8267
|
+
fs6.writeFileSync(settingsPath, JSON.stringify(settings, null, 4), "utf-8");
|
|
6966
8268
|
console.log(import_chalk.default.green(" \u2713 Connection token saved to IDE settings"));
|
|
6967
8269
|
} catch (e) {
|
|
6968
8270
|
console.log(import_chalk.default.yellow(` \u26A0 Could not inject token: ${e.message}`));
|
|
@@ -7284,6 +8586,322 @@ program.command("daemon:stop").description("Stop ADHDev Daemon").action(async ()
|
|
|
7284
8586
|
`));
|
|
7285
8587
|
}
|
|
7286
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
|
+
});
|
|
7287
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) => {
|
|
7288
8906
|
console.log(import_chalk4.default.yellow(' \u26A0 "adhdev bridge" is deprecated. Use "adhdev daemon --cli <type>" instead.\n'));
|
|
7289
8907
|
const { CliDaemon: CliDaemon2 } = await Promise.resolve().then(() => (init_cli_daemon(), cli_daemon_exports));
|
|
@@ -7348,8 +8966,8 @@ program.command("bridge:logs").description("[Legacy] Show CLI bridge service log
|
|
|
7348
8966
|
const logPath = getLogPath2();
|
|
7349
8967
|
const { execSync: execSync8, spawn: spawnProc } = await import("child_process");
|
|
7350
8968
|
try {
|
|
7351
|
-
const
|
|
7352
|
-
if (!
|
|
8969
|
+
const fs6 = await import("fs");
|
|
8970
|
+
if (!fs6.existsSync(logPath)) {
|
|
7353
8971
|
console.log(import_chalk4.default.gray("\n No logs yet. Start the bridge first.\n"));
|
|
7354
8972
|
return;
|
|
7355
8973
|
}
|