adhdev 0.1.35 → 0.1.37

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.
Files changed (2) hide show
  1. package/dist/index.js +1884 -266
  2. 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 os10 = (0, import_os.platform)();
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[os10] || []);
549
+ const appPath = checkPathExists(def.paths[os11] || []);
95
550
  const installed = !!(cliPath || appPath);
96
551
  let resolvedCli = cliPath;
97
- if (!resolvedCli && appPath && os10 === "darwin") {
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 && os10 === "win32") {
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 fs7 = require("fs");
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 (fs7.existsSync(storagePath)) {
558
- const data = JSON.parse(fs7.readFileSync(storagePath, "utf-8"));
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, fs, KNOWN_AGENTS, DaemonCdpManager;
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 fs2, path2, os6, logFile, log, DaemonP2PSender;
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
- fs2 = __toESM(require("fs"));
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 (fs2.existsSync(prebuildPath)) {
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
- fs2.mkdirSync(targetDir, { recursive: true });
2883
- fs2.copyFileSync(prebuildPath, targetPath);
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(fs2.readFileSync(configPath, "utf-8"));
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
- try {
3358
- fs3.appendFileSync(`${require("os").tmpdir()}/adhdev_daemon_scripts.log`, `${msg}
3359
- `);
3360
- } catch {
3361
- }
3885
+ console.log(msg);
3362
3886
  }
3363
- var fs3, http2, https, DaemonScriptLoader;
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 fs4, path3, os7, DaemonCommandHandler;
4110
+ var fs2, path3, os7, DaemonCommandHandler;
3512
4111
  var init_daemon_commands = __esm({
3513
4112
  "src/daemon-commands.ts"() {
3514
4113
  "use strict";
3515
- fs4 = __toESM(require("fs"));
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, or first connected one */
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 (e.g. 'vscode_abc123' → 'vscode') */
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
- const parts = args._targetInstance.split("_");
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
- if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
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 (!script) return { success: false, error: "read_chat script not loaded" };
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 result = await this.getCdp().evaluate(script, 5e4);
3663
- return { success: true, ...result };
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
- return { success: false, error: e.message };
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
- console.log(`[send_chat] Routing to CLI adapter: ${adapter.cliType} (${key})`);
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
- console.log(`[send_chat] CLI adapter failed: ${e.message}`);
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 && targetLoader) {
3689
- const script = targetLoader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
3690
- if (script) {
3691
- console.log(`[send_chat] Targeting IDE: ${this._currentIdeType || "fallback-first"}`);
3692
- try {
3693
- await targetCdp.evaluate(script, 3e4);
3694
- return { success: true, sent: true };
3695
- } catch (e) {
3696
- console.log(`[send_chat] Failed on ${this._currentIdeType}: ${e.message}`);
4344
+ if (targetCdp?.isConnected) {
4345
+ _log(`Targeting IDE: ${this._currentIdeType || "fallback-first"}`);
4346
+ const inputSelector = '[data-lexical-editor="true"]';
4347
+ try {
4348
+ const sent = await targetCdp.typeAndSend(inputSelector, text);
4349
+ if (sent) {
4350
+ _log(`typeAndSend success`);
4351
+ return { success: true, sent: true, method: "typeAndSend" };
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
- console.log(`[send_chat] No specific target, trying all CDPs...`);
3701
- for (const [ideType, cdp] of this.ctx.cdpManagers) {
3702
- if (!cdp.isConnected) continue;
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
- console.log(`[send_chat] Trying IDE: ${ideType}`);
4400
+ _log(`Trying script on ${ideType}`);
3708
4401
  try {
3709
- await cdp.evaluate(script, 3e4);
3710
- return { success: true, sent: true, targetIde: ideType };
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
- console.log(`[send_chat] Failed on ${ideType}: ${e.message}`);
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 cdp = this.getCdp();
3719
- if (!cdp?.isConnected) {
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 script = this.getScriptLoader()?.get("list_chats");
3724
- if (!script) {
3725
- console.log(`[list_chats] script not loaded`);
3726
- return { success: false, error: "list_chats script not loaded" };
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
- console.log(`[list_chats] Evaluating on ideType=${this._currentIdeType}`);
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
- console.log(`[list_chats] Result type=${typeof parsed}, isArray=${Array.isArray(parsed)}, len=${Array.isArray(parsed) ? parsed.length : "N/A"}`);
3739
- return { success: true, chats: parsed };
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] Error: ${e.message}`);
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
- if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
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
- const script = this.getScriptLoader()?.getWithParams("switch_session", { SESSION_ID: sessionId });
3761
- if (!script) return { success: false, error: "switch_session script not loaded" };
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 this.getCdp().evaluate(script, 15e3);
3764
- return { success: true };
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
- async handleFileRead(args) {
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
- const filePath = this.resolveSafePath(args?.path);
3957
- const content = fs4.readFileSync(filePath, "utf-8");
3958
- return { success: true, content, path: filePath };
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
- async handleFileWrite(args) {
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
- const filePath = this.resolveSafePath(args?.path);
3966
- fs4.mkdirSync(path3.dirname(filePath), { recursive: true });
3967
- fs4.writeFileSync(filePath, args?.content || "", "utf-8");
3968
- return { success: true, path: filePath };
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
- async handleFileList(args) {
3974
- try {
3975
- const dirPath = this.resolveSafePath(args?.path || ".");
3976
- const entries = fs4.readdirSync(dirPath, { withFileTypes: true });
3977
- const files = entries.map((e) => ({
3978
- name: e.name,
3979
- type: e.isDirectory() ? "directory" : "file",
3980
- size: e.isFile() ? fs4.statSync(path3.join(dirPath, e.name)).size : void 0
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 (fs4.existsSync(stateFile)) {
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
- adapter.resize(cols, rows);
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(cdp, agentType) {
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 cdp.detachAgent(prev.sessionId);
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(cdp) {
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 cdp.discoverAgentWebviews();
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 cdp.attachToAgent(activeTarget);
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 cdp.detachAgent(agent.sessionId);
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(cdp) {
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) => cdp.evaluateInSession(agent.sessionId, 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 cdp.detachAgent(agent.sessionId);
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(cdp, agentType, text, targetIdeType) {
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) => cdp.evaluateInSession(agent.sessionId, 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(cdp, agentType, action, targetIdeType) {
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) => cdp.evaluateInSession(agent.sessionId, 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(cdp, agentType, targetIdeType) {
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) => cdp.evaluateInSession(agent.sessionId, 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(cdp, agentType) {
4579
- const agent = this.managed.get(agentType);
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) => cdp.evaluateInSession(agent.sessionId, 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(cdp, agentType, sessionId) {
4590
- const agent = this.managed.get(agentType);
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) => cdp.evaluateInSession(agent.sessionId, 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(cdp, agentType) {
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) => cdp.evaluateInSession(agent.sessionId, 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(cdp) {
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 cdp.detachAgent(agent.sessionId);
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 = path4.join(os8.homedir(), ".adhdev");
4661
- if (!fs5.existsSync(dir)) fs5.mkdirSync(dir, { recursive: true });
4662
- return path4.join(dir, "daemon.pid");
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
- fs5.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
5779
+ fs4.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
4666
5780
  }
4667
5781
  function removeDaemonPid() {
4668
5782
  try {
4669
- fs5.unlinkSync(getDaemonPidFile());
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 (!fs5.existsSync(pidFile)) return false;
4677
- const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
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 (!fs5.existsSync(pidFile)) return false;
4689
- const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
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 os8, fs5, path4, crypto2, import_chalk2, DANGEROUS_PATTERNS, AdhdevDaemon;
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
- os8 = __toESM(require("os"));
4718
- fs5 = __toESM(require("fs"));
4719
- path4 = __toESM(require("path"));
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 = os8.hostname().replace(/[^a-zA-Z0-9]/g, "_");
4877
- const machineHash = crypto2.createHash("md5").update(os8.hostname() + os8.homedir()).digest("hex").slice(0, 8);
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: os8.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
- if (!this.commandHandler) return { success: false, error: "No handler" };
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 cdp = ssIdeType ? this.getCdpFor(ssIdeType) : this.getAnyCdp();
4933
- if (!active || !cdp) return;
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 cdp.captureScreenshot();
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(import_chalk2.default.gray(msg2));
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(/^~/, os8.homedir()) : workingDir;
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.p2pSender?.broadcastPtyOutput(adapter.cliType, data);
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
- }, 15e3);
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, cdp] of this.cdpManagers) {
5331
- if (!cdp.isConnected) continue;
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 cdp.evaluate(readChatScript, 3e4);
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
- this._cachedActiveChatMap.set(ideType, { ...raw, activeModal });
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: os8.hostname(),
5461
- platform: os8.platform(),
5462
- release: os8.release(),
5463
- arch: os8.arch(),
5464
- cpus: os8.cpus().length,
5465
- totalMem: os8.totalmem(),
5466
- freeMem: os8.freemem(),
5467
- loadavg: os8.loadavg(),
5468
- uptime: os8.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: path4.basename(a.workingDir), path: a.workingDir })),
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: os8.platform(),
5511
- release: os8.release(),
5512
- arch: os8.arch(),
5513
- cpus: os8.cpus().length,
5514
- totalMem: os8.totalmem(),
5515
- freeMem: os8.freemem(),
5516
- loadavg: os8.loadavg(),
5517
- uptime: os8.uptime(),
5518
- hostname: os8.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 % 4 === 0;
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
- for (const [ideType, cdp] of this.cdpManagers) {
5619
- if (!cdp.isConnected) continue;
5620
- try {
5621
- await this.agentStreamManager.syncAgentSessions(cdp);
5622
- const streams = await this.agentStreamManager.collectAgentStreams(cdp);
5623
- this._cachedAgentStreamsMap.set(ideType, streams);
5624
- } catch {
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(import_chalk2.default.gray(msg));
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 = path5.resolve(workingDir);
6996
+ const absPath = path6.resolve(workingDir);
5695
6997
  const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
5696
- const dir = path5.join(os9.homedir(), ".adhdev");
5697
- if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
5698
- return path5.join(dir, `bridge-${cliType}-${hash}.pid`);
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
- fs6.writeFileSync(pidFile, String(pid), "utf-8");
7004
+ fs5.writeFileSync(pidFile, String(pid), "utf-8");
5703
7005
  }
5704
7006
  function removePidFile(workingDir, cliType) {
5705
7007
  try {
5706
- fs6.unlinkSync(getPidFile(workingDir, cliType));
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 (!fs6.existsSync(pidFile)) return false;
5714
- const pid = parseInt(fs6.readFileSync(pidFile, "utf-8").trim());
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 (!fs6.existsSync(pidFile)) return false;
5726
- const pid = parseInt(fs6.readFileSync(pidFile, "utf-8").trim());
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 = path5.resolve(workingDir);
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 = path5.resolve(workingDir);
7043
+ const absPath = path6.resolve(workingDir);
5742
7044
  const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
5743
- if (!fs6.existsSync(LOG_DIR)) fs6.mkdirSync(LOG_DIR, { recursive: true });
5744
- return path5.join(LOG_DIR, `bridge-${cliType}-${hash}${isErr ? ".err" : ""}.log`);
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 = os9.platform();
7049
+ const platform9 = os10.platform();
5748
7050
  if (platform9 === "darwin") {
5749
7051
  return {
5750
- plist: path5.join(os9.homedir(), "Library", "LaunchAgents", `${label}.plist`)
7052
+ plist: path6.join(os10.homedir(), "Library", "LaunchAgents", `${label}.plist`)
5751
7053
  };
5752
7054
  } else if (platform9 === "linux") {
5753
7055
  return {
5754
- systemd: path5.join(os9.homedir(), ".config", "systemd", "user", `${label}.service`)
7056
+ systemd: path6.join(os10.homedir(), ".config", "systemd", "user", `${label}.service`)
5755
7057
  };
5756
7058
  } else if (platform9 === "win32") {
5757
7059
  return {
5758
- startup: path5.join(os9.homedir(), "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs", "Startup", `${label}.vbs`)
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 = os9.platform();
7066
+ const platform9 = os10.platform();
5765
7067
  const cliType = options.cliType || "gemini-cli";
5766
- const workingDir = options.workingDir || os9.homedir();
7068
+ const workingDir = options.workingDir || os10.homedir();
5767
7069
  const adhdevBin = process.argv[1];
5768
7070
  const nodeBin = process.execPath;
5769
- if (!fs6.existsSync(LOG_DIR)) fs6.mkdirSync(LOG_DIR, { recursive: true });
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>${os9.homedir()}</string>
7126
+ <string>${os10.homedir()}</string>
5825
7127
  </dict>
5826
7128
  </dict>
5827
7129
  </plist>`;
5828
- const agentsDir = path5.dirname(plistPath);
5829
- if (!fs6.existsSync(agentsDir)) fs6.mkdirSync(agentsDir, { recursive: true });
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
- fs6.writeFileSync(plistPath, plistContent, "utf-8");
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=${os9.homedir()}
7162
+ Environment=HOME=${os10.homedir()}
5861
7163
 
5862
7164
  [Install]
5863
7165
  WantedBy=default.target
5864
7166
  `;
5865
- const serviceDir = path5.dirname(servicePath);
5866
- if (!fs6.existsSync(serviceDir)) fs6.mkdirSync(serviceDir, { recursive: true });
5867
- fs6.writeFileSync(servicePath, serviceContent, "utf-8");
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 = path5.dirname(startupPath);
5885
- if (!fs6.existsSync(startupDir)) fs6.mkdirSync(startupDir, { recursive: true });
5886
- fs6.writeFileSync(startupPath, vbsContent, "utf-8");
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 = os9.platform();
7197
+ const platform9 = os10.platform();
5896
7198
  const cliType = options.cliType || "gemini-cli";
5897
- const workingDir = options.workingDir || os9.homedir();
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 (fs6.existsSync(paths.plist)) fs6.unlinkSync(paths.plist);
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 (fs6.existsSync(paths.systemd)) fs6.unlinkSync(paths.systemd);
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 (fs6.existsSync(paths.startup)) fs6.unlinkSync(paths.startup);
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 = os9.homedir(), cliType = "gemini-cli") {
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 fs6.existsSync(paths.plist);
5937
- if (paths.systemd) return fs6.existsSync(paths.systemd);
5938
- if (paths.startup) return fs6.existsSync(paths.startup);
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 = os9.homedir(), cliType = "gemini-cli") {
7243
+ function getLogPath(workingDir = os10.homedir(), cliType = "gemini-cli") {
5942
7244
  return getLogFile(workingDir, cliType);
5943
7245
  }
5944
- var os9, fs6, path5, crypto3, import_chalk3, DANGEROUS_PATTERNS2, CliDaemon, LOG_DIR;
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
- os9 = __toESM(require("os"));
5955
- fs6 = __toESM(require("fs"));
5956
- path5 = __toESM(require("path"));
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 = os9.hostname().replace(/[^a-zA-Z0-9]/g, "_");
6026
- const dirHash = crypto3.createHash("md5").update(path5.resolve(workingDir)).digest("hex").slice(0, 4);
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: os9.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 = fs6.readdirSync(targetDir).map((f) => {
7407
+ const entries = fs5.readdirSync(targetDir).map((f) => {
6106
7408
  try {
6107
7409
  return {
6108
7410
  name: f,
6109
- isDirectory: fs6.statSync(path5.join(targetDir, f)).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 = os9.hostname().replace(/[^a-zA-Z0-9]/g, "_");
6222
- const dirHash = crypto3.createHash("md5").update(path5.resolve(workingDir)).digest("hex").slice(0, 4);
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: os9.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: path5.basename(adapterStatus.workingDir),
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: os9.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: os9.homedir()
7622
+ homeDir: os10.homedir()
6321
7623
  }
6322
7624
  }],
6323
7625
  system: {
6324
- platform: os9.platform(),
6325
- release: os9.release(),
6326
- arch: os9.arch(),
6327
- cpus: os9.cpus().length,
6328
- totalMem: os9.totalmem(),
6329
- freeMem: os9.freemem(),
6330
- loadavg: os9.loadavg(),
6331
- uptime: os9.uptime(),
6332
- hostname: os9.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
- LOG_DIR = path5.join(os9.homedir(), ".adhdev", "logs");
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 fs7 = await import("fs");
6515
- fs7.writeFileSync(vsixPath, buffer);
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 os10 = await import("os");
6929
- const fs7 = await import("fs");
6930
- const path6 = await import("path");
6931
- const platform9 = os10.platform();
6932
- const home = os10.homedir();
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 path6.join(home, "Library", "Application Support", appName2, "User", "settings.json");
8237
+ return path7.join(home, "Library", "Application Support", appName2, "User", "settings.json");
6936
8238
  } else if (platform9 === "win32") {
6937
- return path6.join(process.env.APPDATA || path6.join(home, "AppData", "Roaming"), appName2, "User", "settings.json");
8239
+ return path7.join(process.env.APPDATA || path7.join(home, "AppData", "Roaming"), appName2, "User", "settings.json");
6938
8240
  } else {
6939
- return path6.join(home, ".config", appName2, "User", "settings.json");
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 (fs7.existsSync(settingsPath)) {
8256
+ if (fs6.existsSync(settingsPath)) {
6955
8257
  try {
6956
- settings = JSON.parse(fs7.readFileSync(settingsPath, "utf-8"));
8258
+ settings = JSON.parse(fs6.readFileSync(settingsPath, "utf-8"));
6957
8259
  } catch {
6958
8260
  settings = {};
6959
8261
  }
6960
8262
  } else {
6961
- fs7.mkdirSync(path6.dirname(settingsPath), { recursive: true });
8263
+ fs6.mkdirSync(path7.dirname(settingsPath), { recursive: true });
6962
8264
  }
6963
8265
  settings["adhdev.connectionToken"] = connectionToken;
6964
8266
  settings["adhdev.autoConnect"] = true;
6965
- fs7.writeFileSync(settingsPath, JSON.stringify(settings, null, 4), "utf-8");
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 fs7 = await import("fs");
7352
- if (!fs7.existsSync(logPath)) {
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
  }