adhdev 0.1.21 → 0.1.22

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 +448 -154
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -821,6 +821,8 @@ var init_cli_bridge = __esm({
821
821
  const handlers = this.messageHandlers.get(message.type);
822
822
  if (handlers) {
823
823
  handlers.forEach((h) => h(message));
824
+ } else if (message.type !== "auth_ok" && message.type !== "auth_error") {
825
+ console.log(`[CliBridge] Unhandled message type: ${message.type}`);
824
826
  }
825
827
  } catch (error) {
826
828
  console.error("[CliBridge] Failed to parse message:", error);
@@ -1931,8 +1933,10 @@ var init_daemon_cdp = __esm({
1931
1933
  MAX_FAILURES = 5;
1932
1934
  agentSessions = /* @__PURE__ */ new Map();
1933
1935
  logFn;
1934
- constructor(port = 9333, logFn) {
1936
+ disableFallback;
1937
+ constructor(port = 9333, logFn, disableFallback = false) {
1935
1938
  this.port = port;
1939
+ this.disableFallback = disableFallback;
1936
1940
  this.logFn = logFn || ((msg) => {
1937
1941
  console.log(msg);
1938
1942
  try {
@@ -2011,6 +2015,7 @@ var init_daemon_cdp = __esm({
2011
2015
  async findTarget() {
2012
2016
  let target = await this.findTargetOnPort(this.port);
2013
2017
  if (target) return target;
2018
+ if (this.disableFallback) return null;
2014
2019
  const fallbackPorts = [9222, 9333, 9335, 9337, 9339].filter((p) => p !== this.port);
2015
2020
  for (const port of fallbackPorts) {
2016
2021
  target = await this.findTargetOnPort(port);
@@ -2070,7 +2075,10 @@ var init_daemon_cdp = __esm({
2070
2075
  this.reconnectTimer = setTimeout(async () => {
2071
2076
  this.reconnectTimer = null;
2072
2077
  if (!this._connected) {
2073
- await this.connect();
2078
+ const ok = await this.connect();
2079
+ if (!ok && !this._connected) {
2080
+ this.scheduleReconnect();
2081
+ }
2074
2082
  }
2075
2083
  }, 5e3);
2076
2084
  }
@@ -2256,7 +2264,9 @@ var init_daemon_cdp = __esm({
2256
2264
  try {
2257
2265
  const result = await this.sendInternal("Page.captureScreenshot", {
2258
2266
  format: "jpeg",
2259
- quality: 60
2267
+ quality: 25,
2268
+ clip: void 0
2269
+ // full viewport
2260
2270
  }, 1e4);
2261
2271
  if (result?.data) {
2262
2272
  return Buffer.from(result.data, "base64");
@@ -2577,12 +2587,22 @@ var init_daemon_p2p = __esm({
2577
2587
  fileRequestHandler = null;
2578
2588
  inputHandler = null;
2579
2589
  commandHandler = null;
2590
+ _ssDebugDone = false;
2580
2591
  get screenshotActive() {
2581
2592
  for (const peer of this.peers.values()) {
2582
2593
  if (peer.screenshotActive && peer.state === "connected") return true;
2583
2594
  }
2584
2595
  return false;
2585
2596
  }
2597
+ /** Get the ideType for the currently active screenshot request */
2598
+ get screenshotIdeType() {
2599
+ for (const peer of this.peers.values()) {
2600
+ if (peer.screenshotActive && peer.state === "connected" && peer.screenshotIdeType) {
2601
+ return peer.screenshotIdeType;
2602
+ }
2603
+ }
2604
+ return void 0;
2605
+ }
2586
2606
  constructor(bridge) {
2587
2607
  this.bridge = bridge;
2588
2608
  this.tryLoadNodeDatachannel();
@@ -2781,11 +2801,17 @@ var init_daemon_p2p = __esm({
2781
2801
  }
2782
2802
  });
2783
2803
  const screenshotCh = pc.createDataChannel("screenshots");
2784
- entry.screenshotChannel = screenshotCh;
2785
2804
  screenshotCh.onOpen(() => {
2786
2805
  log(`Screenshots channel OPEN for peer ${pid}`);
2787
- entry.state = "connected";
2788
- this.notifyStateChange();
2806
+ const peer = this.peers.get(pid);
2807
+ if (peer) {
2808
+ peer.screenshotChannel = screenshotCh;
2809
+ peer.state = "connected";
2810
+ this.notifyStateChange();
2811
+ if (peer.screenshotActive) {
2812
+ log(`Screenshots auto-starting for peer ${pid} (was waiting for channel open)`);
2813
+ }
2814
+ }
2789
2815
  });
2790
2816
  screenshotCh.onClosed(() => {
2791
2817
  log(`Screenshots channel CLOSED for peer ${pid}`);
@@ -2819,14 +2845,24 @@ var init_daemon_p2p = __esm({
2819
2845
  const text = typeof msg === "string" ? msg : msg.toString("utf-8");
2820
2846
  try {
2821
2847
  const parsed = JSON.parse(text);
2848
+ log(`Files message from peer ${peerId}: type=${parsed.type}`);
2822
2849
  if (parsed.type === "screenshot_start") {
2823
2850
  const peer = this.peers.get(peerId);
2824
- if (peer) peer.screenshotActive = true;
2851
+ if (peer) {
2852
+ peer.screenshotActive = true;
2853
+ peer.screenshotIdeType = parsed.ideType || void 0;
2854
+ log(`screenshot_start: peer=${peerId}, ideType=${parsed.ideType || "any"}, channelOpen=${!!peer.screenshotChannel}, state=${peer.state}`);
2855
+ } else {
2856
+ log(`screenshot_start: peer ${peerId} NOT FOUND in peers map!`);
2857
+ }
2825
2858
  return;
2826
2859
  }
2827
2860
  if (parsed.type === "screenshot_stop") {
2828
2861
  const peer = this.peers.get(peerId);
2829
- if (peer) peer.screenshotActive = false;
2862
+ if (peer) {
2863
+ peer.screenshotActive = false;
2864
+ log(`screenshot_stop: peer=${peerId}`);
2865
+ }
2830
2866
  return;
2831
2867
  }
2832
2868
  if (parsed.type === "input") {
@@ -2863,14 +2899,28 @@ var init_daemon_p2p = __esm({
2863
2899
  sendScreenshot(base64Data) {
2864
2900
  let sentAny = false;
2865
2901
  const buffer = Buffer.from(base64Data, "base64");
2866
- for (const peer of this.peers.values()) {
2902
+ let debugOnce = !this._ssDebugDone;
2903
+ const CHUNK_SIZE = 6e4;
2904
+ for (const [pid, peer] of this.peers.entries()) {
2905
+ if (debugOnce) {
2906
+ log(`sendScreenshot peer=${pid}: state=${peer.state}, hasSsCh=${!!peer.screenshotChannel}, ssActive=${peer.screenshotActive}, chOpen=${peer.screenshotChannel?.isOpen?.() ?? "N/A"}, bufSize=${buffer.length}`);
2907
+ }
2867
2908
  if (peer.state !== "connected" || !peer.screenshotChannel || !peer.screenshotActive) continue;
2868
2909
  try {
2869
- peer.screenshotChannel.sendMessageBinary(buffer);
2910
+ if (!peer.screenshotChannel.isOpen()) continue;
2911
+ const header = Buffer.alloc(4);
2912
+ header.writeUInt32BE(buffer.length, 0);
2913
+ peer.screenshotChannel.sendMessageBinary(header);
2914
+ for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
2915
+ const chunk = buffer.subarray(offset, Math.min(offset + CHUNK_SIZE, buffer.length));
2916
+ peer.screenshotChannel.sendMessageBinary(chunk);
2917
+ }
2870
2918
  sentAny = true;
2871
- } catch {
2919
+ } catch (e) {
2920
+ if (debugOnce) log(`sendScreenshot ERROR for peer ${pid}: ${e?.message}`);
2872
2921
  }
2873
2922
  }
2923
+ if (debugOnce) this._ssDebugDone = true;
2874
2924
  return sentAny;
2875
2925
  }
2876
2926
  // ─── 핸들러 등록 ────────────────────────────
@@ -2904,7 +2954,9 @@ var init_daemon_p2p = __esm({
2904
2954
  return;
2905
2955
  }
2906
2956
  try {
2907
- const result = await this.inputHandler({ action, params });
2957
+ const peer = this.peers.get(peerId);
2958
+ const ideType = peer?.screenshotIdeType;
2959
+ const result = await this.inputHandler({ action, params, _targetInstance: ideType ? `${ideType}_` : void 0 });
2908
2960
  this.sendToPeer(peerId, { id, type: "response", success: true, result });
2909
2961
  } catch (e) {
2910
2962
  this.sendToPeer(peerId, { id, type: "response", success: false, error: e?.message });
@@ -3156,13 +3208,46 @@ var init_daemon_commands = __esm({
3156
3208
  DaemonCommandHandler = class {
3157
3209
  ctx;
3158
3210
  agentStream = null;
3211
+ /** Get CDP manager for a specific ideType, or first connected one */
3212
+ getCdp(ideType) {
3213
+ const key = ideType || this._currentIdeType;
3214
+ if (key) {
3215
+ const m = this.ctx.cdpManagers.get(key.toLowerCase());
3216
+ if (m?.isConnected) return m;
3217
+ }
3218
+ for (const m of this.ctx.cdpManagers.values()) {
3219
+ if (m.isConnected) return m;
3220
+ }
3221
+ return null;
3222
+ }
3223
+ /** Current IDE type extracted from command args (per-request) */
3224
+ _currentIdeType;
3225
+ /** Extract ideType from _targetInstance (e.g. 'vscode_abc123' → 'vscode') */
3226
+ extractIdeType(args) {
3227
+ if (args?._targetInstance) {
3228
+ const parts = args._targetInstance.split("_");
3229
+ if (parts.length >= 2) return parts[0];
3230
+ }
3231
+ return void 0;
3232
+ }
3159
3233
  constructor(ctx) {
3160
3234
  this.ctx = ctx;
3161
3235
  }
3236
+ /** Get ScriptLoader for specific ideType, fallback to first available */
3237
+ getScriptLoader(ideType) {
3238
+ const key = ideType || this._currentIdeType;
3239
+ if (key) {
3240
+ const l = this.ctx.scriptLoaders.get(key.toLowerCase());
3241
+ if (l) return l;
3242
+ }
3243
+ for (const l of this.ctx.scriptLoaders.values()) return l;
3244
+ return void 0;
3245
+ }
3162
3246
  setAgentStreamManager(manager) {
3163
3247
  this.agentStream = manager;
3164
3248
  }
3165
3249
  async handle(cmd, args) {
3250
+ this._currentIdeType = this.extractIdeType(args);
3166
3251
  switch (cmd) {
3167
3252
  // ─── CDP 직접 처리 ───────────────────
3168
3253
  case "read_chat":
@@ -3205,8 +3290,13 @@ var init_daemon_commands = __esm({
3205
3290
  return this.handleFileListBrowse(args);
3206
3291
  // ─── vscode API → Extension 위임 ────
3207
3292
  case "vscode_command_exec":
3208
- case "execute_vscode_command":
3209
- return this.delegateToExtension(args?.command || cmd, args?.args);
3293
+ case "execute_vscode_command": {
3294
+ const resolvedCmd = args?.commandId || args?.command;
3295
+ if (resolvedCmd === "adhdev.captureCdpScreenshot") {
3296
+ return this.handleScreenshot(args);
3297
+ }
3298
+ return this.delegateToExtension(resolvedCmd || cmd, args?.args);
3299
+ }
3210
3300
  case "get_open_editors":
3211
3301
  return this.delegateToExtension("adhdev.getOpenEditors", [args]);
3212
3302
  case "open_tab":
@@ -3249,59 +3339,97 @@ var init_daemon_commands = __esm({
3249
3339
  }
3250
3340
  // ─── CDP 기반 채팅 명령 ──────────────────────
3251
3341
  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");
3342
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3343
+ const script = this.getScriptLoader()?.get("read_chat");
3254
3344
  if (!script) return { success: false, error: "read_chat script not loaded" };
3255
3345
  try {
3256
- const result = await this.ctx.cdp.evaluate(script, 5e4);
3346
+ const result = await this.getCdp().evaluate(script, 5e4);
3257
3347
  return { success: true, ...result };
3258
3348
  } catch (e) {
3259
3349
  return { success: false, error: e.message };
3260
3350
  }
3261
3351
  }
3262
3352
  async handleSendChat(args) {
3263
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3264
3353
  const text = args?.text || args?.message;
3265
3354
  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 };
3355
+ const targetCdp = this.getCdp();
3356
+ const targetLoader = this.getScriptLoader();
3357
+ if (targetCdp?.isConnected && targetLoader) {
3358
+ const script = targetLoader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
3359
+ if (script) {
3360
+ console.log(`[send_chat] Targeting IDE: ${this._currentIdeType || "fallback-first"}`);
3361
+ try {
3362
+ await targetCdp.evaluate(script, 3e4);
3363
+ return { success: true, sent: true };
3364
+ } catch (e) {
3365
+ console.log(`[send_chat] Failed on ${this._currentIdeType}: ${e.message}`);
3366
+ }
3367
+ }
3273
3368
  }
3369
+ console.log(`[send_chat] No specific target, trying all CDPs...`);
3370
+ for (const [ideType, cdp] of this.ctx.cdpManagers) {
3371
+ if (!cdp.isConnected) continue;
3372
+ const loader = this.ctx.scriptLoaders.get(ideType);
3373
+ if (!loader) continue;
3374
+ const script = loader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
3375
+ if (!script) continue;
3376
+ console.log(`[send_chat] Trying IDE: ${ideType}`);
3377
+ try {
3378
+ await cdp.evaluate(script, 3e4);
3379
+ return { success: true, sent: true, targetIde: ideType };
3380
+ } catch (e) {
3381
+ console.log(`[send_chat] Failed on ${ideType}: ${e.message}`);
3382
+ }
3383
+ }
3384
+ return { success: false, error: "No CDP could send the message" };
3274
3385
  }
3275
3386
  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" };
3387
+ const cdp = this.getCdp();
3388
+ if (!cdp?.isConnected) {
3389
+ console.log(`[list_chats] CDP not connected, ideType=${this._currentIdeType}`);
3390
+ return { success: false, error: "CDP not connected" };
3391
+ }
3392
+ const script = this.getScriptLoader()?.get("list_chats");
3393
+ if (!script) {
3394
+ console.log(`[list_chats] script not loaded`);
3395
+ return { success: false, error: "list_chats script not loaded" };
3396
+ }
3279
3397
  try {
3280
- const result = await this.ctx.cdp.evaluate(script, 3e4);
3281
- return { success: true, chats: result };
3398
+ console.log(`[list_chats] Evaluating on ideType=${this._currentIdeType}`);
3399
+ const result = await cdp.evaluate(script, 3e4);
3400
+ let parsed = result;
3401
+ if (typeof result === "string") {
3402
+ try {
3403
+ parsed = JSON.parse(result);
3404
+ } catch {
3405
+ }
3406
+ }
3407
+ console.log(`[list_chats] Result type=${typeof parsed}, isArray=${Array.isArray(parsed)}, len=${Array.isArray(parsed) ? parsed.length : "N/A"}`);
3408
+ return { success: true, chats: parsed };
3282
3409
  } catch (e) {
3410
+ console.log(`[list_chats] Error: ${e.message}`);
3283
3411
  return { success: false, error: e.message };
3284
3412
  }
3285
3413
  }
3286
3414
  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");
3415
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3416
+ const script = this.getScriptLoader()?.get("new_session");
3289
3417
  if (!script) return { success: false, error: "new_session script not loaded" };
3290
3418
  try {
3291
- await this.ctx.cdp.evaluate(script, 15e3);
3419
+ await this.getCdp().evaluate(script, 15e3);
3292
3420
  return { success: true };
3293
3421
  } catch (e) {
3294
3422
  return { success: false, error: e.message };
3295
3423
  }
3296
3424
  }
3297
3425
  async handleSwitchChat(args) {
3298
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3426
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3299
3427
  const sessionId = args?.sessionId || args?.id || args?.chatId;
3300
3428
  if (!sessionId) return { success: false, error: "sessionId required" };
3301
- const script = this.ctx.scriptLoader?.getWithParams("switch_session", { SESSION_ID: sessionId });
3429
+ const script = this.getScriptLoader()?.getWithParams("switch_session", { SESSION_ID: sessionId });
3302
3430
  if (!script) return { success: false, error: "switch_session script not loaded" };
3303
3431
  try {
3304
- await this.ctx.cdp.evaluate(script, 15e3);
3432
+ await this.getCdp().evaluate(script, 15e3);
3305
3433
  return { success: true };
3306
3434
  } catch (e) {
3307
3435
  return { success: false, error: e.message };
@@ -3315,12 +3443,12 @@ var init_daemon_commands = __esm({
3315
3443
  return this.delegateToExtension("workbench.action.openSettings", [`cursor.model`]);
3316
3444
  }
3317
3445
  async handleResolveModal(args) {
3318
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3446
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3319
3447
  const button = args?.button || args?.buttonText || "Accept";
3320
- const script = this.ctx.scriptLoader?.getWithParams("resolve_modal", { BUTTON_TEXT: button });
3448
+ const script = this.getScriptLoader()?.getWithParams("resolve_modal", { BUTTON_TEXT: button });
3321
3449
  if (!script) {
3322
3450
  try {
3323
- await this.ctx.cdp.evaluate(`
3451
+ await this.getCdp().evaluate(`
3324
3452
  (() => {
3325
3453
  const btns = [...document.querySelectorAll('button')];
3326
3454
  const btn = btns.find(b => b.textContent?.trim() === ${JSON.stringify(button)});
@@ -3334,7 +3462,7 @@ var init_daemon_commands = __esm({
3334
3462
  }
3335
3463
  }
3336
3464
  try {
3337
- await this.ctx.cdp.evaluate(script, 1e4);
3465
+ await this.getCdp().evaluate(script, 1e4);
3338
3466
  return { success: true };
3339
3467
  } catch (e) {
3340
3468
  return { success: false, error: e.message };
@@ -3342,22 +3470,23 @@ var init_daemon_commands = __esm({
3342
3470
  }
3343
3471
  // ─── CDP 직접 명령 ──────────────────────────
3344
3472
  async handleCdpEval(args) {
3345
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3473
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3346
3474
  const expression = args?.expression || args?.script;
3347
3475
  if (!expression) return { success: false, error: "expression required" };
3348
3476
  try {
3349
- const result = await this.ctx.cdp.evaluate(expression, 5e4);
3477
+ const result = await this.getCdp().evaluate(expression, 5e4);
3350
3478
  return { success: true, result };
3351
3479
  } catch (e) {
3352
3480
  return { success: false, error: e.message };
3353
3481
  }
3354
3482
  }
3355
3483
  async handleScreenshot(args) {
3356
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3484
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3357
3485
  try {
3358
- const buf = await this.ctx.cdp.captureScreenshot();
3486
+ const buf = await this.getCdp().captureScreenshot();
3359
3487
  if (buf) {
3360
- return { success: true, screenshot: buf.toString("base64"), format: "jpeg" };
3488
+ const b64 = buf.toString("base64");
3489
+ return { success: true, result: b64, base64: b64, screenshot: b64, format: "jpeg" };
3361
3490
  }
3362
3491
  return { success: false, error: "Screenshot failed" };
3363
3492
  } catch (e) {
@@ -3365,26 +3494,26 @@ var init_daemon_commands = __esm({
3365
3494
  }
3366
3495
  }
3367
3496
  async handleCdpCommand(args) {
3368
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3497
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3369
3498
  const method = args?.method;
3370
3499
  const params = args?.params || {};
3371
3500
  if (!method) return { success: false, error: "method required" };
3372
3501
  try {
3373
- const result = await this.ctx.cdp.sendCdpCommand(method, params);
3502
+ const result = await this.getCdp().sendCdpCommand(method, params);
3374
3503
  return { success: true, result };
3375
3504
  } catch (e) {
3376
3505
  return { success: false, error: e.message };
3377
3506
  }
3378
3507
  }
3379
3508
  async handleCdpBatch(args) {
3380
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3509
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3381
3510
  const commands = args?.commands;
3382
3511
  const stopOnError = args?.stopOnError !== false;
3383
3512
  if (!commands?.length) return { success: false, error: "commands array required" };
3384
3513
  const results = [];
3385
3514
  for (const cmd of commands) {
3386
3515
  try {
3387
- const result = await this.ctx.cdp.sendCdpCommand(cmd.method, cmd.params || {});
3516
+ const result = await this.getCdp().sendCdpCommand(cmd.method, cmd.params || {});
3388
3517
  results.push({ method: cmd.method, success: true, result });
3389
3518
  } catch (e) {
3390
3519
  results.push({ method: cmd.method, success: false, error: e.message });
@@ -3394,49 +3523,60 @@ var init_daemon_commands = __esm({
3394
3523
  return { success: true, results };
3395
3524
  }
3396
3525
  async handleCdpRemoteAction(args) {
3397
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3526
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3398
3527
  const action = args?.action;
3399
3528
  const params = args?.params || args;
3400
3529
  try {
3401
3530
  switch (action) {
3402
3531
  case "input_key": {
3403
3532
  const { key, modifiers } = params;
3404
- await this.ctx.cdp.send("Input.dispatchKeyEvent", {
3533
+ await this.getCdp().send("Input.dispatchKeyEvent", {
3405
3534
  type: "keyDown",
3406
3535
  key,
3407
3536
  ...modifiers?.ctrl ? { modifiers: 2 } : {},
3408
3537
  ...modifiers?.shift ? { modifiers: 8 } : {}
3409
3538
  });
3410
- await this.ctx.cdp.send("Input.dispatchKeyEvent", { type: "keyUp", key });
3539
+ await this.getCdp().send("Input.dispatchKeyEvent", { type: "keyUp", key });
3411
3540
  return { success: true };
3412
3541
  }
3413
3542
  case "input_click": {
3414
- const { x, y, button: btn } = params;
3415
- await this.ctx.cdp.send("Input.dispatchMouseEvent", {
3543
+ let { x, y, nx, ny, button: btn } = params;
3544
+ if ((x === void 0 || y === void 0) && nx !== void 0 && ny !== void 0) {
3545
+ const viewport = await this.getCdp().evaluate(
3546
+ "JSON.stringify({ w: window.innerWidth, h: window.innerHeight })"
3547
+ );
3548
+ const { w, h } = JSON.parse(viewport);
3549
+ x = Math.round(nx * w);
3550
+ y = Math.round(ny * h);
3551
+ }
3552
+ if (x === void 0 || y === void 0) {
3553
+ return { success: false, error: "No coordinates provided (x,y or nx,ny required)" };
3554
+ }
3555
+ await this.getCdp().send("Input.dispatchMouseEvent", {
3416
3556
  type: "mousePressed",
3417
3557
  x,
3418
3558
  y,
3419
3559
  button: btn || "left",
3420
3560
  clickCount: 1
3421
3561
  });
3422
- await this.ctx.cdp.send("Input.dispatchMouseEvent", {
3562
+ await this.getCdp().send("Input.dispatchMouseEvent", {
3423
3563
  type: "mouseReleased",
3424
3564
  x,
3425
3565
  y,
3426
3566
  button: btn || "left",
3427
3567
  clickCount: 1
3428
3568
  });
3429
- return { success: true };
3569
+ return { success: true, x, y };
3430
3570
  }
3431
3571
  case "input_type": {
3432
3572
  const { text } = params;
3433
3573
  for (const char of text || "") {
3434
- await this.ctx.cdp.send("Input.dispatchKeyEvent", {
3574
+ await this.getCdp().send("Input.dispatchKeyEvent", {
3435
3575
  type: "keyDown",
3436
3576
  text: char,
3437
3577
  key: char
3438
3578
  });
3439
- await this.ctx.cdp.send("Input.dispatchKeyEvent", { type: "keyUp", key: char });
3579
+ await this.getCdp().send("Input.dispatchKeyEvent", { type: "keyUp", key: char });
3440
3580
  }
3441
3581
  return { success: true };
3442
3582
  }
@@ -3445,9 +3585,28 @@ var init_daemon_commands = __esm({
3445
3585
  case "page_eval":
3446
3586
  return this.handleCdpEval(params);
3447
3587
  case "dom_query": {
3448
- const html = await this.ctx.cdp.querySelector(params?.selector);
3588
+ const html = await this.getCdp().querySelector(params?.selector);
3449
3589
  return { success: true, html };
3450
3590
  }
3591
+ case "input_wheel": {
3592
+ let { x, y, nx, ny, deltaX, deltaY } = params;
3593
+ if ((x === void 0 || y === void 0) && nx !== void 0 && ny !== void 0) {
3594
+ const viewport = await this.getCdp().evaluate(
3595
+ "JSON.stringify({ w: window.innerWidth, h: window.innerHeight })"
3596
+ );
3597
+ const { w, h } = JSON.parse(viewport);
3598
+ x = Math.round(nx * w);
3599
+ y = Math.round(ny * h);
3600
+ }
3601
+ await this.getCdp().send("Input.dispatchMouseEvent", {
3602
+ type: "mouseWheel",
3603
+ x: x || 0,
3604
+ y: y || 0,
3605
+ deltaX: deltaX || 0,
3606
+ deltaY: deltaY || 0
3607
+ });
3608
+ return { success: true };
3609
+ }
3451
3610
  default:
3452
3611
  return { success: false, error: `Unknown remote action: ${action}` };
3453
3612
  }
@@ -3456,8 +3615,8 @@ var init_daemon_commands = __esm({
3456
3615
  }
3457
3616
  }
3458
3617
  async handleDiscoverAgents(args) {
3459
- if (!this.ctx.cdp?.isConnected) return { success: false, error: "CDP not connected" };
3460
- const agents = await this.ctx.cdp.discoverAgentWebviews();
3618
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
3619
+ const agents = await this.getCdp().discoverAgentWebviews();
3461
3620
  return { success: true, agents };
3462
3621
  }
3463
3622
  // ─── 파일 직접 처리 ─────────────────────────
@@ -3514,7 +3673,7 @@ var init_daemon_commands = __esm({
3514
3673
  if (!this.ctx.localServer || this.ctx.localServer.extensionCount === 0) {
3515
3674
  return { success: false, error: "No extension connected" };
3516
3675
  }
3517
- return this.ctx.localServer.executeVscodeCommand(command, args);
3676
+ return this.ctx.localServer.executeVscodeCommand(command, args, this._currentIdeType);
3518
3677
  }
3519
3678
  // ─── 기타 ───────────────────────────────────
3520
3679
  async handleGetRecentWorkspaces(args) {
@@ -3533,66 +3692,67 @@ var init_daemon_commands = __esm({
3533
3692
  }
3534
3693
  }
3535
3694
  async handleRefreshScripts(args) {
3536
- if (!this.ctx.scriptLoader) return { success: false, error: "ScriptLoader not initialized" };
3537
- await this.ctx.scriptLoader.refresh();
3695
+ const loader = this.getScriptLoader();
3696
+ if (!loader) return { success: false, error: "ScriptLoader not initialized" };
3697
+ await loader.refresh();
3538
3698
  return { success: true };
3539
3699
  }
3540
3700
  // ─── Agent Stream 명령 ───────────────────────
3541
3701
  async handleAgentStreamSwitch(args) {
3542
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3702
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3543
3703
  const agentType = args?.agentType || args?.agent || null;
3544
- await this.agentStream.switchActiveAgent(this.ctx.cdp, agentType);
3704
+ await this.agentStream.switchActiveAgent(this.getCdp(), agentType);
3545
3705
  return { success: true, activeAgent: agentType };
3546
3706
  }
3547
3707
  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);
3708
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3709
+ const streams = await this.agentStream.collectAgentStreams(this.getCdp());
3550
3710
  return { success: true, streams };
3551
3711
  }
3552
3712
  async handleAgentStreamSend(args) {
3553
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3713
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3554
3714
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3555
3715
  const text = args?.text || args?.message;
3556
3716
  if (!agentType || !text) return { success: false, error: "agentType and text required" };
3557
- const ok = await this.agentStream.sendToAgent(this.ctx.cdp, agentType, text);
3717
+ const ok = await this.agentStream.sendToAgent(this.getCdp(), agentType, text);
3558
3718
  return { success: ok };
3559
3719
  }
3560
3720
  async handleAgentStreamResolve(args) {
3561
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3721
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3562
3722
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3563
3723
  const action = args?.action || "approve";
3564
3724
  if (!agentType) return { success: false, error: "agentType required" };
3565
- const ok = await this.agentStream.resolveAgentAction(this.ctx.cdp, agentType, action);
3725
+ const ok = await this.agentStream.resolveAgentAction(this.getCdp(), agentType, action);
3566
3726
  return { success: ok };
3567
3727
  }
3568
3728
  async handleAgentStreamNew(args) {
3569
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3729
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3570
3730
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3571
3731
  if (!agentType) return { success: false, error: "agentType required" };
3572
- const ok = await this.agentStream.newAgentSession(this.ctx.cdp, agentType);
3732
+ const ok = await this.agentStream.newAgentSession(this.getCdp(), agentType);
3573
3733
  return { success: ok };
3574
3734
  }
3575
3735
  async handleAgentStreamListChats(args) {
3576
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3736
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3577
3737
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3578
3738
  if (!agentType) return { success: false, error: "agentType required" };
3579
- const chats = await this.agentStream.listAgentChats(this.ctx.cdp, agentType);
3739
+ const chats = await this.agentStream.listAgentChats(this.getCdp(), agentType);
3580
3740
  return { success: true, chats };
3581
3741
  }
3582
3742
  async handleAgentStreamSwitchSession(args) {
3583
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3743
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3584
3744
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3585
3745
  const sessionId = args?.sessionId || args?.id;
3586
3746
  if (!agentType || !sessionId) return { success: false, error: "agentType and sessionId required" };
3587
- const ok = await this.agentStream.switchAgentSession(this.ctx.cdp, agentType, sessionId);
3747
+ const ok = await this.agentStream.switchAgentSession(this.getCdp(), agentType, sessionId);
3588
3748
  return { success: ok };
3589
3749
  }
3590
3750
  async handleAgentStreamFocus(args) {
3591
- if (!this.agentStream || !this.ctx.cdp) return { success: false, error: "AgentStream or CDP not available" };
3751
+ if (!this.agentStream || !this.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
3592
3752
  const agentType = args?.agentType || args?.agent || this.agentStream.activeAgentType;
3593
3753
  if (!agentType) return { success: false, error: "agentType required" };
3594
3754
  await this.agentStream.ensureAgentPanelOpen(agentType);
3595
- const ok = await this.agentStream.focusAgentEditor(this.ctx.cdp, agentType);
3755
+ const ok = await this.agentStream.focusAgentEditor(this.getCdp(), agentType);
3596
3756
  return { success: ok };
3597
3757
  }
3598
3758
  };
@@ -4193,10 +4353,10 @@ var init_adhdev_daemon = __esm({
4193
4353
  localServer = null;
4194
4354
  bridge = null;
4195
4355
  adapter = null;
4196
- cdpManager = null;
4356
+ cdpManagers = /* @__PURE__ */ new Map();
4197
4357
  cdpDiscoveryTimer = null;
4198
4358
  p2p = null;
4199
- scriptLoader = null;
4359
+ scriptLoaders = /* @__PURE__ */ new Map();
4200
4360
  commandHandler = null;
4201
4361
  screenshotTimer = null;
4202
4362
  agentStreamManager = null;
@@ -4208,7 +4368,9 @@ var init_adhdev_daemon = __esm({
4208
4368
  detectedIdes = [];
4209
4369
  localPort;
4210
4370
  ideType = "unknown";
4211
- _cachedAgentStreams = [];
4371
+ _cachedAgentStreamsMap = /* @__PURE__ */ new Map();
4372
+ _cachedActiveChatMap = /* @__PURE__ */ new Map();
4373
+ _cdpChatBusy = false;
4212
4374
  constructor() {
4213
4375
  this.localPort = DEFAULT_DAEMON_PORT;
4214
4376
  }
@@ -4271,18 +4433,27 @@ var init_adhdev_daemon = __esm({
4271
4433
  }
4272
4434
  this.detectedIdes = await detectIDEs();
4273
4435
  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
- });
4436
+ const serverUrl = options.serverUrl || config.serverUrl;
4437
+ for (const ideType of this.cdpManagers.keys()) {
4438
+ const loader = new DaemonScriptLoader(serverUrl, ideType);
4439
+ loader.setToken(config.connectionToken);
4440
+ await loader.start().catch((e) => {
4441
+ console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${ideType}: ${e.message}`));
4442
+ });
4443
+ this.scriptLoaders.set(ideType, loader);
4444
+ }
4445
+ if (this.scriptLoaders.size === 0) {
4446
+ const fallbackType = this.detectedIdes.find((ide) => ide.installed)?.id || "cursor";
4447
+ const loader = new DaemonScriptLoader(serverUrl, fallbackType);
4448
+ loader.setToken(config.connectionToken);
4449
+ await loader.start().catch((e) => {
4450
+ console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed: ${e.message}`));
4451
+ });
4452
+ this.scriptLoaders.set(fallbackType, loader);
4453
+ }
4283
4454
  this.commandHandler = new DaemonCommandHandler({
4284
- cdp: this.cdpManager,
4285
- scriptLoader: this.scriptLoader,
4455
+ cdpManagers: this.cdpManagers,
4456
+ scriptLoaders: this.scriptLoaders,
4286
4457
  localServer: this.localServer,
4287
4458
  ideType: this.ideType
4288
4459
  });
@@ -4293,16 +4464,7 @@ var init_adhdev_daemon = __esm({
4293
4464
  );
4294
4465
  this.agentStreamManager.setLocalServer(this.localServer);
4295
4466
  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
- }
4467
+ this.startAgentStreamPolling();
4306
4468
  if (options.cliType) {
4307
4469
  const cliInfo = await detectCLI(options.cliType);
4308
4470
  if (cliInfo) {
@@ -4353,12 +4515,26 @@ var init_adhdev_daemon = __esm({
4353
4515
  return { id: req.id, success: result.success, entries: result.files, error: result.error };
4354
4516
  }
4355
4517
  });
4518
+ let ssDebugCount = 0;
4356
4519
  this.screenshotTimer = setInterval(async () => {
4357
- if (!this.p2p?.screenshotActive || !this.cdpManager?.isConnected) return;
4520
+ const active = this.p2p?.screenshotActive;
4521
+ const ssIdeType = this.p2p?.screenshotIdeType;
4522
+ const cdp = ssIdeType ? this.getCdpFor(ssIdeType) : this.getAnyCdp();
4523
+ if (!active || !cdp) return;
4524
+ ssDebugCount++;
4525
+ if (ssDebugCount <= 3 || ssDebugCount % 25 === 0) {
4526
+ console.log(`[SS] Capturing screenshot... (tick ${ssDebugCount}, active=${active}, cdp=true)`);
4527
+ }
4358
4528
  try {
4359
- const buf = await this.cdpManager.captureScreenshot();
4360
- if (buf) this.p2p.sendScreenshot(buf.toString("base64"));
4361
- } catch {
4529
+ const buf = await cdp.captureScreenshot();
4530
+ if (buf) {
4531
+ const sent = this.p2p.sendScreenshot(buf.toString("base64"));
4532
+ if (ssDebugCount <= 3) console.log(`[SS] Screenshot sent: ${buf.length} bytes, delivered=${sent}`);
4533
+ } else {
4534
+ if (ssDebugCount <= 5) console.log(`[SS] captureScreenshot returned null`);
4535
+ }
4536
+ } catch (e) {
4537
+ if (ssDebugCount <= 5) console.log(`[SS] Screenshot error: ${e?.message}`);
4362
4538
  }
4363
4539
  }, 200);
4364
4540
  } else {
@@ -4391,9 +4567,10 @@ var init_adhdev_daemon = __esm({
4391
4567
  console.log(import_chalk2.default.bold(" \u{1F309} ADHDev Daemon"));
4392
4568
  console.log(` ${import_chalk2.default.bold("Local:")} ws://127.0.0.1:${this.localPort}/ipc`);
4393
4569
  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"}`);
4570
+ const cdpStatus = this.cdpManagers.size > 0 ? `\u2705 ${[...this.cdpManagers.entries()].map(([k, m]) => `${k}:${m.getPort()}`).join(", ")}` : "\u274C not connected";
4571
+ console.log(` ${import_chalk2.default.bold("CDP:")} ${cdpStatus}`);
4395
4572
  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"}`);
4573
+ console.log(` ${import_chalk2.default.bold("Scripts:")} ${this.scriptLoaders.size > 0 ? `\u2705 ${[...this.scriptLoaders.keys()].join(", ")}` : "\u274C not loaded"}`);
4397
4574
  if (options.cliType) {
4398
4575
  console.log(` ${import_chalk2.default.bold("CLI:")} ${options.cliType}`);
4399
4576
  console.log(` ${import_chalk2.default.bold("Dir:")} ${options.workingDir || process.cwd()}`);
@@ -4405,19 +4582,6 @@ var init_adhdev_daemon = __esm({
4405
4582
  // ─── 서버 명령 핸들러 ───────────────────────────
4406
4583
  registerServerHandlers() {
4407
4584
  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
4585
  this.bridge.on("command", async (msg) => {
4422
4586
  const cmd = msg.payload.command;
4423
4587
  const args = msg.payload.args;
@@ -4455,7 +4619,38 @@ var init_adhdev_daemon = __esm({
4455
4619
  "file_list",
4456
4620
  "file_list_browse",
4457
4621
  "terminal_exec",
4458
- "refresh_scripts"
4622
+ "refresh_scripts",
4623
+ // CLI/daemon-local commands (launch, restart, detect)
4624
+ "launch_ide",
4625
+ "detect_ides",
4626
+ "restart_session",
4627
+ "switch_cli",
4628
+ "change_dir",
4629
+ "exec_command",
4630
+ // Extension-delegated commands (must be registered to receive WS messages)
4631
+ "vscode_command_exec",
4632
+ "execute_vscode_command",
4633
+ "get_open_editors",
4634
+ "open_tab",
4635
+ "close_tab",
4636
+ "open_folder",
4637
+ "open_folder_picker",
4638
+ "open_recent",
4639
+ "get_commands",
4640
+ "get_recent_workspaces",
4641
+ "open_panel",
4642
+ "open_file",
4643
+ "create_terminal",
4644
+ "close_terminal",
4645
+ // Agent stream commands
4646
+ "agent_stream_switch",
4647
+ "agent_stream_read",
4648
+ "agent_stream_send",
4649
+ "agent_stream_resolve",
4650
+ "agent_stream_new",
4651
+ "agent_stream_list_chats",
4652
+ "agent_stream_switch_session",
4653
+ "agent_stream_focus"
4459
4654
  ];
4460
4655
  for (const cmdType of directCdpCommands) {
4461
4656
  this.bridge.on(cmdType, async (msg) => {
@@ -4506,7 +4701,33 @@ var init_adhdev_daemon = __esm({
4506
4701
  return;
4507
4702
  }
4508
4703
  case "launch_ide": {
4509
- const result = await launchWithCdp(args);
4704
+ const launchArgs = { ...args, ideId: args.ideId || args.ideType };
4705
+ const ideKey = launchArgs.ideId;
4706
+ console.log(`[launch_ide] target=${ideKey || "auto"}`);
4707
+ const result = await launchWithCdp(launchArgs);
4708
+ if (result.success && result.port && result.ideId && !this.cdpManagers.has(result.ideId)) {
4709
+ console.log(import_chalk2.default.cyan(`[launch_ide] Connecting CDP for ${result.ideId} on port ${result.port}...`));
4710
+ const manager = new DaemonCdpManager(result.port, (msg2) => {
4711
+ console.log(import_chalk2.default.gray(msg2));
4712
+ }, true);
4713
+ const connected = await manager.connect();
4714
+ if (connected) {
4715
+ this.cdpManagers.set(result.ideId, manager);
4716
+ console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${result.ideId} (port ${result.port})`));
4717
+ console.log(import_chalk2.default.green(` \u{1F4E1} CDP: ${this.cdpManagers.size} IDE(s) connected`));
4718
+ if (!this.scriptLoaders.has(result.ideId)) {
4719
+ const config = loadConfig();
4720
+ const serverUrl = config.serverUrl || "https://api.adhf.dev";
4721
+ const loader = new DaemonScriptLoader(serverUrl, result.ideId);
4722
+ loader.setToken(config.connectionToken || "");
4723
+ await loader.start().catch((e) => {
4724
+ console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${result.ideId}: ${e?.message}`));
4725
+ });
4726
+ this.scriptLoaders.set(result.ideId, loader);
4727
+ }
4728
+ }
4729
+ }
4730
+ this.startAgentStreamPolling();
4510
4731
  this.sendResult(msg, result.success, result);
4511
4732
  return;
4512
4733
  }
@@ -4570,14 +4791,43 @@ var init_adhdev_daemon = __esm({
4570
4791
  const isProcessing = this.adapter?.isProcessing();
4571
4792
  const interval = isProcessing ? 2e3 : 15e3;
4572
4793
  this.statusTimer = setTimeout(() => {
4573
- this.sendUnifiedStatusReport();
4794
+ this.sendUnifiedStatusReport().catch(() => {
4795
+ });
4574
4796
  scheduleNext();
4575
4797
  }, interval);
4576
4798
  };
4577
4799
  scheduleNext();
4578
4800
  }
4579
- sendUnifiedStatusReport() {
4801
+ async sendUnifiedStatusReport() {
4580
4802
  if (!this.bridge?.isConnected()) return;
4803
+ if (this.cdpManagers.size > 0 && this.scriptLoaders.size > 0 && !this._cdpChatBusy) {
4804
+ this._cdpChatBusy = true;
4805
+ for (const [ideType, cdp] of this.cdpManagers) {
4806
+ if (!cdp.isConnected) continue;
4807
+ const loader = this.scriptLoaders.get(ideType);
4808
+ if (!loader) continue;
4809
+ const readChatScript = loader.get("read_chat");
4810
+ if (!readChatScript) continue;
4811
+ try {
4812
+ const raw = await cdp.evaluate(readChatScript, 3e4);
4813
+ if (raw && typeof raw === "object") {
4814
+ let { activeModal } = raw;
4815
+ if (activeModal) {
4816
+ const w = activeModal.width ?? Infinity;
4817
+ const h = activeModal.height ?? Infinity;
4818
+ if (w < 80 || h < 40) activeModal = void 0;
4819
+ else activeModal = {
4820
+ message: activeModal.message?.slice(0, 300) ?? "",
4821
+ buttons: (activeModal.buttons ?? []).filter((t) => t.length < 30)
4822
+ };
4823
+ }
4824
+ this._cachedActiveChatMap.set(ideType, { ...raw, activeModal });
4825
+ }
4826
+ } catch {
4827
+ }
4828
+ }
4829
+ this._cdpChatBusy = false;
4830
+ }
4581
4831
  const now = Date.now();
4582
4832
  const perExtData = this.localServer?.getPerExtensionData() || [];
4583
4833
  const extSummary = this.localServer?.getLatestExtensionData() || {
@@ -4587,12 +4837,13 @@ var init_adhdev_daemon = __esm({
4587
4837
  aiAgents: [],
4588
4838
  connectedIdes: []
4589
4839
  };
4590
- const cdpAgentStreams = this._cachedAgentStreams || [];
4591
4840
  const managedIdes = perExtData.map((ext) => {
4592
- const isMainCdpIde = ext.ideType.toLowerCase() === this.ideType.toLowerCase();
4841
+ const ideKey = ext.ideType.toLowerCase();
4842
+ const isMainCdpIde = this.cdpManagers.has(ideKey);
4843
+ const cdpStreamsForIde = this._cachedAgentStreamsMap.get(ideKey) || [];
4593
4844
  const ideAgentStreams = [
4594
4845
  ...ext.agentStreams,
4595
- ...isMainCdpIde ? cdpAgentStreams : []
4846
+ ...isMainCdpIde ? cdpStreamsForIde : []
4596
4847
  ];
4597
4848
  return {
4598
4849
  ideType: ext.ideType,
@@ -4602,10 +4853,10 @@ var init_adhdev_daemon = __esm({
4602
4853
  activeFile: ext.activeFile,
4603
4854
  terminals: ext.terminals,
4604
4855
  aiAgents: ext.aiAgents,
4605
- activeChat: ext.activeChat,
4856
+ activeChat: isMainCdpIde && this._cachedActiveChatMap.has(ideKey) ? this._cachedActiveChatMap.get(ideKey) : ext.activeChat,
4606
4857
  chats: ext.chats,
4607
4858
  agentStreams: ideAgentStreams,
4608
- cdpConnected: isMainCdpIde ? this.cdpManager?.isConnected || false : false
4859
+ cdpConnected: isMainCdpIde ? this.cdpManagers.get(ext.ideType.toLowerCase())?.isConnected || false : false
4609
4860
  };
4610
4861
  });
4611
4862
  const managedClis = [];
@@ -4673,8 +4924,12 @@ var init_adhdev_daemon = __esm({
4673
4924
  // 관리 중인 IDE/CLI (계층 데이터)
4674
4925
  managedIdes,
4675
4926
  managedClis,
4676
- // IDE 감지 결과 (설치된 IDE 목록)
4677
- detectedIdes: this.detectedIdes,
4927
+ // IDE 감지 결과 (설치된 IDE 목록) — 프론트엔드 호환 매핑 (id→type)
4928
+ detectedIdes: this.detectedIdes.map((ide) => ({
4929
+ ...ide,
4930
+ type: ide.id
4931
+ // 프론트엔드 MachineDetail.tsx는 'type' 필드 사용
4932
+ })),
4678
4933
  // P2P 상태
4679
4934
  p2p: {
4680
4935
  available: this.p2p?.isAvailable || false,
@@ -4682,8 +4937,8 @@ var init_adhdev_daemon = __esm({
4682
4937
  peers: this.p2p?.connectedPeerCount || 0,
4683
4938
  screenshotActive: this.p2p?.screenshotActive || false
4684
4939
  },
4685
- cdpConnected: this.cdpManager?.isConnected || false,
4686
- scriptLoaderReady: !!this.scriptLoader,
4940
+ cdpConnected: this.getAnyCdp() !== null,
4941
+ scriptLoaderReady: this.scriptLoaders.size > 0,
4687
4942
  timestamp: now,
4688
4943
  // ─── Legacy compat (서버 기존 로직용, 점진적 제거 예정) ───
4689
4944
  activeFile: extSummary.activeFile,
@@ -4703,7 +4958,7 @@ var init_adhdev_daemon = __esm({
4703
4958
  messages: c.activeChat?.messages || [],
4704
4959
  inputContent: ""
4705
4960
  })),
4706
- ...cdpAgentStreams
4961
+ ...[...this._cachedAgentStreamsMap.values()].flat()
4707
4962
  ],
4708
4963
  connectedExtensions: extSummary.connectedIdes,
4709
4964
  system: {
@@ -4740,13 +4995,16 @@ var init_adhdev_daemon = __esm({
4740
4995
  if (this.cdpDiscoveryTimer) clearInterval(this.cdpDiscoveryTimer);
4741
4996
  if (this.screenshotTimer) clearInterval(this.screenshotTimer);
4742
4997
  if (this.agentStreamTimer) clearInterval(this.agentStreamTimer);
4743
- if (this.agentStreamManager && this.cdpManager) {
4744
- await this.agentStreamManager.dispose(this.cdpManager).catch(() => {
4998
+ const anyCdpStop = this.getAnyCdp();
4999
+ if (this.agentStreamManager && anyCdpStop) {
5000
+ await this.agentStreamManager.dispose(anyCdpStop).catch(() => {
4745
5001
  });
4746
5002
  }
4747
5003
  this.p2p?.disconnect();
4748
- this.scriptLoader?.stop();
4749
- this.cdpManager?.disconnect();
5004
+ for (const loader of this.scriptLoaders.values()) loader.stop();
5005
+ this.scriptLoaders.clear();
5006
+ for (const m of this.cdpManagers.values()) m.disconnect();
5007
+ this.cdpManagers.clear();
4750
5008
  this.localServer?.stop();
4751
5009
  this.bridge?.disconnect();
4752
5010
  removeDaemonPid();
@@ -4754,6 +5012,33 @@ var init_adhdev_daemon = __esm({
4754
5012
  process.exit(0);
4755
5013
  }
4756
5014
  // ─── CDP 관리 ───────────────────────────────────
5015
+ /** 첫 번째 연결된 CDP 매니저 반환 */
5016
+ getAnyCdp() {
5017
+ for (const m of this.cdpManagers.values()) {
5018
+ if (m.isConnected) return m;
5019
+ }
5020
+ return null;
5021
+ }
5022
+ /** 특정 IDE의 CDP 매니저 반환 */
5023
+ getCdpFor(ideType) {
5024
+ return this.cdpManagers.get(ideType.toLowerCase()) || null;
5025
+ }
5026
+ /** Agent stream polling 시작 (idempotent — 이미 시작됐으면 무시) */
5027
+ startAgentStreamPolling() {
5028
+ if (this.agentStreamTimer) return;
5029
+ this.agentStreamTimer = setInterval(async () => {
5030
+ if (!this.agentStreamManager || this.cdpManagers.size === 0) return;
5031
+ for (const [ideType, cdp] of this.cdpManagers) {
5032
+ if (!cdp.isConnected) continue;
5033
+ try {
5034
+ await this.agentStreamManager.syncAgentSessions(cdp);
5035
+ const streams = await this.agentStreamManager.collectAgentStreams(cdp);
5036
+ this._cachedAgentStreamsMap.set(ideType, streams);
5037
+ } catch {
5038
+ }
5039
+ }
5040
+ }, 5e3);
5041
+ }
4757
5042
  async initCdp() {
4758
5043
  const cdpPortMap = {
4759
5044
  cursor: 9333,
@@ -4763,7 +5048,7 @@ var init_adhdev_daemon = __esm({
4763
5048
  };
4764
5049
  const portsToTry = [];
4765
5050
  for (const ide of this.detectedIdes) {
4766
- const ideKey = ide.type || ide.name?.toLowerCase();
5051
+ const ideKey = ide.id || ide.name?.toLowerCase();
4767
5052
  const port = cdpPortMap[ideKey];
4768
5053
  if (port) portsToTry.push({ port, ide: ideKey });
4769
5054
  }
@@ -4775,22 +5060,31 @@ var init_adhdev_daemon = __esm({
4775
5060
  for (const { port, ide } of portsToTry) {
4776
5061
  const manager = new DaemonCdpManager(port, (msg) => {
4777
5062
  console.log(import_chalk2.default.gray(msg));
4778
- });
5063
+ }, true);
4779
5064
  const connected = await manager.connect();
4780
5065
  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;
5066
+ const actualPort = manager.getPort();
5067
+ const actualIde = Object.entries(cdpPortMap).find(([, p]) => p === actualPort)?.[0] || ide;
5068
+ this.cdpManagers.set(actualIde, manager);
5069
+ console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${actualIde} (port ${actualPort})`));
5070
+ if (this.ideType === "unknown") {
5071
+ this.ideType = actualIde;
5072
+ }
4790
5073
  }
4791
5074
  }
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`));
5075
+ if (this.cdpManagers.size > 0) {
5076
+ console.log(import_chalk2.default.green(` \u{1F4E1} CDP: ${this.cdpManagers.size} IDE(s) connected`));
5077
+ this.cdpDiscoveryTimer = setInterval(async () => {
5078
+ for (const m of this.cdpManagers.values()) {
5079
+ if (m.isConnected) {
5080
+ await m.discoverAgentWebviews();
5081
+ }
5082
+ }
5083
+ }, 3e4);
5084
+ } else {
5085
+ console.log(import_chalk2.default.yellow(` \u26A0 CDP not available \u2014 tried ports: ${portsToTry.map((p) => `${p.ide}:${p.port}`).join(", ")}`));
5086
+ console.log(import_chalk2.default.yellow(` IDE may need relaunch with --remote-debugging-port`));
5087
+ }
4794
5088
  }
4795
5089
  };
4796
5090
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adhdev",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "ADHDev CLI — Detect, install and configure your IDE + AI agent extensions",
5
5
  "main": "dist/index.js",
6
6
  "bin": {