adhdev 0.1.21 → 0.1.23

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 +826 -323
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -325,11 +325,61 @@ var init_config = __esm({
325
325
  userName: null,
326
326
  setupCompleted: false,
327
327
  setupDate: null,
328
- configuredCLIs: []
328
+ configuredCLIs: [],
329
+ enabledIdes: []
329
330
  };
330
331
  }
331
332
  });
332
333
 
334
+ // src/cli-detector.ts
335
+ async function detectCLIs() {
336
+ const platform9 = os.platform();
337
+ const whichCmd = platform9 === "win32" ? "where" : "which";
338
+ const results = [];
339
+ for (const cli of KNOWN_CLIS) {
340
+ try {
341
+ const pathResult = (0, import_child_process3.execSync)(`${whichCmd} ${cli.command} 2>/dev/null`, {
342
+ encoding: "utf-8",
343
+ timeout: 5e3,
344
+ stdio: ["pipe", "pipe", "pipe"]
345
+ }).trim().split("\n")[0];
346
+ if (!pathResult) throw new Error("Not found");
347
+ let version;
348
+ try {
349
+ const versionResult = (0, import_child_process3.execSync)(`${cli.command} --version 2>/dev/null`, {
350
+ encoding: "utf-8",
351
+ timeout: 5e3,
352
+ stdio: ["pipe", "pipe", "pipe"]
353
+ }).trim();
354
+ const match = versionResult.match(/(\d+\.\d+[\.\d]*)/);
355
+ version = match ? match[1] : versionResult.split("\n")[0].slice(0, 30);
356
+ } catch {
357
+ }
358
+ results.push({ ...cli, installed: true, version, path: pathResult });
359
+ } catch {
360
+ results.push({ ...cli, installed: false });
361
+ }
362
+ }
363
+ return results;
364
+ }
365
+ async function detectCLI(cliId) {
366
+ const all = await detectCLIs();
367
+ return all.find((c) => c.id === cliId && c.installed) || null;
368
+ }
369
+ var import_child_process3, os, KNOWN_CLIS;
370
+ var init_cli_detector = __esm({
371
+ "src/cli-detector.ts"() {
372
+ "use strict";
373
+ import_child_process3 = require("child_process");
374
+ os = __toESM(require("os"));
375
+ KNOWN_CLIS = [
376
+ { id: "gemini-cli", displayName: "Gemini CLI", icon: "\u264A", command: "gemini" },
377
+ { id: "claude-code", displayName: "Claude Code", icon: "\u{1F916}", command: "claude" },
378
+ { id: "codex-cli", displayName: "Codex CLI", icon: "\u{1F9E0}", command: "codex" }
379
+ ];
380
+ }
381
+ });
382
+
333
383
  // src/launch.ts
334
384
  async function findFreePort(ports) {
335
385
  for (const port2 of ports) {
@@ -377,38 +427,38 @@ async function isCdpActive(port) {
377
427
  });
378
428
  }
379
429
  async function killIdeProcess(ideId) {
380
- const plat = os.platform();
430
+ const plat = os2.platform();
381
431
  const appName = MAC_APP_IDENTIFIERS[ideId];
382
432
  const winProcesses = WIN_PROCESS_NAMES[ideId];
383
433
  try {
384
434
  if (plat === "darwin" && appName) {
385
435
  try {
386
- (0, import_child_process3.execSync)(`osascript -e 'tell application "${appName}" to quit' 2>/dev/null`, {
436
+ (0, import_child_process4.execSync)(`osascript -e 'tell application "${appName}" to quit' 2>/dev/null`, {
387
437
  timeout: 5e3
388
438
  });
389
439
  } catch {
390
440
  try {
391
- (0, import_child_process3.execSync)(`pkill -f "${appName}" 2>/dev/null`);
441
+ (0, import_child_process4.execSync)(`pkill -f "${appName}" 2>/dev/null`);
392
442
  } catch {
393
443
  }
394
444
  }
395
445
  } else if (plat === "win32" && winProcesses) {
396
446
  for (const proc of winProcesses) {
397
447
  try {
398
- (0, import_child_process3.execSync)(`taskkill /IM "${proc}" /F 2>nul`, { timeout: 5e3 });
448
+ (0, import_child_process4.execSync)(`taskkill /IM "${proc}" /F 2>nul`, { timeout: 5e3 });
399
449
  } catch {
400
450
  }
401
451
  }
402
452
  try {
403
453
  const exeName = winProcesses[0].replace(".exe", "");
404
- (0, import_child_process3.execSync)(`powershell -Command "Get-Process -Name '${exeName}' -ErrorAction SilentlyContinue | Stop-Process -Force"`, {
454
+ (0, import_child_process4.execSync)(`powershell -Command "Get-Process -Name '${exeName}' -ErrorAction SilentlyContinue | Stop-Process -Force"`, {
405
455
  timeout: 1e4
406
456
  });
407
457
  } catch {
408
458
  }
409
459
  } else {
410
460
  try {
411
- (0, import_child_process3.execSync)(`pkill -f "${ideId}" 2>/dev/null`);
461
+ (0, import_child_process4.execSync)(`pkill -f "${ideId}" 2>/dev/null`);
412
462
  } catch {
413
463
  }
414
464
  }
@@ -418,13 +468,13 @@ async function killIdeProcess(ideId) {
418
468
  }
419
469
  if (plat === "darwin" && appName) {
420
470
  try {
421
- (0, import_child_process3.execSync)(`pkill -9 -f "${appName}" 2>/dev/null`);
471
+ (0, import_child_process4.execSync)(`pkill -9 -f "${appName}" 2>/dev/null`);
422
472
  } catch {
423
473
  }
424
474
  } else if (plat === "win32" && winProcesses) {
425
475
  for (const proc of winProcesses) {
426
476
  try {
427
- (0, import_child_process3.execSync)(`taskkill /IM "${proc}" /F 2>nul`);
477
+ (0, import_child_process4.execSync)(`taskkill /IM "${proc}" /F 2>nul`);
428
478
  } catch {
429
479
  }
430
480
  }
@@ -436,26 +486,26 @@ async function killIdeProcess(ideId) {
436
486
  }
437
487
  }
438
488
  function isIdeRunning(ideId) {
439
- const plat = os.platform();
489
+ const plat = os2.platform();
440
490
  try {
441
491
  if (plat === "darwin") {
442
492
  const appName = MAC_APP_IDENTIFIERS[ideId];
443
493
  if (!appName) return false;
444
- const result = (0, import_child_process3.execSync)(`pgrep -f "${appName}" 2>/dev/null`, { encoding: "utf-8" });
494
+ const result = (0, import_child_process4.execSync)(`pgrep -f "${appName}" 2>/dev/null`, { encoding: "utf-8" });
445
495
  return result.trim().length > 0;
446
496
  } else if (plat === "win32") {
447
497
  const winProcesses = WIN_PROCESS_NAMES[ideId];
448
498
  if (!winProcesses) return false;
449
499
  for (const proc of winProcesses) {
450
500
  try {
451
- const result = (0, import_child_process3.execSync)(`tasklist /FI "IMAGENAME eq ${proc}" /NH 2>nul`, { encoding: "utf-8" });
501
+ const result = (0, import_child_process4.execSync)(`tasklist /FI "IMAGENAME eq ${proc}" /NH 2>nul`, { encoding: "utf-8" });
452
502
  if (result.includes(proc)) return true;
453
503
  } catch {
454
504
  }
455
505
  }
456
506
  try {
457
507
  const exeName = winProcesses[0].replace(".exe", "");
458
- const result = (0, import_child_process3.execSync)(
508
+ const result = (0, import_child_process4.execSync)(
459
509
  `powershell -Command "(Get-Process -Name '${exeName}' -ErrorAction SilentlyContinue).Count"`,
460
510
  { encoding: "utf-8", timeout: 5e3 }
461
511
  );
@@ -464,7 +514,7 @@ function isIdeRunning(ideId) {
464
514
  }
465
515
  return false;
466
516
  } else {
467
- const result = (0, import_child_process3.execSync)(`pgrep -f "${ideId}" 2>/dev/null`, { encoding: "utf-8" });
517
+ const result = (0, import_child_process4.execSync)(`pgrep -f "${ideId}" 2>/dev/null`, { encoding: "utf-8" });
468
518
  return result.trim().length > 0;
469
519
  }
470
520
  } catch {
@@ -472,12 +522,12 @@ function isIdeRunning(ideId) {
472
522
  }
473
523
  }
474
524
  function detectCurrentWorkspace(ideId) {
475
- const plat = os.platform();
525
+ const plat = os2.platform();
476
526
  if (plat === "darwin") {
477
527
  try {
478
528
  const appName = MAC_APP_IDENTIFIERS[ideId];
479
529
  if (!appName) return void 0;
480
- const result = (0, import_child_process3.execSync)(
530
+ const result = (0, import_child_process4.execSync)(
481
531
  `lsof -c "${appName}" 2>/dev/null | grep cwd | head -1 | awk '{print $NF}'`,
482
532
  { encoding: "utf-8", timeout: 3e3 }
483
533
  );
@@ -498,7 +548,7 @@ function detectCurrentWorkspace(ideId) {
498
548
  const appName = appNameMap[ideId];
499
549
  if (appName) {
500
550
  const storagePath = path.join(
501
- process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming"),
551
+ process.env.APPDATA || path.join(os2.homedir(), "AppData", "Roaming"),
502
552
  appName,
503
553
  "storage.json"
504
554
  );
@@ -520,7 +570,7 @@ function detectCurrentWorkspace(ideId) {
520
570
  return void 0;
521
571
  }
522
572
  async function launchWithCdp(options = {}) {
523
- const platform9 = os.platform();
573
+ const platform9 = os2.platform();
524
574
  let targetIde;
525
575
  const ides = await detectIDEs();
526
576
  if (options.ideId) {
@@ -630,9 +680,9 @@ async function launchMacOS(ide, port, workspace, newWindow) {
630
680
  if (workspace) args.push(workspace);
631
681
  if (appName) {
632
682
  const openArgs = ["-a", appName, "--args", ...args];
633
- (0, import_child_process3.spawn)("open", openArgs, { detached: true, stdio: "ignore" }).unref();
683
+ (0, import_child_process4.spawn)("open", openArgs, { detached: true, stdio: "ignore" }).unref();
634
684
  } else if (ide.cliCommand) {
635
- (0, import_child_process3.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
685
+ (0, import_child_process4.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
636
686
  } else {
637
687
  throw new Error(`No app identifier or CLI for ${ide.displayName}`);
638
688
  }
@@ -658,15 +708,15 @@ async function launchLinux(ide, port, workspace, newWindow) {
658
708
  const args = ["--remote-debugging-port=" + port];
659
709
  if (newWindow) args.push("--new-window");
660
710
  if (workspace) args.push(workspace);
661
- (0, import_child_process3.spawn)(cli, args, { detached: true, stdio: "ignore" }).unref();
711
+ (0, import_child_process4.spawn)(cli, args, { detached: true, stdio: "ignore" }).unref();
662
712
  }
663
- var import_child_process3, net, os, path, CDP_PORTS, MAC_APP_IDENTIFIERS, WIN_PROCESS_NAMES;
713
+ var import_child_process4, net, os2, path, CDP_PORTS, MAC_APP_IDENTIFIERS, WIN_PROCESS_NAMES;
664
714
  var init_launch = __esm({
665
715
  "src/launch.ts"() {
666
716
  "use strict";
667
- import_child_process3 = require("child_process");
717
+ import_child_process4 = require("child_process");
668
718
  net = __toESM(require("net"));
669
- os = __toESM(require("os"));
719
+ os2 = __toESM(require("os"));
670
720
  path = __toESM(require("path"));
671
721
  init_detector();
672
722
  init_config();
@@ -821,6 +871,8 @@ var init_cli_bridge = __esm({
821
871
  const handlers = this.messageHandlers.get(message.type);
822
872
  if (handlers) {
823
873
  handlers.forEach((h) => h(message));
874
+ } else if (message.type !== "auth_ok") {
875
+ console.log(`[CliBridge] Unhandled message type: ${message.type}`);
824
876
  }
825
877
  } catch (error) {
826
878
  console.error("[CliBridge] Failed to parse message:", error);
@@ -881,21 +933,21 @@ function stripAnsi(str) {
881
933
  return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][^\x1B]*\x1B\\/g, "");
882
934
  }
883
935
  function findGeminiBinary() {
884
- const isWin = os2.platform() === "win32";
936
+ const isWin = os3.platform() === "win32";
885
937
  const cmd = isWin ? "where gemini" : "which gemini";
886
938
  try {
887
- const result = (0, import_child_process4.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
939
+ const result = (0, import_child_process5.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
888
940
  return result.split("\n")[0].trim();
889
941
  } catch {
890
942
  return isWin ? "gemini.cmd" : "gemini";
891
943
  }
892
944
  }
893
- var os2, import_child_process4, pty, PROMPT_PATTERNS, STARTUP_DIALOG_PATTERNS, GeminiCliAdapter;
945
+ var os3, import_child_process5, pty, PROMPT_PATTERNS, STARTUP_DIALOG_PATTERNS, GeminiCliAdapter;
894
946
  var init_gemini_cli = __esm({
895
947
  "src/cli-adapters/gemini-cli.ts"() {
896
948
  "use strict";
897
- os2 = __toESM(require("os"));
898
- import_child_process4 = require("child_process");
949
+ os3 = __toESM(require("os"));
950
+ import_child_process5 = require("child_process");
899
951
  try {
900
952
  pty = require("node-pty");
901
953
  } catch {
@@ -1147,21 +1199,21 @@ function stripAnsi2(str) {
1147
1199
  return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][^\x1B]*\x1B\\/g, "");
1148
1200
  }
1149
1201
  function findClaudeBinary() {
1150
- const isWin = os3.platform() === "win32";
1202
+ const isWin = os4.platform() === "win32";
1151
1203
  const cmd = isWin ? "where claude" : "which claude";
1152
1204
  try {
1153
- const result = (0, import_child_process5.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
1205
+ const result = (0, import_child_process6.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
1154
1206
  return result.split("\n")[0].trim();
1155
1207
  } catch {
1156
1208
  return isWin ? "claude.cmd" : "claude";
1157
1209
  }
1158
1210
  }
1159
- var os3, import_child_process5, pty2, ClaudeCliAdapter;
1211
+ var os4, import_child_process6, pty2, ClaudeCliAdapter;
1160
1212
  var init_claude_cli = __esm({
1161
1213
  "src/cli-adapters/claude-cli.ts"() {
1162
1214
  "use strict";
1163
- os3 = __toESM(require("os"));
1164
- import_child_process5 = require("child_process");
1215
+ os4 = __toESM(require("os"));
1216
+ import_child_process6 = require("child_process");
1165
1217
  try {
1166
1218
  pty2 = require("node-pty");
1167
1219
  } catch {
@@ -1359,21 +1411,21 @@ function stripAnsi3(str) {
1359
1411
  return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][^\x1B]*\x1B\\/g, "");
1360
1412
  }
1361
1413
  function findCodexBinary() {
1362
- const isWin = os4.platform() === "win32";
1414
+ const isWin = os5.platform() === "win32";
1363
1415
  const cmd = isWin ? "where codex" : "which codex";
1364
1416
  try {
1365
- const result = (0, import_child_process6.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
1417
+ const result = (0, import_child_process7.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
1366
1418
  return result.split("\n")[0].trim();
1367
1419
  } catch {
1368
1420
  return isWin ? "codex.cmd" : "codex";
1369
1421
  }
1370
1422
  }
1371
- var os4, import_child_process6, pty3, CodexCliAdapter;
1423
+ var os5, import_child_process7, pty3, CodexCliAdapter;
1372
1424
  var init_codex_cli = __esm({
1373
1425
  "src/cli-adapters/codex-cli.ts"() {
1374
1426
  "use strict";
1375
- os4 = __toESM(require("os"));
1376
- import_child_process6 = require("child_process");
1427
+ os5 = __toESM(require("os"));
1428
+ import_child_process7 = require("child_process");
1377
1429
  try {
1378
1430
  pty3 = require("node-pty");
1379
1431
  } catch {
@@ -1826,13 +1878,16 @@ var init_local_server = __esm({
1826
1878
  /**
1827
1879
  * 가장 최근 Extension 상태 데이터 (통합)
1828
1880
  */
1829
- getLatestExtensionData() {
1881
+ getLatestExtensionData(enabledIdes) {
1830
1882
  let activeFile = null;
1831
1883
  const allWorkspaceFolders = [];
1832
1884
  let totalTerminals = 0;
1833
1885
  const allAgents = [];
1834
1886
  const connectedIdes = [];
1835
1887
  for (const ext of this.extensions.values()) {
1888
+ if (enabledIdes && enabledIdes.length > 0 && !enabledIdes.includes(ext.ideType.toLowerCase())) {
1889
+ continue;
1890
+ }
1836
1891
  connectedIdes.push({ ideType: ext.ideType, instanceId: ext.instanceId });
1837
1892
  if (ext.lastStatus) {
1838
1893
  if (!activeFile && ext.lastStatus.activeFile) {
@@ -1855,55 +1910,6 @@ var init_local_server = __esm({
1855
1910
  }
1856
1911
  });
1857
1912
 
1858
- // src/cli-detector.ts
1859
- async function detectCLIs() {
1860
- const platform9 = os5.platform();
1861
- const whichCmd = platform9 === "win32" ? "where" : "which";
1862
- const results = [];
1863
- for (const cli of KNOWN_CLIS) {
1864
- try {
1865
- const pathResult = (0, import_child_process7.execSync)(`${whichCmd} ${cli.command} 2>/dev/null`, {
1866
- encoding: "utf-8",
1867
- timeout: 5e3,
1868
- stdio: ["pipe", "pipe", "pipe"]
1869
- }).trim().split("\n")[0];
1870
- if (!pathResult) throw new Error("Not found");
1871
- let version;
1872
- try {
1873
- const versionResult = (0, import_child_process7.execSync)(`${cli.command} --version 2>/dev/null`, {
1874
- encoding: "utf-8",
1875
- timeout: 5e3,
1876
- stdio: ["pipe", "pipe", "pipe"]
1877
- }).trim();
1878
- const match = versionResult.match(/(\d+\.\d+[\.\d]*)/);
1879
- version = match ? match[1] : versionResult.split("\n")[0].slice(0, 30);
1880
- } catch {
1881
- }
1882
- results.push({ ...cli, installed: true, version, path: pathResult });
1883
- } catch {
1884
- results.push({ ...cli, installed: false });
1885
- }
1886
- }
1887
- return results;
1888
- }
1889
- async function detectCLI(cliId) {
1890
- const all = await detectCLIs();
1891
- return all.find((c) => c.id === cliId && c.installed) || null;
1892
- }
1893
- var import_child_process7, os5, KNOWN_CLIS;
1894
- var init_cli_detector = __esm({
1895
- "src/cli-detector.ts"() {
1896
- "use strict";
1897
- import_child_process7 = require("child_process");
1898
- os5 = __toESM(require("os"));
1899
- KNOWN_CLIS = [
1900
- { id: "gemini-cli", displayName: "Gemini CLI", icon: "\u264A", command: "gemini" },
1901
- { id: "claude-code", displayName: "Claude Code", icon: "\u{1F916}", command: "claude" },
1902
- { id: "codex-cli", displayName: "Codex CLI", icon: "\u{1F9E0}", command: "codex" }
1903
- ];
1904
- }
1905
- });
1906
-
1907
1913
  // src/daemon-cdp.ts
1908
1914
  var import_ws3, http, fs, KNOWN_AGENTS, DaemonCdpManager;
1909
1915
  var init_daemon_cdp = __esm({
@@ -1919,10 +1925,15 @@ var init_daemon_cdp = __esm({
1919
1925
  ];
1920
1926
  DaemonCdpManager = class {
1921
1927
  ws = null;
1928
+ browserWs = null;
1929
+ // browser-level WS for Target discovery
1930
+ browserMsgId = 1e4;
1931
+ browserPending = /* @__PURE__ */ new Map();
1922
1932
  msgId = 1;
1923
1933
  pending = /* @__PURE__ */ new Map();
1924
1934
  port;
1925
1935
  _connected = false;
1936
+ _browserConnected = false;
1926
1937
  targetUrl = "";
1927
1938
  reconnectTimer = null;
1928
1939
  contexts = /* @__PURE__ */ new Set();
@@ -1931,8 +1942,10 @@ var init_daemon_cdp = __esm({
1931
1942
  MAX_FAILURES = 5;
1932
1943
  agentSessions = /* @__PURE__ */ new Map();
1933
1944
  logFn;
1934
- constructor(port = 9333, logFn) {
1945
+ disableFallback;
1946
+ constructor(port = 9333, logFn, disableFallback = false) {
1935
1947
  this.port = port;
1948
+ this.disableFallback = disableFallback;
1936
1949
  this.logFn = logFn || ((msg) => {
1937
1950
  console.log(msg);
1938
1951
  try {
@@ -2011,6 +2024,7 @@ var init_daemon_cdp = __esm({
2011
2024
  async findTarget() {
2012
2025
  let target = await this.findTargetOnPort(this.port);
2013
2026
  if (target) return target;
2027
+ if (this.disableFallback) return null;
2014
2028
  const fallbackPorts = [9222, 9333, 9335, 9337, 9339].filter((p) => p !== this.port);
2015
2029
  for (const port of fallbackPorts) {
2016
2030
  target = await this.findTargetOnPort(port);
@@ -2031,6 +2045,8 @@ var init_daemon_cdp = __esm({
2031
2045
  await this.sendInternal("Runtime.enable");
2032
2046
  } catch {
2033
2047
  }
2048
+ this.connectBrowserWs().catch(() => {
2049
+ });
2034
2050
  resolve3(true);
2035
2051
  });
2036
2052
  this.ws.on("message", (data) => {
@@ -2055,6 +2071,9 @@ var init_daemon_cdp = __esm({
2055
2071
  this.ws.on("close", () => {
2056
2072
  this.log("[CDP] WebSocket closed \u2014 scheduling reconnect");
2057
2073
  this._connected = false;
2074
+ this._browserConnected = false;
2075
+ this.browserWs?.close();
2076
+ this.browserWs = null;
2058
2077
  this.connectPromise = null;
2059
2078
  this.scheduleReconnect();
2060
2079
  });
@@ -2065,12 +2084,98 @@ var init_daemon_cdp = __esm({
2065
2084
  });
2066
2085
  });
2067
2086
  }
2087
+ /** Browser-level CDP 연결 — Target discovery에 필요 */
2088
+ async connectBrowserWs() {
2089
+ if (this._browserConnected && this.browserWs?.readyState === import_ws3.default.OPEN) return;
2090
+ try {
2091
+ const browserWsUrl = await this.getBrowserWsUrl();
2092
+ if (!browserWsUrl) {
2093
+ this.log("[CDP] No browser WS URL found");
2094
+ return;
2095
+ }
2096
+ this.log(`[CDP] Connecting browser WS for target discovery...`);
2097
+ await new Promise((resolve3, reject) => {
2098
+ this.browserWs = new import_ws3.default(browserWsUrl);
2099
+ this.browserWs.on("open", async () => {
2100
+ this._browserConnected = true;
2101
+ this.log("[CDP] \u2705 Browser WS connected \u2014 enabling target discovery");
2102
+ try {
2103
+ await this.sendBrowser("Target.setDiscoverTargets", { discover: true });
2104
+ } catch (e) {
2105
+ this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
2106
+ }
2107
+ resolve3();
2108
+ });
2109
+ this.browserWs.on("message", (data) => {
2110
+ try {
2111
+ const msg = JSON.parse(data.toString());
2112
+ if (msg.id && this.browserPending.has(msg.id)) {
2113
+ const { resolve: resolve4, reject: reject2 } = this.browserPending.get(msg.id);
2114
+ this.browserPending.delete(msg.id);
2115
+ if (msg.error) reject2(new Error(msg.error.message));
2116
+ else resolve4(msg.result);
2117
+ }
2118
+ } catch {
2119
+ }
2120
+ });
2121
+ this.browserWs.on("close", () => {
2122
+ this._browserConnected = false;
2123
+ this.browserWs = null;
2124
+ });
2125
+ this.browserWs.on("error", (err) => {
2126
+ this.log(`[CDP] Browser WS error: ${err.message}`);
2127
+ this._browserConnected = false;
2128
+ reject(err);
2129
+ });
2130
+ });
2131
+ } catch (e) {
2132
+ this.log(`[CDP] Browser WS connect failed: ${e.message}`);
2133
+ }
2134
+ }
2135
+ getBrowserWsUrl() {
2136
+ return new Promise((resolve3) => {
2137
+ const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
2138
+ let data = "";
2139
+ res.on("data", (chunk) => data += chunk.toString());
2140
+ res.on("end", () => {
2141
+ try {
2142
+ const info = JSON.parse(data);
2143
+ resolve3(info.webSocketDebuggerUrl || null);
2144
+ } catch {
2145
+ resolve3(null);
2146
+ }
2147
+ });
2148
+ });
2149
+ req.on("error", () => resolve3(null));
2150
+ req.setTimeout(3e3, () => {
2151
+ req.destroy();
2152
+ resolve3(null);
2153
+ });
2154
+ });
2155
+ }
2156
+ sendBrowser(method, params = {}, timeoutMs = 15e3) {
2157
+ return new Promise((resolve3, reject) => {
2158
+ if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
2159
+ const id = this.browserMsgId++;
2160
+ this.browserPending.set(id, { resolve: resolve3, reject });
2161
+ this.browserWs.send(JSON.stringify({ id, method, params }));
2162
+ setTimeout(() => {
2163
+ if (this.browserPending.has(id)) {
2164
+ this.browserPending.delete(id);
2165
+ reject(new Error(`Browser CDP timeout: ${method}`));
2166
+ }
2167
+ }, timeoutMs);
2168
+ });
2169
+ }
2068
2170
  scheduleReconnect() {
2069
2171
  if (this.reconnectTimer) return;
2070
2172
  this.reconnectTimer = setTimeout(async () => {
2071
2173
  this.reconnectTimer = null;
2072
2174
  if (!this._connected) {
2073
- await this.connect();
2175
+ const ok = await this.connect();
2176
+ if (!ok && !this._connected) {
2177
+ this.scheduleReconnect();
2178
+ }
2074
2179
  }
2075
2180
  }, 5e3);
2076
2181
  }
@@ -2082,6 +2187,9 @@ var init_daemon_cdp = __esm({
2082
2187
  this.ws?.close();
2083
2188
  this.ws = null;
2084
2189
  this._connected = false;
2190
+ this.browserWs?.close();
2191
+ this.browserWs = null;
2192
+ this._browserConnected = false;
2085
2193
  this.failureCount = 0;
2086
2194
  }
2087
2195
  get isConnected() {
@@ -2155,23 +2263,47 @@ var init_daemon_cdp = __esm({
2155
2263
  // ─── Agent Webview Multi-Session ─────────────────────────
2156
2264
  async discoverAgentWebviews() {
2157
2265
  if (!this.isConnected) return [];
2266
+ if (!this._browserConnected) {
2267
+ await this.connectBrowserWs().catch(() => {
2268
+ });
2269
+ }
2158
2270
  try {
2159
- const result = await this.sendInternal("Target.getTargets");
2160
- const allTargets = result?.targetInfos || [];
2271
+ let allTargets = [];
2272
+ if (this._browserConnected) {
2273
+ const result = await this.sendBrowser("Target.getTargets");
2274
+ allTargets = result?.targetInfos || [];
2275
+ } else {
2276
+ const result = await this.sendInternal("Target.getTargets");
2277
+ allTargets = result?.targetInfos || [];
2278
+ }
2161
2279
  const iframes = allTargets.filter((t) => t.type === "iframe");
2162
- this.log(`[CDP] discoverAgentWebviews: ${allTargets.length} total, ${iframes.length} iframes`);
2280
+ const typeMap = /* @__PURE__ */ new Map();
2281
+ for (const t of allTargets) {
2282
+ typeMap.set(t.type, (typeMap.get(t.type) || 0) + 1);
2283
+ }
2284
+ const typeSummary = [...typeMap.entries()].map(([k, v]) => `${k}:${v}`).join(",");
2285
+ this.log(`[CDP] discoverAgentWebviews: ${allTargets.length} total [${typeSummary}], ${iframes.length} iframes (browser=${this._browserConnected})`);
2286
+ for (const t of allTargets) {
2287
+ if (t.type !== "page" && t.type !== "worker" && t.type !== "service_worker") {
2288
+ this.log(`[CDP] target: type=${t.type} url=${(t.url || "").substring(0, 120)}`);
2289
+ }
2290
+ if ((t.url || "").includes("vscode-webview")) {
2291
+ this.log(`[CDP] webview: type=${t.type} url=${(t.url || "").substring(0, 150)}`);
2292
+ }
2293
+ }
2163
2294
  const agents = [];
2164
2295
  for (const target of allTargets) {
2165
2296
  if (target.type !== "iframe") continue;
2166
- const hasWebview = target.url?.includes("vscode-webview");
2297
+ const url = target.url || "";
2298
+ const hasWebview = url.includes("vscode-webview");
2167
2299
  if (!hasWebview) continue;
2168
2300
  for (const known of KNOWN_AGENTS) {
2169
- if (known.extensionIdPattern.test(target.url)) {
2301
+ if (known.extensionIdPattern.test(url)) {
2170
2302
  agents.push({
2171
2303
  targetId: target.targetId,
2172
2304
  extensionId: known.extensionId,
2173
2305
  agentType: known.agentType,
2174
- url: target.url
2306
+ url
2175
2307
  });
2176
2308
  this.log(`[CDP] Found agent: ${known.agentType} (${target.targetId})`);
2177
2309
  break;
@@ -2190,7 +2322,8 @@ var init_daemon_cdp = __esm({
2190
2322
  if (t.agentType === target.agentType) return sid;
2191
2323
  }
2192
2324
  try {
2193
- const result = await this.sendInternal("Target.attachToTarget", {
2325
+ const sendFn = this._browserConnected ? this.sendBrowser.bind(this) : this.sendInternal.bind(this);
2326
+ const result = await sendFn("Target.attachToTarget", {
2194
2327
  targetId: target.targetId,
2195
2328
  flatten: true
2196
2329
  });
@@ -2206,12 +2339,15 @@ var init_daemon_cdp = __esm({
2206
2339
  }
2207
2340
  }
2208
2341
  async evaluateInSession(sessionId, expression, timeoutMs = 15e3) {
2209
- if (!this.isConnected || !this.ws || this.ws.readyState !== import_ws3.default.OPEN) {
2342
+ const ws = this._browserConnected ? this.browserWs : this.ws;
2343
+ const pendingMap = this._browserConnected ? this.browserPending : this.pending;
2344
+ const getNextId = () => this._browserConnected ? this.browserMsgId++ : this.msgId++;
2345
+ if (!ws || ws.readyState !== import_ws3.default.OPEN) {
2210
2346
  throw new Error("CDP not connected");
2211
2347
  }
2212
2348
  return new Promise((resolve3, reject) => {
2213
- const id = this.msgId++;
2214
- this.pending.set(id, {
2349
+ const id = getNextId();
2350
+ pendingMap.set(id, {
2215
2351
  resolve: (result) => {
2216
2352
  if (result?.result?.subtype === "error") {
2217
2353
  reject(new Error(result.result.description));
@@ -2221,15 +2357,15 @@ var init_daemon_cdp = __esm({
2221
2357
  },
2222
2358
  reject
2223
2359
  });
2224
- this.ws.send(JSON.stringify({
2360
+ ws.send(JSON.stringify({
2225
2361
  id,
2226
2362
  sessionId,
2227
2363
  method: "Runtime.evaluate",
2228
2364
  params: { expression, returnByValue: true, awaitPromise: true }
2229
2365
  }));
2230
2366
  setTimeout(() => {
2231
- if (this.pending.has(id)) {
2232
- this.pending.delete(id);
2367
+ if (pendingMap.has(id)) {
2368
+ pendingMap.delete(id);
2233
2369
  reject(new Error(`CDP agent timeout: ${sessionId.substring(0, 12)}...`));
2234
2370
  }
2235
2371
  }, timeoutMs);
@@ -2237,7 +2373,8 @@ var init_daemon_cdp = __esm({
2237
2373
  }
2238
2374
  async detachAgent(sessionId) {
2239
2375
  try {
2240
- await this.sendInternal("Target.detachFromTarget", { sessionId });
2376
+ const sendFn = this._browserConnected ? this.sendBrowser.bind(this) : this.sendInternal.bind(this);
2377
+ await sendFn("Target.detachFromTarget", { sessionId });
2241
2378
  } catch {
2242
2379
  }
2243
2380
  this.agentSessions.delete(sessionId);
@@ -2256,7 +2393,9 @@ var init_daemon_cdp = __esm({
2256
2393
  try {
2257
2394
  const result = await this.sendInternal("Page.captureScreenshot", {
2258
2395
  format: "jpeg",
2259
- quality: 60
2396
+ quality: 25,
2397
+ clip: void 0
2398
+ // full viewport
2260
2399
  }, 1e4);
2261
2400
  if (result?.data) {
2262
2401
  return Buffer.from(result.data, "base64");
@@ -2577,12 +2716,22 @@ var init_daemon_p2p = __esm({
2577
2716
  fileRequestHandler = null;
2578
2717
  inputHandler = null;
2579
2718
  commandHandler = null;
2719
+ _ssDebugDone = false;
2580
2720
  get screenshotActive() {
2581
2721
  for (const peer of this.peers.values()) {
2582
2722
  if (peer.screenshotActive && peer.state === "connected") return true;
2583
2723
  }
2584
2724
  return false;
2585
2725
  }
2726
+ /** Get the ideType for the currently active screenshot request */
2727
+ get screenshotIdeType() {
2728
+ for (const peer of this.peers.values()) {
2729
+ if (peer.screenshotActive && peer.state === "connected" && peer.screenshotIdeType) {
2730
+ return peer.screenshotIdeType;
2731
+ }
2732
+ }
2733
+ return void 0;
2734
+ }
2586
2735
  constructor(bridge) {
2587
2736
  this.bridge = bridge;
2588
2737
  this.tryLoadNodeDatachannel();
@@ -2591,7 +2740,8 @@ var init_daemon_p2p = __esm({
2591
2740
  tryLoadNodeDatachannel() {
2592
2741
  try {
2593
2742
  this.nodeDatachannel = require_lib();
2594
- log(`node-datachannel loaded \u2705`);
2743
+ const keys = Object.keys(this.nodeDatachannel).join(",");
2744
+ log(`node-datachannel loaded \u2705 (keys: ${keys.substring(0, 100)})`);
2595
2745
  return;
2596
2746
  } catch (e) {
2597
2747
  log(`node-datachannel not found: ${e?.message}`);
@@ -2725,7 +2875,12 @@ var init_daemon_p2p = __esm({
2725
2875
  if (existing?.state === "connected") return;
2726
2876
  if (existing?.state === "connecting") this.disconnectPeer(pid);
2727
2877
  log(`initiateConnection() for peer ${pid}...`);
2728
- const { PeerConnection } = this.nodeDatachannel;
2878
+ const mod = this.nodeDatachannel;
2879
+ const PeerConnectionCtor = mod.PeerConnection || mod.default?.PeerConnection || mod.default || mod;
2880
+ if (!PeerConnectionCtor || typeof PeerConnectionCtor !== "function") {
2881
+ log(`PeerConnection constructor not found in node-datachannel module. Keys: ${Object.keys(mod).join(",")}`);
2882
+ return;
2883
+ }
2729
2884
  let iceServers = [
2730
2885
  "stun:stun.cloudflare.com:3478",
2731
2886
  "stun:stun.l.google.com:19302"
@@ -2738,7 +2893,7 @@ var init_daemon_p2p = __esm({
2738
2893
  }
2739
2894
  } catch {
2740
2895
  }
2741
- const pc = new PeerConnection(`ADHDev-Daemon-${pid.substring(0, 8)}`, {
2896
+ const pc = new PeerConnectionCtor(`ADHDev-Daemon-${pid.substring(0, 8)}`, {
2742
2897
  iceServers
2743
2898
  });
2744
2899
  const entry = {
@@ -2781,11 +2936,17 @@ var init_daemon_p2p = __esm({
2781
2936
  }
2782
2937
  });
2783
2938
  const screenshotCh = pc.createDataChannel("screenshots");
2784
- entry.screenshotChannel = screenshotCh;
2785
2939
  screenshotCh.onOpen(() => {
2786
2940
  log(`Screenshots channel OPEN for peer ${pid}`);
2787
- entry.state = "connected";
2788
- this.notifyStateChange();
2941
+ const peer = this.peers.get(pid);
2942
+ if (peer) {
2943
+ peer.screenshotChannel = screenshotCh;
2944
+ peer.state = "connected";
2945
+ this.notifyStateChange();
2946
+ if (peer.screenshotActive) {
2947
+ log(`Screenshots auto-starting for peer ${pid} (was waiting for channel open)`);
2948
+ }
2949
+ }
2789
2950
  });
2790
2951
  screenshotCh.onClosed(() => {
2791
2952
  log(`Screenshots channel CLOSED for peer ${pid}`);
@@ -2819,14 +2980,24 @@ var init_daemon_p2p = __esm({
2819
2980
  const text = typeof msg === "string" ? msg : msg.toString("utf-8");
2820
2981
  try {
2821
2982
  const parsed = JSON.parse(text);
2983
+ log(`Files message from peer ${peerId}: type=${parsed.type}`);
2822
2984
  if (parsed.type === "screenshot_start") {
2823
2985
  const peer = this.peers.get(peerId);
2824
- if (peer) peer.screenshotActive = true;
2986
+ if (peer) {
2987
+ peer.screenshotActive = true;
2988
+ peer.screenshotIdeType = parsed.ideType || void 0;
2989
+ log(`screenshot_start: peer=${peerId}, ideType=${parsed.ideType || "any"}, channelOpen=${!!peer.screenshotChannel}, state=${peer.state}`);
2990
+ } else {
2991
+ log(`screenshot_start: peer ${peerId} NOT FOUND in peers map!`);
2992
+ }
2825
2993
  return;
2826
2994
  }
2827
2995
  if (parsed.type === "screenshot_stop") {
2828
2996
  const peer = this.peers.get(peerId);
2829
- if (peer) peer.screenshotActive = false;
2997
+ if (peer) {
2998
+ peer.screenshotActive = false;
2999
+ log(`screenshot_stop: peer=${peerId}`);
3000
+ }
2830
3001
  return;
2831
3002
  }
2832
3003
  if (parsed.type === "input") {
@@ -2863,14 +3034,28 @@ var init_daemon_p2p = __esm({
2863
3034
  sendScreenshot(base64Data) {
2864
3035
  let sentAny = false;
2865
3036
  const buffer = Buffer.from(base64Data, "base64");
2866
- for (const peer of this.peers.values()) {
3037
+ let debugOnce = !this._ssDebugDone;
3038
+ const CHUNK_SIZE = 6e4;
3039
+ for (const [pid, peer] of this.peers.entries()) {
3040
+ if (debugOnce) {
3041
+ log(`sendScreenshot peer=${pid}: state=${peer.state}, hasSsCh=${!!peer.screenshotChannel}, ssActive=${peer.screenshotActive}, chOpen=${peer.screenshotChannel?.isOpen?.() ?? "N/A"}, bufSize=${buffer.length}`);
3042
+ }
2867
3043
  if (peer.state !== "connected" || !peer.screenshotChannel || !peer.screenshotActive) continue;
2868
3044
  try {
2869
- peer.screenshotChannel.sendMessageBinary(buffer);
3045
+ if (!peer.screenshotChannel.isOpen()) continue;
3046
+ const header = Buffer.alloc(4);
3047
+ header.writeUInt32BE(buffer.length, 0);
3048
+ peer.screenshotChannel.sendMessageBinary(header);
3049
+ for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
3050
+ const chunk = buffer.subarray(offset, Math.min(offset + CHUNK_SIZE, buffer.length));
3051
+ peer.screenshotChannel.sendMessageBinary(chunk);
3052
+ }
2870
3053
  sentAny = true;
2871
- } catch {
3054
+ } catch (e) {
3055
+ if (debugOnce) log(`sendScreenshot ERROR for peer ${pid}: ${e?.message}`);
2872
3056
  }
2873
3057
  }
3058
+ if (debugOnce) this._ssDebugDone = true;
2874
3059
  return sentAny;
2875
3060
  }
2876
3061
  // ─── 핸들러 등록 ────────────────────────────
@@ -2904,7 +3089,9 @@ var init_daemon_p2p = __esm({
2904
3089
  return;
2905
3090
  }
2906
3091
  try {
2907
- const result = await this.inputHandler({ action, params });
3092
+ const peer = this.peers.get(peerId);
3093
+ const ideType = peer?.screenshotIdeType;
3094
+ const result = await this.inputHandler({ action, params, _targetInstance: ideType ? `${ideType}_` : void 0 });
2908
3095
  this.sendToPeer(peerId, { id, type: "response", success: true, result });
2909
3096
  } catch (e) {
2910
3097
  this.sendToPeer(peerId, { id, type: "response", success: false, error: e?.message });
@@ -3156,13 +3343,46 @@ var init_daemon_commands = __esm({
3156
3343
  DaemonCommandHandler = class {
3157
3344
  ctx;
3158
3345
  agentStream = null;
3346
+ /** Get CDP manager for a specific ideType, or first connected one */
3347
+ getCdp(ideType) {
3348
+ const key = ideType || this._currentIdeType;
3349
+ if (key) {
3350
+ const m = this.ctx.cdpManagers.get(key.toLowerCase());
3351
+ if (m?.isConnected) return m;
3352
+ }
3353
+ for (const m of this.ctx.cdpManagers.values()) {
3354
+ if (m.isConnected) return m;
3355
+ }
3356
+ return null;
3357
+ }
3358
+ /** Current IDE type extracted from command args (per-request) */
3359
+ _currentIdeType;
3360
+ /** Extract ideType from _targetInstance (e.g. 'vscode_abc123' → 'vscode') */
3361
+ extractIdeType(args) {
3362
+ if (args?._targetInstance) {
3363
+ const parts = args._targetInstance.split("_");
3364
+ if (parts.length >= 2) return parts[0];
3365
+ }
3366
+ return void 0;
3367
+ }
3159
3368
  constructor(ctx) {
3160
3369
  this.ctx = ctx;
3161
3370
  }
3371
+ /** Get ScriptLoader for specific ideType, fallback to first available */
3372
+ getScriptLoader(ideType) {
3373
+ const key = ideType || this._currentIdeType;
3374
+ if (key) {
3375
+ const l = this.ctx.scriptLoaders.get(key.toLowerCase());
3376
+ if (l) return l;
3377
+ }
3378
+ for (const l of this.ctx.scriptLoaders.values()) return l;
3379
+ return void 0;
3380
+ }
3162
3381
  setAgentStreamManager(manager) {
3163
3382
  this.agentStream = manager;
3164
3383
  }
3165
3384
  async handle(cmd, args) {
3385
+ this._currentIdeType = this.extractIdeType(args);
3166
3386
  switch (cmd) {
3167
3387
  // ─── CDP 직접 처리 ───────────────────
3168
3388
  case "read_chat":
@@ -3205,8 +3425,13 @@ var init_daemon_commands = __esm({
3205
3425
  return this.handleFileListBrowse(args);
3206
3426
  // ─── vscode API → Extension 위임 ────
3207
3427
  case "vscode_command_exec":
3208
- case "execute_vscode_command":
3209
- return this.delegateToExtension(args?.command || cmd, args?.args);
3428
+ case "execute_vscode_command": {
3429
+ const resolvedCmd = args?.commandId || args?.command;
3430
+ if (resolvedCmd === "adhdev.captureCdpScreenshot") {
3431
+ return this.handleScreenshot(args);
3432
+ }
3433
+ return this.delegateToExtension(resolvedCmd || cmd, args?.args);
3434
+ }
3210
3435
  case "get_open_editors":
3211
3436
  return this.delegateToExtension("adhdev.getOpenEditors", [args]);
3212
3437
  case "open_tab":
@@ -3249,59 +3474,97 @@ var init_daemon_commands = __esm({
3249
3474
  }
3250
3475
  // ─── CDP 기반 채팅 명령 ──────────────────────
3251
3476
  async handleReadChat(args) {
3252
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3253
- const script = this.ctx.scriptLoader?.get("read_chat");
3477
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3478
+ const script = this.getScriptLoader()?.get("read_chat");
3254
3479
  if (!script) return { success: false, error: "read_chat script not loaded" };
3255
3480
  try {
3256
- const result = await this.ctx.cdp.evaluate(script, 5e4);
3481
+ const result = await this.getCdp().evaluate(script, 5e4);
3257
3482
  return { success: true, ...result };
3258
3483
  } catch (e) {
3259
3484
  return { success: false, error: e.message };
3260
3485
  }
3261
3486
  }
3262
3487
  async handleSendChat(args) {
3263
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3264
3488
  const text = args?.text || args?.message;
3265
3489
  if (!text) return { success: false, error: "text required" };
3266
- const script = this.ctx.scriptLoader?.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
3267
- if (!script) return { success: false, error: "send_message script not loaded" };
3268
- try {
3269
- await this.ctx.cdp.evaluate(script, 3e4);
3270
- return { success: true, sent: true };
3271
- } catch (e) {
3272
- return { success: false, error: e.message };
3490
+ const targetCdp = this.getCdp();
3491
+ const targetLoader = this.getScriptLoader();
3492
+ if (targetCdp?.isConnected && targetLoader) {
3493
+ const script = targetLoader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
3494
+ if (script) {
3495
+ console.log(`[send_chat] Targeting IDE: ${this._currentIdeType || "fallback-first"}`);
3496
+ try {
3497
+ await targetCdp.evaluate(script, 3e4);
3498
+ return { success: true, sent: true };
3499
+ } catch (e) {
3500
+ console.log(`[send_chat] Failed on ${this._currentIdeType}: ${e.message}`);
3501
+ }
3502
+ }
3503
+ }
3504
+ console.log(`[send_chat] No specific target, trying all CDPs...`);
3505
+ for (const [ideType, cdp] of this.ctx.cdpManagers) {
3506
+ if (!cdp.isConnected) continue;
3507
+ const loader = this.ctx.scriptLoaders.get(ideType);
3508
+ if (!loader) continue;
3509
+ const script = loader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
3510
+ if (!script) continue;
3511
+ console.log(`[send_chat] Trying IDE: ${ideType}`);
3512
+ try {
3513
+ await cdp.evaluate(script, 3e4);
3514
+ return { success: true, sent: true, targetIde: ideType };
3515
+ } catch (e) {
3516
+ console.log(`[send_chat] Failed on ${ideType}: ${e.message}`);
3517
+ }
3273
3518
  }
3519
+ return { success: false, error: "No CDP could send the message" };
3274
3520
  }
3275
3521
  async handleListChats(args) {
3276
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3277
- const script = this.ctx.scriptLoader?.get("list_chats");
3278
- if (!script) return { success: false, error: "list_chats script not loaded" };
3522
+ const cdp = this.getCdp();
3523
+ if (!cdp?.isConnected) {
3524
+ console.log(`[list_chats] CDP not connected, ideType=${this._currentIdeType}`);
3525
+ return { success: false, error: "CDP not connected" };
3526
+ }
3527
+ const script = this.getScriptLoader()?.get("list_chats");
3528
+ if (!script) {
3529
+ console.log(`[list_chats] script not loaded`);
3530
+ return { success: false, error: "list_chats script not loaded" };
3531
+ }
3279
3532
  try {
3280
- const result = await this.ctx.cdp.evaluate(script, 3e4);
3281
- return { success: true, chats: result };
3533
+ console.log(`[list_chats] Evaluating on ideType=${this._currentIdeType}`);
3534
+ const result = await cdp.evaluate(script, 3e4);
3535
+ let parsed = result;
3536
+ if (typeof result === "string") {
3537
+ try {
3538
+ parsed = JSON.parse(result);
3539
+ } catch {
3540
+ }
3541
+ }
3542
+ console.log(`[list_chats] Result type=${typeof parsed}, isArray=${Array.isArray(parsed)}, len=${Array.isArray(parsed) ? parsed.length : "N/A"}`);
3543
+ return { success: true, chats: parsed };
3282
3544
  } catch (e) {
3545
+ console.log(`[list_chats] Error: ${e.message}`);
3283
3546
  return { success: false, error: e.message };
3284
3547
  }
3285
3548
  }
3286
3549
  async handleNewChat(args) {
3287
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3288
- const script = this.ctx.scriptLoader?.get("new_session");
3550
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3551
+ const script = this.getScriptLoader()?.get("new_session");
3289
3552
  if (!script) return { success: false, error: "new_session script not loaded" };
3290
3553
  try {
3291
- await this.ctx.cdp.evaluate(script, 15e3);
3554
+ await this.getCdp().evaluate(script, 15e3);
3292
3555
  return { success: true };
3293
3556
  } catch (e) {
3294
3557
  return { success: false, error: e.message };
3295
3558
  }
3296
3559
  }
3297
3560
  async handleSwitchChat(args) {
3298
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3561
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3299
3562
  const sessionId = args?.sessionId || args?.id || args?.chatId;
3300
3563
  if (!sessionId) return { success: false, error: "sessionId required" };
3301
- const script = this.ctx.scriptLoader?.getWithParams("switch_session", { SESSION_ID: sessionId });
3564
+ const script = this.getScriptLoader()?.getWithParams("switch_session", { SESSION_ID: sessionId });
3302
3565
  if (!script) return { success: false, error: "switch_session script not loaded" };
3303
3566
  try {
3304
- await this.ctx.cdp.evaluate(script, 15e3);
3567
+ await this.getCdp().evaluate(script, 15e3);
3305
3568
  return { success: true };
3306
3569
  } catch (e) {
3307
3570
  return { success: false, error: e.message };
@@ -3315,12 +3578,12 @@ var init_daemon_commands = __esm({
3315
3578
  return this.delegateToExtension("workbench.action.openSettings", [`cursor.model`]);
3316
3579
  }
3317
3580
  async handleResolveModal(args) {
3318
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3581
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3319
3582
  const button = args?.button || args?.buttonText || "Accept";
3320
- const script = this.ctx.scriptLoader?.getWithParams("resolve_modal", { BUTTON_TEXT: button });
3583
+ const script = this.getScriptLoader()?.getWithParams("resolve_modal", { BUTTON_TEXT: button });
3321
3584
  if (!script) {
3322
3585
  try {
3323
- await this.ctx.cdp.evaluate(`
3586
+ await this.getCdp().evaluate(`
3324
3587
  (() => {
3325
3588
  const btns = [...document.querySelectorAll('button')];
3326
3589
  const btn = btns.find(b => b.textContent?.trim() === ${JSON.stringify(button)});
@@ -3334,7 +3597,7 @@ var init_daemon_commands = __esm({
3334
3597
  }
3335
3598
  }
3336
3599
  try {
3337
- await this.ctx.cdp.evaluate(script, 1e4);
3600
+ await this.getCdp().evaluate(script, 1e4);
3338
3601
  return { success: true };
3339
3602
  } catch (e) {
3340
3603
  return { success: false, error: e.message };
@@ -3342,22 +3605,23 @@ var init_daemon_commands = __esm({
3342
3605
  }
3343
3606
  // ─── CDP 직접 명령 ──────────────────────────
3344
3607
  async handleCdpEval(args) {
3345
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3608
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3346
3609
  const expression = args?.expression || args?.script;
3347
3610
  if (!expression) return { success: false, error: "expression required" };
3348
3611
  try {
3349
- const result = await this.ctx.cdp.evaluate(expression, 5e4);
3612
+ const result = await this.getCdp().evaluate(expression, 5e4);
3350
3613
  return { success: true, result };
3351
3614
  } catch (e) {
3352
3615
  return { success: false, error: e.message };
3353
3616
  }
3354
3617
  }
3355
3618
  async handleScreenshot(args) {
3356
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3619
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3357
3620
  try {
3358
- const buf = await this.ctx.cdp.captureScreenshot();
3621
+ const buf = await this.getCdp().captureScreenshot();
3359
3622
  if (buf) {
3360
- return { success: true, screenshot: buf.toString("base64"), format: "jpeg" };
3623
+ const b64 = buf.toString("base64");
3624
+ return { success: true, result: b64, base64: b64, screenshot: b64, format: "jpeg" };
3361
3625
  }
3362
3626
  return { success: false, error: "Screenshot failed" };
3363
3627
  } catch (e) {
@@ -3365,26 +3629,26 @@ var init_daemon_commands = __esm({
3365
3629
  }
3366
3630
  }
3367
3631
  async handleCdpCommand(args) {
3368
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3632
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3369
3633
  const method = args?.method;
3370
3634
  const params = args?.params || {};
3371
3635
  if (!method) return { success: false, error: "method required" };
3372
3636
  try {
3373
- const result = await this.ctx.cdp.sendCdpCommand(method, params);
3637
+ const result = await this.getCdp().sendCdpCommand(method, params);
3374
3638
  return { success: true, result };
3375
3639
  } catch (e) {
3376
3640
  return { success: false, error: e.message };
3377
3641
  }
3378
3642
  }
3379
3643
  async handleCdpBatch(args) {
3380
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3644
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3381
3645
  const commands = args?.commands;
3382
3646
  const stopOnError = args?.stopOnError !== false;
3383
3647
  if (!commands?.length) return { success: false, error: "commands array required" };
3384
3648
  const results = [];
3385
3649
  for (const cmd of commands) {
3386
3650
  try {
3387
- const result = await this.ctx.cdp.sendCdpCommand(cmd.method, cmd.params || {});
3651
+ const result = await this.getCdp().sendCdpCommand(cmd.method, cmd.params || {});
3388
3652
  results.push({ method: cmd.method, success: true, result });
3389
3653
  } catch (e) {
3390
3654
  results.push({ method: cmd.method, success: false, error: e.message });
@@ -3394,49 +3658,60 @@ var init_daemon_commands = __esm({
3394
3658
  return { success: true, results };
3395
3659
  }
3396
3660
  async handleCdpRemoteAction(args) {
3397
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3661
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3398
3662
  const action = args?.action;
3399
3663
  const params = args?.params || args;
3400
3664
  try {
3401
3665
  switch (action) {
3402
3666
  case "input_key": {
3403
3667
  const { key, modifiers } = params;
3404
- await this.ctx.cdp.send("Input.dispatchKeyEvent", {
3668
+ await this.getCdp().send("Input.dispatchKeyEvent", {
3405
3669
  type: "keyDown",
3406
3670
  key,
3407
3671
  ...modifiers?.ctrl ? { modifiers: 2 } : {},
3408
3672
  ...modifiers?.shift ? { modifiers: 8 } : {}
3409
3673
  });
3410
- await this.ctx.cdp.send("Input.dispatchKeyEvent", { type: "keyUp", key });
3674
+ await this.getCdp().send("Input.dispatchKeyEvent", { type: "keyUp", key });
3411
3675
  return { success: true };
3412
3676
  }
3413
3677
  case "input_click": {
3414
- const { x, y, button: btn } = params;
3415
- await this.ctx.cdp.send("Input.dispatchMouseEvent", {
3678
+ let { x, y, nx, ny, button: btn } = params;
3679
+ if ((x === void 0 || y === void 0) && nx !== void 0 && ny !== void 0) {
3680
+ const viewport = await this.getCdp().evaluate(
3681
+ "JSON.stringify({ w: window.innerWidth, h: window.innerHeight })"
3682
+ );
3683
+ const { w, h } = JSON.parse(viewport);
3684
+ x = Math.round(nx * w);
3685
+ y = Math.round(ny * h);
3686
+ }
3687
+ if (x === void 0 || y === void 0) {
3688
+ return { success: false, error: "No coordinates provided (x,y or nx,ny required)" };
3689
+ }
3690
+ await this.getCdp().send("Input.dispatchMouseEvent", {
3416
3691
  type: "mousePressed",
3417
3692
  x,
3418
3693
  y,
3419
3694
  button: btn || "left",
3420
3695
  clickCount: 1
3421
3696
  });
3422
- await this.ctx.cdp.send("Input.dispatchMouseEvent", {
3697
+ await this.getCdp().send("Input.dispatchMouseEvent", {
3423
3698
  type: "mouseReleased",
3424
3699
  x,
3425
3700
  y,
3426
3701
  button: btn || "left",
3427
3702
  clickCount: 1
3428
3703
  });
3429
- return { success: true };
3704
+ return { success: true, x, y };
3430
3705
  }
3431
3706
  case "input_type": {
3432
3707
  const { text } = params;
3433
3708
  for (const char of text || "") {
3434
- await this.ctx.cdp.send("Input.dispatchKeyEvent", {
3709
+ await this.getCdp().send("Input.dispatchKeyEvent", {
3435
3710
  type: "keyDown",
3436
3711
  text: char,
3437
3712
  key: char
3438
3713
  });
3439
- await this.ctx.cdp.send("Input.dispatchKeyEvent", { type: "keyUp", key: char });
3714
+ await this.getCdp().send("Input.dispatchKeyEvent", { type: "keyUp", key: char });
3440
3715
  }
3441
3716
  return { success: true };
3442
3717
  }
@@ -3445,9 +3720,28 @@ var init_daemon_commands = __esm({
3445
3720
  case "page_eval":
3446
3721
  return this.handleCdpEval(params);
3447
3722
  case "dom_query": {
3448
- const html = await this.ctx.cdp.querySelector(params?.selector);
3723
+ const html = await this.getCdp().querySelector(params?.selector);
3449
3724
  return { success: true, html };
3450
3725
  }
3726
+ case "input_wheel": {
3727
+ let { x, y, nx, ny, deltaX, deltaY } = params;
3728
+ if ((x === void 0 || y === void 0) && nx !== void 0 && ny !== void 0) {
3729
+ const viewport = await this.getCdp().evaluate(
3730
+ "JSON.stringify({ w: window.innerWidth, h: window.innerHeight })"
3731
+ );
3732
+ const { w, h } = JSON.parse(viewport);
3733
+ x = Math.round(nx * w);
3734
+ y = Math.round(ny * h);
3735
+ }
3736
+ await this.getCdp().send("Input.dispatchMouseEvent", {
3737
+ type: "mouseWheel",
3738
+ x: x || 0,
3739
+ y: y || 0,
3740
+ deltaX: deltaX || 0,
3741
+ deltaY: deltaY || 0
3742
+ });
3743
+ return { success: true };
3744
+ }
3451
3745
  default:
3452
3746
  return { success: false, error: `Unknown remote action: ${action}` };
3453
3747
  }
@@ -3456,8 +3750,8 @@ var init_daemon_commands = __esm({
3456
3750
  }
3457
3751
  }
3458
3752
  async handleDiscoverAgents(args) {
3459
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3460
- const agents = await this.ctx.cdp.discoverAgentWebviews();
3753
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3754
+ const agents = await this.getCdp().discoverAgentWebviews();
3461
3755
  return { success: true, agents };
3462
3756
  }
3463
3757
  // ─── 파일 직접 처리 ─────────────────────────
@@ -3514,7 +3808,7 @@ var init_daemon_commands = __esm({
3514
3808
  if (!this.ctx.localServer || this.ctx.localServer.extensionCount === 0) {
3515
3809
  return { success: false, error: "No extension connected" };
3516
3810
  }
3517
- return this.ctx.localServer.executeVscodeCommand(command, args);
3811
+ return this.ctx.localServer.executeVscodeCommand(command, args, this._currentIdeType);
3518
3812
  }
3519
3813
  // ─── 기타 ───────────────────────────────────
3520
3814
  async handleGetRecentWorkspaces(args) {
@@ -3533,66 +3827,67 @@ var init_daemon_commands = __esm({
3533
3827
  }
3534
3828
  }
3535
3829
  async handleRefreshScripts(args) {
3536
- if (!this.ctx.scriptLoader) return { success: false, error: "ScriptLoader not initialized" };
3537
- await this.ctx.scriptLoader.refresh();
3830
+ const loader = this.getScriptLoader();
3831
+ if (!loader) return { success: false, error: "ScriptLoader not initialized" };
3832
+ await loader.refresh();
3538
3833
  return { success: true };
3539
3834
  }
3540
3835
  // ─── Agent Stream 명령 ───────────────────────
3541
3836
  async handleAgentStreamSwitch(args) {
3542
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3837
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3543
3838
  const agentType = args?.agentType || args?.agent || null;
3544
- await this.agentStream.switchActiveAgent(this.ctx.cdp, agentType);
3839
+ await this.agentStream.switchActiveAgent(this.getCdp(), agentType);
3545
3840
  return { success: true, activeAgent: agentType };
3546
3841
  }
3547
3842
  async handleAgentStreamRead(args) {
3548
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3549
- const streams = await this.agentStream.collectAgentStreams(this.ctx.cdp);
3843
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3844
+ const streams = await this.agentStream.collectAgentStreams(this.getCdp());
3550
3845
  return { success: true, streams };
3551
3846
  }
3552
3847
  async handleAgentStreamSend(args) {
3553
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3848
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3554
3849
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3555
3850
  const text = args?.text || args?.message;
3556
3851
  if (!agentType || !text) return { success: false, error: "agentType and text required" };
3557
- const ok = await this.agentStream.sendToAgent(this.ctx.cdp, agentType, text);
3852
+ const ok = await this.agentStream.sendToAgent(this.getCdp(), agentType, text);
3558
3853
  return { success: ok };
3559
3854
  }
3560
3855
  async handleAgentStreamResolve(args) {
3561
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3856
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3562
3857
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3563
3858
  const action = args?.action || "approve";
3564
3859
  if (!agentType) return { success: false, error: "agentType required" };
3565
- const ok = await this.agentStream.resolveAgentAction(this.ctx.cdp, agentType, action);
3860
+ const ok = await this.agentStream.resolveAgentAction(this.getCdp(), agentType, action);
3566
3861
  return { success: ok };
3567
3862
  }
3568
3863
  async handleAgentStreamNew(args) {
3569
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3864
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3570
3865
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3571
3866
  if (!agentType) return { success: false, error: "agentType required" };
3572
- const ok = await this.agentStream.newAgentSession(this.ctx.cdp, agentType);
3867
+ const ok = await this.agentStream.newAgentSession(this.getCdp(), agentType);
3573
3868
  return { success: ok };
3574
3869
  }
3575
3870
  async handleAgentStreamListChats(args) {
3576
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3871
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3577
3872
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3578
3873
  if (!agentType) return { success: false, error: "agentType required" };
3579
- const chats = await this.agentStream.listAgentChats(this.ctx.cdp, agentType);
3874
+ const chats = await this.agentStream.listAgentChats(this.getCdp(), agentType);
3580
3875
  return { success: true, chats };
3581
3876
  }
3582
3877
  async handleAgentStreamSwitchSession(args) {
3583
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3878
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3584
3879
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3585
3880
  const sessionId = args?.sessionId || args?.id;
3586
3881
  if (!agentType || !sessionId) return { success: false, error: "agentType and sessionId required" };
3587
- const ok = await this.agentStream.switchAgentSession(this.ctx.cdp, agentType, sessionId);
3882
+ const ok = await this.agentStream.switchAgentSession(this.getCdp(), agentType, sessionId);
3588
3883
  return { success: ok };
3589
3884
  }
3590
3885
  async handleAgentStreamFocus(args) {
3591
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3886
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3592
3887
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3593
3888
  if (!agentType) return { success: false, error: "agentType required" };
3594
3889
  await this.agentStream.ensureAgentPanelOpen(agentType);
3595
- const ok = await this.agentStream.focusAgentEditor(this.ctx.cdp, agentType);
3890
+ const ok = await this.agentStream.focusAgentEditor(this.getCdp(), agentType);
3596
3891
  return { success: ok };
3597
3892
  }
3598
3893
  };
@@ -4192,23 +4487,29 @@ var init_adhdev_daemon = __esm({
4192
4487
  AdhdevDaemon = class {
4193
4488
  localServer = null;
4194
4489
  bridge = null;
4195
- adapter = null;
4196
- cdpManager = null;
4490
+ adapters = /* @__PURE__ */ new Map();
4491
+ cdpManagers = /* @__PURE__ */ new Map();
4197
4492
  cdpDiscoveryTimer = null;
4198
4493
  p2p = null;
4199
- scriptLoader = null;
4494
+ scriptLoaders = /* @__PURE__ */ new Map();
4200
4495
  commandHandler = null;
4201
4496
  screenshotTimer = null;
4202
4497
  agentStreamManager = null;
4203
4498
  agentStreamTimer = null;
4204
4499
  running = false;
4205
4500
  statusTimer = null;
4206
- lastAgentStatus = "idle";
4207
- generatingStartedAt = 0;
4501
+ lastAgentStatus = /* @__PURE__ */ new Map();
4502
+ generatingStartedAt = /* @__PURE__ */ new Map();
4503
+ getCliKey(cliType, dir) {
4504
+ const hash = require("crypto").createHash("md5").update(require("path").resolve(dir)).digest("hex").slice(0, 8);
4505
+ return `${cliType}_${hash}`;
4506
+ }
4208
4507
  detectedIdes = [];
4209
4508
  localPort;
4210
4509
  ideType = "unknown";
4211
- _cachedAgentStreams = [];
4510
+ _cachedAgentStreamsMap = /* @__PURE__ */ new Map();
4511
+ _cachedActiveChatMap = /* @__PURE__ */ new Map();
4512
+ _cdpChatBusy = false;
4212
4513
  constructor() {
4213
4514
  this.localPort = DEFAULT_DAEMON_PORT;
4214
4515
  }
@@ -4271,18 +4572,27 @@ var init_adhdev_daemon = __esm({
4271
4572
  }
4272
4573
  this.detectedIdes = await detectIDEs();
4273
4574
  await this.initCdp();
4274
- this.ideType = this.detectedIdes.find((ide) => ide.running)?.type || "cursor";
4275
- this.scriptLoader = new DaemonScriptLoader(
4276
- options.serverUrl || config.serverUrl,
4277
- this.ideType
4278
- );
4279
- this.scriptLoader.setToken(config.connectionToken);
4280
- await this.scriptLoader.start().catch((e) => {
4281
- console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed: ${e.message}`));
4282
- });
4575
+ const serverUrl = options.serverUrl || config.serverUrl;
4576
+ for (const ideType of this.cdpManagers.keys()) {
4577
+ const loader = new DaemonScriptLoader(serverUrl, ideType);
4578
+ loader.setToken(config.connectionToken);
4579
+ await loader.start().catch((e) => {
4580
+ console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${ideType}: ${e.message}`));
4581
+ });
4582
+ this.scriptLoaders.set(ideType, loader);
4583
+ }
4584
+ if (this.scriptLoaders.size === 0) {
4585
+ const fallbackType = this.detectedIdes.find((ide) => ide.installed)?.id || "cursor";
4586
+ const loader = new DaemonScriptLoader(serverUrl, fallbackType);
4587
+ loader.setToken(config.connectionToken);
4588
+ await loader.start().catch((e) => {
4589
+ console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed: ${e.message}`));
4590
+ });
4591
+ this.scriptLoaders.set(fallbackType, loader);
4592
+ }
4283
4593
  this.commandHandler = new DaemonCommandHandler({
4284
- cdp: this.cdpManager,
4285
- scriptLoader: this.scriptLoader,
4594
+ cdpManagers: this.cdpManagers,
4595
+ scriptLoaders: this.scriptLoaders,
4286
4596
  localServer: this.localServer,
4287
4597
  ideType: this.ideType
4288
4598
  });
@@ -4293,26 +4603,11 @@ var init_adhdev_daemon = __esm({
4293
4603
  );
4294
4604
  this.agentStreamManager.setLocalServer(this.localServer);
4295
4605
  this.commandHandler.setAgentStreamManager(this.agentStreamManager);
4296
- if (this.cdpManager) {
4297
- this.agentStreamTimer = setInterval(async () => {
4298
- if (!this.cdpManager?.isConnected || !this.agentStreamManager) return;
4299
- try {
4300
- await this.agentStreamManager.syncAgentSessions(this.cdpManager);
4301
- this._cachedAgentStreams = await this.agentStreamManager.collectAgentStreams(this.cdpManager);
4302
- } catch {
4303
- }
4304
- }, 5e3);
4305
- }
4606
+ this.startAgentStreamPolling();
4306
4607
  if (options.cliType) {
4307
- const cliInfo = await detectCLI(options.cliType);
4308
- if (cliInfo) {
4309
- this.adapter = this.createAdapter(options.cliType, workingDir);
4310
- await this.adapter.spawn();
4311
- this.adapter.setOnStatusChange(() => this.sendUnifiedStatusReport());
4312
- console.log(import_chalk2.default.green(` \u{1F916} CLI Agent started: ${cliInfo.displayName} v${cliInfo.version || "unknown"}`));
4313
- } else {
4314
- console.log(import_chalk2.default.yellow(` \u26A0 CLI ${options.cliType} not found \u2014 running without CLI agent`));
4315
- }
4608
+ await this.startCliSession(options.cliType, workingDir).catch((e) => {
4609
+ console.log(import_chalk2.default.yellow(` \u26A0 Failed to start CLI ${options.cliType}: ${e.message}`));
4610
+ });
4316
4611
  }
4317
4612
  const machineId = os8.hostname().replace(/[^a-zA-Z0-9]/g, "_");
4318
4613
  const machineHash = crypto2.createHash("md5").update(os8.hostname() + os8.homedir()).digest("hex").slice(0, 8);
@@ -4327,9 +4622,6 @@ var init_adhdev_daemon = __esm({
4327
4622
  instanceId
4328
4623
  }
4329
4624
  });
4330
- if (this.adapter && typeof this.adapter.setBridge === "function") {
4331
- this.adapter.setBridge(this.bridge);
4332
- }
4333
4625
  this.p2p = new DaemonP2PSender(this.bridge);
4334
4626
  if (this.p2p.isAvailable) {
4335
4627
  console.log(import_chalk2.default.green(" \u{1F517} P2P available (node-datachannel)"));
@@ -4353,12 +4645,26 @@ var init_adhdev_daemon = __esm({
4353
4645
  return { id: req.id, success: result.success, entries: result.files, error: result.error };
4354
4646
  }
4355
4647
  });
4648
+ let ssDebugCount = 0;
4356
4649
  this.screenshotTimer = setInterval(async () => {
4357
- if (!this.p2p?.screenshotActive || !this.cdpManager?.isConnected) return;
4650
+ const active = this.p2p?.screenshotActive;
4651
+ const ssIdeType = this.p2p?.screenshotIdeType;
4652
+ const cdp = ssIdeType ? this.getCdpFor(ssIdeType) : this.getAnyCdp();
4653
+ if (!active || !cdp) return;
4654
+ ssDebugCount++;
4655
+ if (ssDebugCount <= 3 || ssDebugCount % 25 === 0) {
4656
+ console.log(`[SS] Capturing screenshot... (tick ${ssDebugCount}, active=${active}, cdp=true)`);
4657
+ }
4358
4658
  try {
4359
- const buf = await this.cdpManager.captureScreenshot();
4360
- if (buf) this.p2p.sendScreenshot(buf.toString("base64"));
4361
- } catch {
4659
+ const buf = await cdp.captureScreenshot();
4660
+ if (buf) {
4661
+ const sent = this.p2p.sendScreenshot(buf.toString("base64"));
4662
+ if (ssDebugCount <= 3) console.log(`[SS] Screenshot sent: ${buf.length} bytes, delivered=${sent}`);
4663
+ } else {
4664
+ if (ssDebugCount <= 5) console.log(`[SS] captureScreenshot returned null`);
4665
+ }
4666
+ } catch (e) {
4667
+ if (ssDebugCount <= 5) console.log(`[SS] Screenshot error: ${e?.message}`);
4362
4668
  }
4363
4669
  }, 200);
4364
4670
  } else {
@@ -4391,9 +4697,10 @@ var init_adhdev_daemon = __esm({
4391
4697
  console.log(import_chalk2.default.bold(" \u{1F309} ADHDev Daemon"));
4392
4698
  console.log(` ${import_chalk2.default.bold("Local:")} ws://127.0.0.1:${this.localPort}/ipc`);
4393
4699
  console.log(` ${import_chalk2.default.bold("Server:")} ${serverUrl}`);
4394
- console.log(` ${import_chalk2.default.bold("CDP:")} ${this.cdpManager?.isConnected ? `\u2705 port ${this.cdpManager.getPort()}` : "\u274C not connected"}`);
4700
+ const cdpStatus = this.cdpManagers.size > 0 ? `\u2705 ${[...this.cdpManagers.entries()].map(([k, m]) => `${k}:${m.getPort()}`).join(", ")}` : "\u274C not connected";
4701
+ console.log(` ${import_chalk2.default.bold("CDP:")} ${cdpStatus}`);
4395
4702
  console.log(` ${import_chalk2.default.bold("P2P:")} ${this.p2p?.isAvailable ? "\u2705 available" : "\u274C unavailable"}`);
4396
- console.log(` ${import_chalk2.default.bold("Scripts:")} ${this.scriptLoader ? `\u2705 ${this.ideType}` : "\u274C not loaded"}`);
4703
+ console.log(` ${import_chalk2.default.bold("Scripts:")} ${this.scriptLoaders.size > 0 ? `\u2705 ${[...this.scriptLoaders.keys()].join(", ")}` : "\u274C not loaded"}`);
4397
4704
  if (options.cliType) {
4398
4705
  console.log(` ${import_chalk2.default.bold("CLI:")} ${options.cliType}`);
4399
4706
  console.log(` ${import_chalk2.default.bold("Dir:")} ${options.workingDir || process.cwd()}`);
@@ -4405,19 +4712,6 @@ var init_adhdev_daemon = __esm({
4405
4712
  // ─── 서버 명령 핸들러 ───────────────────────────
4406
4713
  registerServerHandlers() {
4407
4714
  if (!this.bridge) return;
4408
- this.bridge.on("send_chat", (msg) => {
4409
- const text = msg.payload.message;
4410
- if (!text || !this.adapter) {
4411
- this.sendResult(msg, false, { error: "No message or CLI adapter" });
4412
- return;
4413
- }
4414
- console.log(import_chalk2.default.cyan(` \u2190 Chat: "${text.slice(0, 60)}${text.length > 60 ? "..." : ""}"`));
4415
- this.adapter.sendMessage(text).then(() => {
4416
- this.sendResult(msg, true, { sent: true });
4417
- }).catch((e) => {
4418
- this.sendResult(msg, false, { error: e?.message || "Failed" });
4419
- });
4420
- });
4421
4715
  this.bridge.on("command", async (msg) => {
4422
4716
  const cmd = msg.payload.command;
4423
4717
  const args = msg.payload.args;
@@ -4455,7 +4749,38 @@ var init_adhdev_daemon = __esm({
4455
4749
  "file_list",
4456
4750
  "file_list_browse",
4457
4751
  "terminal_exec",
4458
- "refresh_scripts"
4752
+ "refresh_scripts",
4753
+ // CLI/daemon-local commands (launch, restart, detect)
4754
+ "launch_ide",
4755
+ "detect_ides",
4756
+ "restart_session",
4757
+ "exec_command",
4758
+ "launch_cli",
4759
+ "stop_cli",
4760
+ // Extension-delegated commands (must be registered to receive WS messages)
4761
+ "vscode_command_exec",
4762
+ "execute_vscode_command",
4763
+ "get_open_editors",
4764
+ "open_tab",
4765
+ "close_tab",
4766
+ "open_folder",
4767
+ "open_folder_picker",
4768
+ "open_recent",
4769
+ "get_commands",
4770
+ "get_recent_workspaces",
4771
+ "open_panel",
4772
+ "open_file",
4773
+ "create_terminal",
4774
+ "close_terminal",
4775
+ // Agent stream commands
4776
+ "agent_stream_switch",
4777
+ "agent_stream_read",
4778
+ "agent_stream_send",
4779
+ "agent_stream_resolve",
4780
+ "agent_stream_new",
4781
+ "agent_stream_list_chats",
4782
+ "agent_stream_switch_session",
4783
+ "agent_stream_focus"
4459
4784
  ];
4460
4785
  for (const cmdType of directCdpCommands) {
4461
4786
  this.bridge.on(cmdType, async (msg) => {
@@ -4467,21 +4792,33 @@ var init_adhdev_daemon = __esm({
4467
4792
  console.log(import_chalk2.default.magenta(` \u2699 Command: ${cmd}`));
4468
4793
  try {
4469
4794
  switch (cmd) {
4470
- case "switch_cli": {
4471
- const newCli = args?.cli || "gemini-cli";
4472
- const newDir = args?.dir || this.adapter?.workingDir || process.cwd();
4473
- await this.switchCliSession(newCli, newDir);
4474
- this.sendResult(msg, true, { cli: newCli, dir: newDir });
4795
+ case "launch_cli": {
4796
+ const cliType = args?.cliType;
4797
+ const dir = args?.dir || process.cwd();
4798
+ if (!cliType) throw new Error("cliType required");
4799
+ const key = this.getCliKey(cliType, dir);
4800
+ if (!this.adapters.has(key)) {
4801
+ await this.startCliSession(cliType, dir);
4802
+ }
4803
+ this.sendResult(msg, true, { cliType, dir, id: key });
4475
4804
  return;
4476
4805
  }
4477
- case "change_dir": {
4478
- if (!args?.dir) throw new Error("Directory path required");
4479
- if (this.adapter) await this.switchCliSession(this.adapter.cliType, args.dir);
4480
- this.sendResult(msg, true, { dir: args.dir });
4806
+ case "stop_cli": {
4807
+ const cliType = args?.cliType;
4808
+ const dir = args?.dir || process.cwd();
4809
+ if (!cliType) throw new Error("cliType required");
4810
+ const key = this.getCliKey(cliType, dir);
4811
+ await this.stopCliSession(key);
4812
+ this.sendResult(msg, true, { cliType, dir });
4481
4813
  return;
4482
4814
  }
4483
4815
  case "restart_session": {
4484
- if (this.adapter) await this.switchCliSession(this.adapter.cliType, this.adapter.workingDir);
4816
+ const cliType = args?.cliType || args?.agentType;
4817
+ const dir = args?.dir || process.cwd();
4818
+ if (!cliType) throw new Error("cliType required");
4819
+ const key = this.getCliKey(cliType, dir);
4820
+ await this.stopCliSession(key);
4821
+ await this.startCliSession(cliType, dir);
4485
4822
  this.sendResult(msg, true, { restarted: true });
4486
4823
  return;
4487
4824
  }
@@ -4492,7 +4829,7 @@ var init_adhdev_daemon = __esm({
4492
4829
  this.sendResult(msg, false, { error: `Blocked: "${cmdStr}"` });
4493
4830
  return;
4494
4831
  }
4495
- const cwd = this.adapter?.workingDir || process.cwd();
4832
+ const cwd = args?.dir || process.cwd();
4496
4833
  const { exec: exec2 } = require("child_process");
4497
4834
  exec2(cmdStr, { cwd, timeout: 6e4 }, (err, stdout, stderr) => {
4498
4835
  if (!this.bridge) return;
@@ -4506,7 +4843,33 @@ var init_adhdev_daemon = __esm({
4506
4843
  return;
4507
4844
  }
4508
4845
  case "launch_ide": {
4509
- const result = await launchWithCdp(args);
4846
+ const launchArgs = { ...args, ideId: args.ideId || args.ideType };
4847
+ const ideKey = launchArgs.ideId;
4848
+ console.log(`[launch_ide] target=${ideKey || "auto"}`);
4849
+ const result = await launchWithCdp(launchArgs);
4850
+ if (result.success && result.port && result.ideId && !this.cdpManagers.has(result.ideId)) {
4851
+ console.log(import_chalk2.default.cyan(`[launch_ide] Connecting CDP for ${result.ideId} on port ${result.port}...`));
4852
+ const manager = new DaemonCdpManager(result.port, (msg2) => {
4853
+ console.log(import_chalk2.default.gray(msg2));
4854
+ }, true);
4855
+ const connected = await manager.connect();
4856
+ if (connected) {
4857
+ this.cdpManagers.set(result.ideId, manager);
4858
+ console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${result.ideId} (port ${result.port})`));
4859
+ console.log(import_chalk2.default.green(` \u{1F4E1} CDP: ${this.cdpManagers.size} IDE(s) connected`));
4860
+ if (!this.scriptLoaders.has(result.ideId)) {
4861
+ const config = loadConfig();
4862
+ const serverUrl = config.serverUrl || "https://api.adhf.dev";
4863
+ const loader = new DaemonScriptLoader(serverUrl, result.ideId);
4864
+ loader.setToken(config.connectionToken || "");
4865
+ await loader.start().catch((e) => {
4866
+ console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${result.ideId}: ${e?.message}`));
4867
+ });
4868
+ this.scriptLoaders.set(result.ideId, loader);
4869
+ }
4870
+ }
4871
+ }
4872
+ this.startAgentStreamPolling();
4510
4873
  this.sendResult(msg, result.success, result);
4511
4874
  return;
4512
4875
  }
@@ -4551,48 +4914,111 @@ var init_adhdev_daemon = __esm({
4551
4914
  return new GeminiCliAdapter(workingDir);
4552
4915
  }
4553
4916
  }
4554
- async switchCliSession(cliType, workingDir) {
4555
- console.log(import_chalk2.default.yellow(` \u26A1 Switching CLI to ${cliType} in ${workingDir}...`));
4556
- if (this.adapter) this.adapter.shutdown();
4917
+ async startCliSession(cliType, workingDir) {
4557
4918
  const cliInfo = await detectCLI(cliType);
4558
4919
  if (!cliInfo) throw new Error(`${cliType} not found`);
4559
- this.adapter = this.createAdapter(cliType, workingDir);
4560
- await this.adapter.spawn();
4561
- if (this.bridge && typeof this.adapter.setBridge === "function") {
4562
- this.adapter.setBridge(this.bridge);
4920
+ const key = this.getCliKey(cliType, workingDir);
4921
+ if (this.adapters.has(key)) {
4922
+ console.log(import_chalk2.default.yellow(` \u26A1 CLI ${cliType} already running in ${workingDir}`));
4923
+ return;
4924
+ }
4925
+ console.log(import_chalk2.default.yellow(` \u26A1 Starting CLI ${cliType} in ${workingDir}...`));
4926
+ const adapter = this.createAdapter(cliType, workingDir);
4927
+ await adapter.spawn();
4928
+ if (this.bridge && typeof adapter.setBridge === "function") {
4929
+ adapter.setBridge(this.bridge);
4930
+ }
4931
+ adapter.setOnStatusChange(() => this.sendUnifiedStatusReport());
4932
+ this.adapters.set(key, adapter);
4933
+ this.lastAgentStatus.set(key, "idle");
4934
+ console.log(import_chalk2.default.green(` \u2713 CLI started: ${cliInfo.displayName} v${cliInfo.version || "unknown"} in ${workingDir}`));
4935
+ this.sendUnifiedStatusReport();
4936
+ }
4937
+ async stopCliSession(key) {
4938
+ const adapter = this.adapters.get(key);
4939
+ if (adapter) {
4940
+ adapter.shutdown();
4941
+ this.adapters.delete(key);
4942
+ this.lastAgentStatus.delete(key);
4943
+ this.generatingStartedAt.delete(key);
4944
+ console.log(import_chalk2.default.yellow(` \u{1F6D1} CLI Agent stopped: ${adapter.cliType} in ${adapter.workingDir}`));
4945
+ this.sendUnifiedStatusReport();
4563
4946
  }
4564
- this.adapter.setOnStatusChange(() => this.sendUnifiedStatusReport());
4565
- console.log(import_chalk2.default.green(` \u2713 CLI switched to ${cliType}`));
4566
4947
  }
4567
4948
  // ─── 통합 상태 보고 ─────────────────────────────
4568
4949
  startStatusReporting() {
4569
4950
  const scheduleNext = () => {
4570
- const isProcessing = this.adapter?.isProcessing();
4951
+ const isProcessing = Array.from(this.adapters.values()).some((a) => a.isProcessing());
4571
4952
  const interval = isProcessing ? 2e3 : 15e3;
4572
4953
  this.statusTimer = setTimeout(() => {
4573
- this.sendUnifiedStatusReport();
4954
+ this.sendUnifiedStatusReport().catch(() => {
4955
+ });
4574
4956
  scheduleNext();
4575
4957
  }, interval);
4576
4958
  };
4577
4959
  scheduleNext();
4578
4960
  }
4579
- sendUnifiedStatusReport() {
4961
+ async sendUnifiedStatusReport() {
4580
4962
  if (!this.bridge?.isConnected()) return;
4963
+ if (this.cdpManagers.size > 0 && this.scriptLoaders.size > 0 && !this._cdpChatBusy) {
4964
+ this._cdpChatBusy = true;
4965
+ for (const [ideType, cdp] of this.cdpManagers) {
4966
+ if (!cdp.isConnected) continue;
4967
+ const loader = this.scriptLoaders.get(ideType);
4968
+ if (!loader) continue;
4969
+ const readChatScript = loader.get("read_chat");
4970
+ if (!readChatScript) continue;
4971
+ try {
4972
+ const raw = await cdp.evaluate(readChatScript, 3e4);
4973
+ if (raw && typeof raw === "object") {
4974
+ let { activeModal } = raw;
4975
+ if (activeModal) {
4976
+ const w = activeModal.width ?? Infinity;
4977
+ const h = activeModal.height ?? Infinity;
4978
+ if (w < 80 || h < 40) activeModal = void 0;
4979
+ else activeModal = {
4980
+ message: activeModal.message?.slice(0, 300) ?? "",
4981
+ buttons: (activeModal.buttons ?? []).filter((t) => t.length < 30)
4982
+ };
4983
+ }
4984
+ this._cachedActiveChatMap.set(ideType, { ...raw, activeModal });
4985
+ }
4986
+ } catch {
4987
+ }
4988
+ }
4989
+ this._cdpChatBusy = false;
4990
+ }
4581
4991
  const now = Date.now();
4582
4992
  const perExtData = this.localServer?.getPerExtensionData() || [];
4583
- const extSummary = this.localServer?.getLatestExtensionData() || {
4993
+ const enabledIdesFilter = loadConfig().enabledIdes || [];
4994
+ const filteredExtData = enabledIdesFilter.length > 0 ? perExtData.filter((ext) => enabledIdesFilter.includes(ext.ideType.toLowerCase())) : perExtData;
4995
+ const extSummary = this.localServer?.getLatestExtensionData(enabledIdesFilter) || {
4584
4996
  activeFile: null,
4585
4997
  workspaceFolders: [],
4586
4998
  terminals: 0,
4587
4999
  aiAgents: [],
4588
5000
  connectedIdes: []
4589
5001
  };
4590
- const cdpAgentStreams = this._cachedAgentStreams || [];
4591
- const managedIdes = perExtData.map((ext) => {
4592
- const isMainCdpIde = ext.ideType.toLowerCase() === this.ideType.toLowerCase();
5002
+ const managedIdes = filteredExtData.map((ext) => {
5003
+ const ideKey = ext.ideType.toLowerCase();
5004
+ const isMainCdpIde = this.cdpManagers.has(ideKey);
5005
+ const cdpStreamsForIde = this._cachedAgentStreamsMap.get(ideKey) || [];
5006
+ const extAgentsForIde = (ext.aiAgents || []).filter((a) => {
5007
+ const existsInCdp = cdpStreamsForIde.some((s) => s.agentType === a.id);
5008
+ const existsInExt = ext.agentStreams.some((s) => s.agentType === a.id);
5009
+ return !existsInCdp && !existsInExt;
5010
+ }).map((a) => ({
5011
+ agentType: a.id,
5012
+ agentName: a.name,
5013
+ extensionId: a.id,
5014
+ status: a.status === "active" || a.status === "installed" ? "connected" : "idle",
5015
+ messages: [],
5016
+ inputContent: ""
5017
+ }));
4593
5018
  const ideAgentStreams = [
4594
5019
  ...ext.agentStreams,
4595
- ...isMainCdpIde ? cdpAgentStreams : []
5020
+ ...isMainCdpIde ? cdpStreamsForIde : [],
5021
+ ...extAgentsForIde
4596
5022
  ];
4597
5023
  return {
4598
5024
  ideType: ext.ideType,
@@ -4602,54 +5028,57 @@ var init_adhdev_daemon = __esm({
4602
5028
  activeFile: ext.activeFile,
4603
5029
  terminals: ext.terminals,
4604
5030
  aiAgents: ext.aiAgents,
4605
- activeChat: ext.activeChat,
5031
+ activeChat: isMainCdpIde && this._cachedActiveChatMap.has(ideKey) ? this._cachedActiveChatMap.get(ideKey) : ext.activeChat,
4606
5032
  chats: ext.chats,
4607
5033
  agentStreams: ideAgentStreams,
4608
- cdpConnected: isMainCdpIde ? this.cdpManager?.isConnected || false : false
5034
+ cdpConnected: isMainCdpIde ? this.cdpManagers.get(ext.ideType.toLowerCase())?.isConnected || false : false
4609
5035
  };
4610
5036
  });
4611
5037
  const managedClis = [];
4612
- if (this.adapter) {
4613
- const adapterStatus = this.adapter.getStatus();
5038
+ for (const [key, adapter] of this.adapters.entries()) {
5039
+ const adapterStatus = adapter.getStatus();
4614
5040
  const cliStatus = adapterStatus.status;
4615
- if (cliStatus !== this.lastAgentStatus) {
4616
- if (this.lastAgentStatus === "idle" && cliStatus === "generating") {
4617
- this.generatingStartedAt = now;
4618
- this.bridge.sendMessage("status_event", {
5041
+ let lastStatus = this.lastAgentStatus.get(key) || "idle";
5042
+ if (cliStatus !== lastStatus) {
5043
+ if (lastStatus === "idle" && cliStatus === "generating") {
5044
+ this.generatingStartedAt.set(key, now);
5045
+ this.bridge?.sendMessage("status_event", {
4619
5046
  event: "agent:generating_started",
4620
- chatTitle: `${this.adapter.cliName} Session`,
5047
+ chatTitle: `${adapter.cliName} Session`,
4621
5048
  timestamp: now
4622
5049
  });
4623
- } else if (this.lastAgentStatus === "generating" && cliStatus === "idle") {
4624
- const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
4625
- this.bridge.sendMessage("status_event", {
5050
+ } else if (lastStatus === "generating" && cliStatus === "idle") {
5051
+ const startedAt = this.generatingStartedAt.get(key);
5052
+ const duration = startedAt ? Math.round((now - startedAt) / 1e3) : 0;
5053
+ this.bridge?.sendMessage("status_event", {
4626
5054
  event: "agent:generating_completed",
4627
- chatTitle: `${this.adapter.cliName} Session`,
5055
+ chatTitle: `${adapter.cliName} Session`,
4628
5056
  duration,
4629
5057
  timestamp: now
4630
5058
  });
4631
- this.generatingStartedAt = 0;
5059
+ this.generatingStartedAt.delete(key);
4632
5060
  }
4633
- this.lastAgentStatus = cliStatus;
5061
+ this.lastAgentStatus.set(key, cliStatus);
4634
5062
  }
4635
5063
  const recentMessages = adapterStatus.messages.slice(-50);
4636
5064
  const cliMessages = recentMessages.map((m) => ({
4637
5065
  role: m.role,
4638
5066
  content: m.content.length > 2e3 ? m.content.slice(0, 2e3) + "\n... (truncated)" : m.content
4639
5067
  }));
4640
- const partial = this.adapter.getPartialResponse();
5068
+ const partial = adapter.getPartialResponse();
4641
5069
  if (cliStatus === "generating" && partial) {
4642
- cliMessages.push({ role: "assistant", content: partial.slice(0, 2e3) + "..." });
5070
+ cliMessages.push({ role: "assistant", content: partial.length > 2e3 ? partial.slice(0, 2e3) + "..." : partial + "..." });
4643
5071
  }
4644
5072
  managedClis.push({
4645
- cliType: this.adapter.cliType,
4646
- cliName: this.adapter.cliName,
4647
- workingDir: this.adapter.workingDir,
5073
+ id: key,
5074
+ cliType: adapter.cliType,
5075
+ cliName: adapter.cliName,
4648
5076
  status: cliStatus,
5077
+ workingDir: adapter.workingDir,
4649
5078
  activeChat: {
4650
- id: `cli-${this.adapter.cliType}`,
5079
+ id: key,
4651
5080
  status: cliStatus,
4652
- title: `${this.adapter.cliName} Session`,
5081
+ title: `${adapter.cliName} Session`,
4653
5082
  messages: cliMessages,
4654
5083
  inputContent: "",
4655
5084
  activeModal: adapterStatus.activeModal
@@ -4673,8 +5102,12 @@ var init_adhdev_daemon = __esm({
4673
5102
  // 관리 중인 IDE/CLI (계층 데이터)
4674
5103
  managedIdes,
4675
5104
  managedClis,
4676
- // IDE 감지 결과 (설치된 IDE 목록)
4677
- detectedIdes: this.detectedIdes,
5105
+ // IDE 감지 결과 (설치된 IDE 목록) — 프론트엔드 호환 매핑 (id→type)
5106
+ detectedIdes: this.detectedIdes.map((ide) => ({
5107
+ ...ide,
5108
+ type: ide.id
5109
+ // 프론트엔드 MachineDetail.tsx는 'type' 필드 사용
5110
+ })),
4678
5111
  // P2P 상태
4679
5112
  p2p: {
4680
5113
  available: this.p2p?.isAvailable || false,
@@ -4682,18 +5115,25 @@ var init_adhdev_daemon = __esm({
4682
5115
  peers: this.p2p?.connectedPeerCount || 0,
4683
5116
  screenshotActive: this.p2p?.screenshotActive || false
4684
5117
  },
4685
- cdpConnected: this.cdpManager?.isConnected || false,
4686
- scriptLoaderReady: !!this.scriptLoader,
5118
+ cdpConnected: this.getAnyCdp() !== null,
5119
+ scriptLoaderReady: this.scriptLoaders.size > 0,
4687
5120
  timestamp: now,
4688
5121
  // ─── Legacy compat (서버 기존 로직용, 점진적 제거 예정) ───
4689
5122
  activeFile: extSummary.activeFile,
4690
- workspaceFolders: extSummary.workspaceFolders.length > 0 ? extSummary.workspaceFolders : this.adapter ? [{ name: path4.basename(this.adapter.workingDir), path: this.adapter.workingDir }] : [],
5123
+ workspaceFolders: extSummary.workspaceFolders.length > 0 ? extSummary.workspaceFolders : Array.from(this.adapters.values()).map((a) => ({ name: path4.basename(a.workingDir), path: a.workingDir })),
4691
5124
  terminals: extSummary.terminals,
5125
+ // Legacy aiAgents: managedIdes에서 각 IDE의 aiAgents를 통합
4692
5126
  aiAgents: [
4693
- ...extSummary.aiAgents,
4694
- ...this.adapter ? [{ id: this.adapter.cliType, name: this.adapter.cliName, status: this.lastAgentStatus }] : []
5127
+ ...managedIdes.flatMap((ide) => (ide.aiAgents || []).map((a) => ({
5128
+ id: a.id,
5129
+ name: a.name,
5130
+ status: a.status,
5131
+ ideType: ide.ideType
5132
+ }))),
5133
+ ...Array.from(this.adapters.entries()).map(([k, a]) => ({ id: a.cliType, name: a.cliName, status: this.lastAgentStatus.get(k) || "idle" }))
4695
5134
  ],
4696
5135
  activeChat: managedClis[0]?.activeChat || managedIdes[0]?.activeChat || null,
5136
+ // Legacy agentStreams: managedIdes의 agentStreams를 통합 (이미 IDE별로 올바르게 구성됨)
4697
5137
  agentStreams: [
4698
5138
  ...managedClis.map((c) => ({
4699
5139
  agentType: c.cliType,
@@ -4703,7 +5143,7 @@ var init_adhdev_daemon = __esm({
4703
5143
  messages: c.activeChat?.messages || [],
4704
5144
  inputContent: ""
4705
5145
  })),
4706
- ...cdpAgentStreams
5146
+ ...managedIdes.flatMap((ide) => ide.agentStreams || [])
4707
5147
  ],
4708
5148
  connectedExtensions: extSummary.connectedIdes,
4709
5149
  system: {
@@ -4735,18 +5175,22 @@ var init_adhdev_daemon = __esm({
4735
5175
  if (!this.running) return;
4736
5176
  this.running = false;
4737
5177
  console.log(import_chalk2.default.yellow("\n Shutting down ADHDev Daemon..."));
4738
- this.adapter?.cancel();
5178
+ for (const adapter of this.adapters.values()) adapter.shutdown();
5179
+ this.adapters.clear();
4739
5180
  if (this.statusTimer) clearTimeout(this.statusTimer);
4740
5181
  if (this.cdpDiscoveryTimer) clearInterval(this.cdpDiscoveryTimer);
4741
5182
  if (this.screenshotTimer) clearInterval(this.screenshotTimer);
4742
5183
  if (this.agentStreamTimer) clearInterval(this.agentStreamTimer);
4743
- if (this.agentStreamManager && this.cdpManager) {
4744
- await this.agentStreamManager.dispose(this.cdpManager).catch(() => {
5184
+ const anyCdpStop = this.getAnyCdp();
5185
+ if (this.agentStreamManager && anyCdpStop) {
5186
+ await this.agentStreamManager.dispose(anyCdpStop).catch(() => {
4745
5187
  });
4746
5188
  }
4747
5189
  this.p2p?.disconnect();
4748
- this.scriptLoader?.stop();
4749
- this.cdpManager?.disconnect();
5190
+ for (const loader of this.scriptLoaders.values()) loader.stop();
5191
+ this.scriptLoaders.clear();
5192
+ for (const m of this.cdpManagers.values()) m.disconnect();
5193
+ this.cdpManagers.clear();
4750
5194
  this.localServer?.stop();
4751
5195
  this.bridge?.disconnect();
4752
5196
  removeDaemonPid();
@@ -4754,6 +5198,33 @@ var init_adhdev_daemon = __esm({
4754
5198
  process.exit(0);
4755
5199
  }
4756
5200
  // ─── CDP 관리 ───────────────────────────────────
5201
+ /** 첫 번째 연결된 CDP 매니저 반환 */
5202
+ getAnyCdp() {
5203
+ for (const m of this.cdpManagers.values()) {
5204
+ if (m.isConnected) return m;
5205
+ }
5206
+ return null;
5207
+ }
5208
+ /** 특정 IDE의 CDP 매니저 반환 */
5209
+ getCdpFor(ideType) {
5210
+ return this.cdpManagers.get(ideType.toLowerCase()) || null;
5211
+ }
5212
+ /** Agent stream polling 시작 (idempotent — 이미 시작됐으면 무시) */
5213
+ startAgentStreamPolling() {
5214
+ if (this.agentStreamTimer) return;
5215
+ this.agentStreamTimer = setInterval(async () => {
5216
+ if (!this.agentStreamManager || this.cdpManagers.size === 0) return;
5217
+ for (const [ideType, cdp] of this.cdpManagers) {
5218
+ if (!cdp.isConnected) continue;
5219
+ try {
5220
+ await this.agentStreamManager.syncAgentSessions(cdp);
5221
+ const streams = await this.agentStreamManager.collectAgentStreams(cdp);
5222
+ this._cachedAgentStreamsMap.set(ideType, streams);
5223
+ } catch {
5224
+ }
5225
+ }
5226
+ }, 5e3);
5227
+ }
4757
5228
  async initCdp() {
4758
5229
  const cdpPortMap = {
4759
5230
  cursor: 9333,
@@ -4763,7 +5234,7 @@ var init_adhdev_daemon = __esm({
4763
5234
  };
4764
5235
  const portsToTry = [];
4765
5236
  for (const ide of this.detectedIdes) {
4766
- const ideKey = ide.type || ide.name?.toLowerCase();
5237
+ const ideKey = ide.id || ide.name?.toLowerCase();
4767
5238
  const port = cdpPortMap[ideKey];
4768
5239
  if (port) portsToTry.push({ port, ide: ideKey });
4769
5240
  }
@@ -4772,25 +5243,36 @@ var init_adhdev_daemon = __esm({
4772
5243
  portsToTry.push({ port, ide });
4773
5244
  }
4774
5245
  }
4775
- for (const { port, ide } of portsToTry) {
5246
+ const enabledIdes = loadConfig().enabledIdes || [];
5247
+ const filteredPorts = enabledIdes.length > 0 ? portsToTry.filter((p) => enabledIdes.includes(p.ide)) : portsToTry;
5248
+ for (const { port, ide } of filteredPorts) {
4776
5249
  const manager = new DaemonCdpManager(port, (msg) => {
4777
5250
  console.log(import_chalk2.default.gray(msg));
4778
- });
5251
+ }, true);
4779
5252
  const connected = await manager.connect();
4780
5253
  if (connected) {
4781
- this.cdpManager = manager;
4782
- this.ideType = ide;
4783
- console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${ide} (port ${port})`));
4784
- this.cdpDiscoveryTimer = setInterval(async () => {
4785
- if (this.cdpManager?.isConnected) {
4786
- await this.cdpManager.discoverAgentWebviews();
4787
- }
4788
- }, 3e4);
4789
- return;
5254
+ const actualPort = manager.getPort();
5255
+ const actualIde = Object.entries(cdpPortMap).find(([, p]) => p === actualPort)?.[0] || ide;
5256
+ this.cdpManagers.set(actualIde, manager);
5257
+ console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${actualIde} (port ${actualPort})`));
5258
+ if (this.ideType === "unknown") {
5259
+ this.ideType = actualIde;
5260
+ }
4790
5261
  }
4791
5262
  }
4792
- console.log(import_chalk2.default.yellow(` \u26A0 CDP not available \u2014 tried ports: ${portsToTry.map((p) => `${p.ide}:${p.port}`).join(", ")}`));
4793
- console.log(import_chalk2.default.yellow(` IDE may need relaunch with --remote-debugging-port`));
5263
+ if (this.cdpManagers.size > 0) {
5264
+ console.log(import_chalk2.default.green(` \u{1F4E1} CDP: ${this.cdpManagers.size} IDE(s) connected`));
5265
+ this.cdpDiscoveryTimer = setInterval(async () => {
5266
+ for (const m of this.cdpManagers.values()) {
5267
+ if (m.isConnected) {
5268
+ await m.discoverAgentWebviews();
5269
+ }
5270
+ }
5271
+ }, 3e4);
5272
+ } else {
5273
+ console.log(import_chalk2.default.yellow(` \u26A0 CDP not available \u2014 tried ports: ${portsToTry.map((p) => `${p.ide}:${p.port}`).join(", ")}`));
5274
+ console.log(import_chalk2.default.yellow(` IDE may need relaunch with --remote-debugging-port`));
5275
+ }
4794
5276
  }
4795
5277
  };
4796
5278
  }
@@ -6170,6 +6652,7 @@ async function installCliOnly() {
6170
6652
 
6171
6653
  // src/index.ts
6172
6654
  init_detector();
6655
+ init_cli_detector();
6173
6656
  init_config();
6174
6657
  init_launch();
6175
6658
  var import_fs3 = require("fs");
@@ -6299,6 +6782,15 @@ program.command("status").description("Show current ADHDev setup status").action
6299
6782
  }
6300
6783
  }
6301
6784
  }
6785
+ const clis = await detectCLIs();
6786
+ const installedClis = clis.filter((c) => c.installed);
6787
+ if (installedClis.length > 0) {
6788
+ console.log(` ${import_chalk4.default.bold("CLI Agents:")}`);
6789
+ installedClis.forEach((cli) => {
6790
+ const ver = cli.version ? import_chalk4.default.gray(` v${cli.version}`) : "";
6791
+ console.log(` ${import_chalk4.default.green("\u2713")} ${cli.icon} ${cli.displayName}${ver}`);
6792
+ });
6793
+ }
6302
6794
  console.log(` ${import_chalk4.default.bold("Extensions:")} ${config.installedExtensions.length} installed`);
6303
6795
  config.installedExtensions.forEach((ext) => {
6304
6796
  console.log(import_chalk4.default.gray(` \u2022 ${ext}`));
@@ -6326,6 +6818,17 @@ program.command("detect").description("Detect installed IDEs on your system").ac
6326
6818
  console.log(` ${import_chalk4.default.gray("\u2717")} ${ide.icon} ${import_chalk4.default.gray(ide.displayName)} \u2014 not found`);
6327
6819
  }
6328
6820
  });
6821
+ console.log(import_chalk4.default.bold("\n\u{1F50D} Detecting installed CLI Agents...\n"));
6822
+ const clis = await detectCLIs();
6823
+ clis.forEach((cli) => {
6824
+ if (cli.installed) {
6825
+ const version = cli.version ? import_chalk4.default.gray(` v${cli.version}`) : "";
6826
+ console.log(` ${import_chalk4.default.green("\u2713")} ${cli.icon} ${import_chalk4.default.bold(cli.displayName)}${version}`);
6827
+ console.log(import_chalk4.default.gray(` Path: ${cli.path}`));
6828
+ } else {
6829
+ console.log(` ${import_chalk4.default.gray("\u2717")} ${cli.icon} ${import_chalk4.default.gray(cli.displayName)} \u2014 not found`);
6830
+ }
6831
+ });
6329
6832
  console.log();
6330
6833
  });
6331
6834
  program.command("reset").description("Reset ADHDev configuration").action(async () => {