adhdev 0.1.47 → 0.1.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -262,7 +262,8 @@ var init_config = __esm({
262
262
  recentCliWorkspaces: [],
263
263
  machineNickname: null,
264
264
  cliHistory: [],
265
- providerSettings: {}
265
+ providerSettings: {},
266
+ ideSettings: {}
266
267
  };
267
268
  }
268
269
  });
@@ -336,6 +337,54 @@ var init_provider_loader = __esm({
336
337
  getAll() {
337
338
  return [...this.providers.values()];
338
339
  }
340
+ /**
341
+ * Provider가 활성화되어 있는지 확인 (per-IDE)
342
+ * ideSettings[ideType].extensions[type].enabled를 참조.
343
+ * 설정이 없으면 기본 false (비활성) — 사용자가 명시적으로 켜야 활성화.
344
+ * ideType 없이 호출하면 항상 true.
345
+ */
346
+ isEnabled(type, ideType) {
347
+ if (!ideType) return true;
348
+ try {
349
+ const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
350
+ const config = loadConfig2();
351
+ const val = config.ideSettings?.[ideType]?.extensions?.[type]?.enabled;
352
+ return val === true;
353
+ } catch {
354
+ return false;
355
+ }
356
+ }
357
+ /**
358
+ * IDE별 extension enabled 설정 저장
359
+ */
360
+ setIdeExtensionEnabled(ideType, extensionType, enabled) {
361
+ try {
362
+ const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = (init_config(), __toCommonJS(config_exports));
363
+ const config = loadConfig2();
364
+ if (!config.ideSettings) config.ideSettings = {};
365
+ if (!config.ideSettings[ideType]) config.ideSettings[ideType] = {};
366
+ if (!config.ideSettings[ideType].extensions) config.ideSettings[ideType].extensions = {};
367
+ config.ideSettings[ideType].extensions[extensionType] = { enabled };
368
+ saveConfig2(config);
369
+ this.log(`IDE extension setting: ${ideType}.${extensionType}.enabled = ${enabled}`);
370
+ return true;
371
+ } catch (e) {
372
+ this.log(`Failed to save IDE extension setting: ${e.message}`);
373
+ return false;
374
+ }
375
+ }
376
+ /**
377
+ * 카테고리별 활성화된 프로바이더만 반환 (per-IDE)
378
+ */
379
+ getEnabledByCategory(cat, ideType) {
380
+ return this.getByCategory(cat).filter((p) => this.isEnabled(p.type, ideType));
381
+ }
382
+ /**
383
+ * Extension 카테고리 중 활성화되고 extensionIdPattern이 있는 것만 (per-IDE)
384
+ */
385
+ getEnabledExtensionProviders(ideType) {
386
+ return this.getExtensionProviders().filter((p) => this.isEnabled(p.type, ideType));
387
+ }
339
388
  /**
340
389
  * IDE 카테고리의 CDP 포트 맵 반환
341
390
  * launch.ts, adhdev-daemon.ts에서 사용
@@ -857,7 +906,7 @@ var init_local_server = __esm({
857
906
  * 로컬 WS 서버 시작
858
907
  */
859
908
  async start() {
860
- return new Promise((resolve5, reject) => {
909
+ return new Promise((resolve6, reject) => {
861
910
  try {
862
911
  this.wss = new import_ws2.WebSocketServer({
863
912
  port: this.port,
@@ -867,7 +916,7 @@ var init_local_server = __esm({
867
916
  });
868
917
  this.wss.on("listening", () => {
869
918
  console.log(`[LocalServer] Listening on ws://127.0.0.1:${this.port}${DAEMON_WS_PATH}`);
870
- resolve5();
919
+ resolve6();
871
920
  });
872
921
  this.wss.on("connection", (ws) => {
873
922
  this.handleConnection(ws);
@@ -1046,14 +1095,14 @@ var init_local_server = __esm({
1046
1095
  return { success: false, error: "No extension connected" };
1047
1096
  }
1048
1097
  const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1049
- return new Promise((resolve5) => {
1098
+ return new Promise((resolve6) => {
1050
1099
  const timeout = setTimeout(() => {
1051
1100
  this.commandCallbacks.delete(requestId);
1052
- resolve5({ success: false, error: "Command execution timeout (10s)" });
1101
+ resolve6({ success: false, error: "Command execution timeout (10s)" });
1053
1102
  }, 1e4);
1054
1103
  this.commandCallbacks.set(requestId, (result) => {
1055
1104
  clearTimeout(timeout);
1056
- resolve5(result);
1105
+ resolve6(result);
1057
1106
  });
1058
1107
  this.sendToExtension(targetWs, {
1059
1108
  type: "daemon:execute_vscode",
@@ -1177,17 +1226,17 @@ async function findFreePort(ports) {
1177
1226
  throw new Error("No free port found");
1178
1227
  }
1179
1228
  function checkPortFree(port) {
1180
- return new Promise((resolve5) => {
1229
+ return new Promise((resolve6) => {
1181
1230
  const server = net.createServer();
1182
1231
  server.unref();
1183
- server.on("error", () => resolve5(false));
1232
+ server.on("error", () => resolve6(false));
1184
1233
  server.listen(port, "127.0.0.1", () => {
1185
- server.close(() => resolve5(true));
1234
+ server.close(() => resolve6(true));
1186
1235
  });
1187
1236
  });
1188
1237
  }
1189
1238
  async function isCdpActive(port) {
1190
- return new Promise((resolve5) => {
1239
+ return new Promise((resolve6) => {
1191
1240
  const req = require("http").get(`http://127.0.0.1:${port}/json/version`, {
1192
1241
  timeout: 2e3
1193
1242
  }, (res) => {
@@ -1196,16 +1245,16 @@ async function isCdpActive(port) {
1196
1245
  res.on("end", () => {
1197
1246
  try {
1198
1247
  const info = JSON.parse(data);
1199
- resolve5(!!info["WebKit-Version"] || !!info["Browser"]);
1248
+ resolve6(!!info["WebKit-Version"] || !!info["Browser"]);
1200
1249
  } catch {
1201
- resolve5(false);
1250
+ resolve6(false);
1202
1251
  }
1203
1252
  });
1204
1253
  });
1205
- req.on("error", () => resolve5(false));
1254
+ req.on("error", () => resolve6(false));
1206
1255
  req.on("timeout", () => {
1207
1256
  req.destroy();
1208
- resolve5(false);
1257
+ resolve6(false);
1209
1258
  });
1210
1259
  });
1211
1260
  }
@@ -1530,12 +1579,53 @@ var init_daemon_cdp = __esm({
1530
1579
  logFn;
1531
1580
  extensionProviders = [];
1532
1581
  _lastDiscoverSig = "";
1533
- constructor(port = 9333, logFn) {
1582
+ _targetId = null;
1583
+ // 특정 targetId로 연결 (multi-window 지원)
1584
+ _pageTitle = "";
1585
+ // 연결된 페이지 제목
1586
+ constructor(port = 9333, logFn, targetId) {
1534
1587
  this.port = port;
1588
+ this._targetId = targetId || null;
1535
1589
  this.logFn = logFn || ((msg) => {
1536
1590
  console.log(msg);
1537
1591
  });
1538
1592
  }
1593
+ /** 연결된 페이지의 제목 (workspace 이름 포함) */
1594
+ get pageTitle() {
1595
+ return this._pageTitle;
1596
+ }
1597
+ /**
1598
+ * 포트에 있는 모든 workbench 페이지 목록 조회 (static)
1599
+ * 같은 포트에 여러 IDE 창이 열려 있으면 여러 개 반환
1600
+ */
1601
+ static listAllTargets(port) {
1602
+ return new Promise((resolve6) => {
1603
+ const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
1604
+ let data = "";
1605
+ res.on("data", (chunk) => data += chunk.toString());
1606
+ res.on("end", () => {
1607
+ try {
1608
+ const targets = JSON.parse(data);
1609
+ const pages = targets.filter(
1610
+ (t) => t.type === "page" && t.webSocketDebuggerUrl
1611
+ );
1612
+ const isNonMain = (title) => !title || /extension-output|ADHDev CDP|Debug Console|Output\s*$|Launchpad/i.test(title);
1613
+ const mainPages = pages.filter(
1614
+ (t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
1615
+ );
1616
+ resolve6(mainPages.length > 0 ? mainPages : pages.filter((t) => !isNonMain(t.title || "")));
1617
+ } catch {
1618
+ resolve6([]);
1619
+ }
1620
+ });
1621
+ });
1622
+ req.on("error", () => resolve6([]));
1623
+ req.setTimeout(2e3, () => {
1624
+ req.destroy();
1625
+ resolve6([]);
1626
+ });
1627
+ });
1628
+ }
1539
1629
  setPort(port) {
1540
1630
  this.port = port;
1541
1631
  this.log(`[CDP] Port changed to ${port}`);
@@ -1572,7 +1662,7 @@ var init_daemon_cdp = __esm({
1572
1662
  }
1573
1663
  }
1574
1664
  findTargetOnPort(port) {
1575
- return new Promise((resolve5) => {
1665
+ return new Promise((resolve6) => {
1576
1666
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
1577
1667
  let data = "";
1578
1668
  res.on("data", (chunk) => data += chunk.toString());
@@ -1583,23 +1673,35 @@ var init_daemon_cdp = __esm({
1583
1673
  (t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
1584
1674
  );
1585
1675
  if (pages.length === 0) {
1586
- resolve5(targets.find((t) => t.webSocketDebuggerUrl) || null);
1676
+ resolve6(targets.find((t) => t.webSocketDebuggerUrl) || null);
1587
1677
  return;
1588
1678
  }
1589
1679
  const isNonMain = (title) => !title || /extension-output|ADHDev CDP|Debug Console|Output\s*$|Launchpad/i.test(title);
1590
1680
  const mainPages = pages.filter((t) => !isNonMain(t.title || ""));
1591
1681
  const list = mainPages.length > 0 ? mainPages : pages;
1592
1682
  this.log(`[CDP] pages(${list.length}): ${list.map((t) => `"${t.title}"`).join(", ")}`);
1593
- resolve5(list[0]);
1683
+ if (this._targetId) {
1684
+ const specific = list.find((t) => t.id === this._targetId);
1685
+ if (specific) {
1686
+ this._pageTitle = specific.title || "";
1687
+ resolve6(specific);
1688
+ } else {
1689
+ this.log(`[CDP] Target ${this._targetId} not found in page list`);
1690
+ resolve6(null);
1691
+ }
1692
+ return;
1693
+ }
1694
+ this._pageTitle = list[0]?.title || "";
1695
+ resolve6(list[0]);
1594
1696
  } catch {
1595
- resolve5(null);
1697
+ resolve6(null);
1596
1698
  }
1597
1699
  });
1598
1700
  });
1599
- req.on("error", () => resolve5(null));
1701
+ req.on("error", () => resolve6(null));
1600
1702
  req.setTimeout(2e3, () => {
1601
1703
  req.destroy();
1602
- resolve5(null);
1704
+ resolve6(null);
1603
1705
  });
1604
1706
  });
1605
1707
  }
@@ -1610,7 +1712,7 @@ var init_daemon_cdp = __esm({
1610
1712
  this.extensionProviders = providers;
1611
1713
  }
1612
1714
  connectToTarget(wsUrl) {
1613
- return new Promise((resolve5) => {
1715
+ return new Promise((resolve6) => {
1614
1716
  this.ws = new import_ws3.default(wsUrl);
1615
1717
  this.ws.on("open", async () => {
1616
1718
  this._connected = true;
@@ -1620,17 +1722,17 @@ var init_daemon_cdp = __esm({
1620
1722
  }
1621
1723
  this.connectBrowserWs().catch(() => {
1622
1724
  });
1623
- resolve5(true);
1725
+ resolve6(true);
1624
1726
  });
1625
1727
  this.ws.on("message", (data) => {
1626
1728
  try {
1627
1729
  const msg = JSON.parse(data.toString());
1628
1730
  if (msg.id && this.pending.has(msg.id)) {
1629
- const { resolve: resolve6, reject } = this.pending.get(msg.id);
1731
+ const { resolve: resolve7, reject } = this.pending.get(msg.id);
1630
1732
  this.pending.delete(msg.id);
1631
1733
  this.failureCount = 0;
1632
1734
  if (msg.error) reject(new Error(msg.error.message));
1633
- else resolve6(msg.result);
1735
+ else resolve7(msg.result);
1634
1736
  } else if (msg.method === "Runtime.executionContextCreated") {
1635
1737
  this.contexts.add(msg.params.context.id);
1636
1738
  } else if (msg.method === "Runtime.executionContextDestroyed") {
@@ -1653,7 +1755,7 @@ var init_daemon_cdp = __esm({
1653
1755
  this.ws.on("error", (err) => {
1654
1756
  this.log(`[CDP] WebSocket error: ${err.message}`);
1655
1757
  this._connected = false;
1656
- resolve5(false);
1758
+ resolve6(false);
1657
1759
  });
1658
1760
  });
1659
1761
  }
@@ -1667,7 +1769,7 @@ var init_daemon_cdp = __esm({
1667
1769
  return;
1668
1770
  }
1669
1771
  this.log(`[CDP] Connecting browser WS for target discovery...`);
1670
- await new Promise((resolve5, reject) => {
1772
+ await new Promise((resolve6, reject) => {
1671
1773
  this.browserWs = new import_ws3.default(browserWsUrl);
1672
1774
  this.browserWs.on("open", async () => {
1673
1775
  this._browserConnected = true;
@@ -1677,16 +1779,16 @@ var init_daemon_cdp = __esm({
1677
1779
  } catch (e) {
1678
1780
  this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
1679
1781
  }
1680
- resolve5();
1782
+ resolve6();
1681
1783
  });
1682
1784
  this.browserWs.on("message", (data) => {
1683
1785
  try {
1684
1786
  const msg = JSON.parse(data.toString());
1685
1787
  if (msg.id && this.browserPending.has(msg.id)) {
1686
- const { resolve: resolve6, reject: reject2 } = this.browserPending.get(msg.id);
1788
+ const { resolve: resolve7, reject: reject2 } = this.browserPending.get(msg.id);
1687
1789
  this.browserPending.delete(msg.id);
1688
1790
  if (msg.error) reject2(new Error(msg.error.message));
1689
- else resolve6(msg.result);
1791
+ else resolve7(msg.result);
1690
1792
  }
1691
1793
  } catch {
1692
1794
  }
@@ -1706,31 +1808,31 @@ var init_daemon_cdp = __esm({
1706
1808
  }
1707
1809
  }
1708
1810
  getBrowserWsUrl() {
1709
- return new Promise((resolve5) => {
1811
+ return new Promise((resolve6) => {
1710
1812
  const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
1711
1813
  let data = "";
1712
1814
  res.on("data", (chunk) => data += chunk.toString());
1713
1815
  res.on("end", () => {
1714
1816
  try {
1715
1817
  const info = JSON.parse(data);
1716
- resolve5(info.webSocketDebuggerUrl || null);
1818
+ resolve6(info.webSocketDebuggerUrl || null);
1717
1819
  } catch {
1718
- resolve5(null);
1820
+ resolve6(null);
1719
1821
  }
1720
1822
  });
1721
1823
  });
1722
- req.on("error", () => resolve5(null));
1824
+ req.on("error", () => resolve6(null));
1723
1825
  req.setTimeout(3e3, () => {
1724
1826
  req.destroy();
1725
- resolve5(null);
1827
+ resolve6(null);
1726
1828
  });
1727
1829
  });
1728
1830
  }
1729
1831
  sendBrowser(method, params = {}, timeoutMs = 15e3) {
1730
- return new Promise((resolve5, reject) => {
1832
+ return new Promise((resolve6, reject) => {
1731
1833
  if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
1732
1834
  const id = this.browserMsgId++;
1733
- this.browserPending.set(id, { resolve: resolve5, reject });
1835
+ this.browserPending.set(id, { resolve: resolve6, reject });
1734
1836
  this.browserWs.send(JSON.stringify({ id, method, params }));
1735
1837
  setTimeout(() => {
1736
1838
  if (this.browserPending.has(id)) {
@@ -1770,11 +1872,11 @@ var init_daemon_cdp = __esm({
1770
1872
  }
1771
1873
  // ─── CDP Protocol ────────────────────────────────────────
1772
1874
  sendInternal(method, params = {}, timeoutMs = 15e3) {
1773
- return new Promise((resolve5, reject) => {
1875
+ return new Promise((resolve6, reject) => {
1774
1876
  if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
1775
1877
  if (this.ws.readyState !== import_ws3.default.OPEN) return reject(new Error("WebSocket not open"));
1776
1878
  const id = this.msgId++;
1777
- this.pending.set(id, { resolve: resolve5, reject });
1879
+ this.pending.set(id, { resolve: resolve6, reject });
1778
1880
  this.ws.send(JSON.stringify({ id, method, params }));
1779
1881
  setTimeout(() => {
1780
1882
  if (this.pending.has(id)) {
@@ -1978,14 +2080,14 @@ var init_daemon_cdp = __esm({
1978
2080
  if (!ws || ws.readyState !== import_ws3.default.OPEN) {
1979
2081
  throw new Error("CDP not connected");
1980
2082
  }
1981
- return new Promise((resolve5, reject) => {
2083
+ return new Promise((resolve6, reject) => {
1982
2084
  const id = getNextId();
1983
2085
  pendingMap.set(id, {
1984
2086
  resolve: (result) => {
1985
2087
  if (result?.result?.subtype === "error") {
1986
2088
  reject(new Error(result.result.description));
1987
2089
  } else {
1988
- resolve5(result?.result?.value);
2090
+ resolve6(result?.result?.value);
1989
2091
  }
1990
2092
  },
1991
2093
  reject
@@ -2462,13 +2564,13 @@ var init_daemon_p2p = __esm({
2462
2564
  } catch {
2463
2565
  }
2464
2566
  const http3 = require("https");
2465
- const data = await new Promise((resolve5, reject) => {
2567
+ const data = await new Promise((resolve6, reject) => {
2466
2568
  const req = http3.get(`${serverUrl}/api/v1/turn/credentials`, {
2467
2569
  headers: { "Authorization": `Bearer ${token}` }
2468
2570
  }, (res) => {
2469
2571
  let d = "";
2470
2572
  res.on("data", (c) => d += c);
2471
- res.on("end", () => resolve5(d));
2573
+ res.on("end", () => resolve6(d));
2472
2574
  });
2473
2575
  req.on("error", reject);
2474
2576
  req.setTimeout(5e3, () => {
@@ -2841,7 +2943,11 @@ var init_daemon_p2p = __esm({
2841
2943
  return;
2842
2944
  }
2843
2945
  if (type === "p2p_answer") {
2844
- const peer = peerId ? this.peers.get(peerId) : this.getFirstPeer();
2946
+ if (!peerId) {
2947
+ log("p2p_answer without peerId \u2014 ignoring (multi-peer requires explicit peerId)");
2948
+ return;
2949
+ }
2950
+ const peer = this.peers.get(peerId);
2845
2951
  if (peer?.pc) {
2846
2952
  log(`Received SDP answer for peer ${peer.peerId}`);
2847
2953
  peer.pc.setRemoteDescription(payload.sdp, payload.type);
@@ -2849,16 +2955,18 @@ var init_daemon_p2p = __esm({
2849
2955
  return;
2850
2956
  }
2851
2957
  if (type === "p2p_ice") {
2852
- const peer = peerId ? this.peers.get(peerId) : this.getFirstPeer();
2958
+ if (!peerId) {
2959
+ log("p2p_ice without peerId \u2014 ignoring (multi-peer requires explicit peerId)");
2960
+ return;
2961
+ }
2962
+ const peer = this.peers.get(peerId);
2853
2963
  if (peer?.pc && payload.candidate) {
2854
2964
  peer.pc.addRemoteCandidate(payload.candidate, payload.mid || payload.sdpMid || "0");
2855
2965
  }
2856
2966
  return;
2857
2967
  }
2858
2968
  }
2859
- getFirstPeer() {
2860
- return this.peers.values().next().value;
2861
- }
2969
+ // getFirstPeer removed — multi-peer requires explicit peerId routing
2862
2970
  // ─── 연결 관리 ──────────────────────────────
2863
2971
  disconnectPeer(peerId) {
2864
2972
  const peer = this.peers.get(peerId);
@@ -3143,15 +3251,15 @@ var init_dev_server = __esm({
3143
3251
  this.json(res, 500, { error: e.message });
3144
3252
  }
3145
3253
  });
3146
- return new Promise((resolve5, reject) => {
3254
+ return new Promise((resolve6, reject) => {
3147
3255
  this.server.listen(port, "127.0.0.1", () => {
3148
3256
  this.log(`Dev server listening on http://127.0.0.1:${port}`);
3149
- resolve5();
3257
+ resolve6();
3150
3258
  });
3151
3259
  this.server.on("error", (e) => {
3152
3260
  if (e.code === "EADDRINUSE") {
3153
3261
  this.log(`Port ${port} in use, skipping dev server`);
3154
- resolve5();
3262
+ resolve6();
3155
3263
  } else {
3156
3264
  reject(e);
3157
3265
  }
@@ -3818,14 +3926,14 @@ var init_dev_server = __esm({
3818
3926
  res.end(JSON.stringify(data, null, 2));
3819
3927
  }
3820
3928
  async readBody(req) {
3821
- return new Promise((resolve5) => {
3929
+ return new Promise((resolve6) => {
3822
3930
  let body = "";
3823
3931
  req.on("data", (chunk) => body += chunk);
3824
3932
  req.on("end", () => {
3825
3933
  try {
3826
- resolve5(JSON.parse(body));
3934
+ resolve6(JSON.parse(body));
3827
3935
  } catch {
3828
- resolve5({});
3936
+ resolve6({});
3829
3937
  }
3830
3938
  });
3831
3939
  });
@@ -4198,7 +4306,11 @@ var init_daemon_commands = __esm({
4198
4306
  getProvider(overrideType) {
4199
4307
  const key = overrideType || this._currentProviderType || this._currentIdeType;
4200
4308
  if (!key || !this.ctx.providerLoader) return void 0;
4201
- return this.ctx.providerLoader.resolve(key);
4309
+ const result = this.ctx.providerLoader.resolve(key);
4310
+ if (result) return result;
4311
+ const baseType = key.split("_")[0];
4312
+ if (baseType !== key) return this.ctx.providerLoader.resolve(baseType);
4313
+ return void 0;
4202
4314
  }
4203
4315
  /** Get a provider script by name from ProviderLoader.
4204
4316
  * Returns the script string or null. */
@@ -4376,6 +4488,20 @@ var init_daemon_commands = __esm({
4376
4488
  return this.handleGetProviderSettings(args);
4377
4489
  case "set_provider_setting":
4378
4490
  return this.handleSetProviderSetting(args);
4491
+ // ─── IDE Extension Settings (per-IDE on/off) ──────────────
4492
+ case "get_ide_extensions":
4493
+ return this.handleGetIdeExtensions(args);
4494
+ case "set_ide_extension":
4495
+ return this.handleSetIdeExtension(args);
4496
+ // ─── Extension Model / Mode Control ──────────────
4497
+ case "list_extension_models":
4498
+ return await this.handleExtensionScript(args, "listModels");
4499
+ case "set_extension_model":
4500
+ return await this.handleExtensionScript(args, "setModel");
4501
+ case "list_extension_modes":
4502
+ return await this.handleExtensionScript(args, "listModes");
4503
+ case "set_extension_mode":
4504
+ return await this.handleExtensionScript(args, "setMode");
4379
4505
  default:
4380
4506
  return { success: false, error: `Unknown command: ${cmd}` };
4381
4507
  }
@@ -4686,6 +4812,7 @@ var init_daemon_commands = __esm({
4686
4812
  const provider2 = this.getProvider();
4687
4813
  const action = args?.action || "approve";
4688
4814
  const button = args?.button || args?.buttonText || (action === "approve" ? "Accept" : action === "reject" ? "Reject" : "Accept");
4815
+ console.log(`[resolveAction] action=${action} button="${button}" provider=${provider2?.type}`);
4689
4816
  if (provider2?.category === "extension" && this.agentStream && this.getCdp()) {
4690
4817
  const ok = await this.agentStream.resolveAgentAction(
4691
4818
  this.getCdp(),
@@ -4695,14 +4822,50 @@ var init_daemon_commands = __esm({
4695
4822
  );
4696
4823
  return { success: ok };
4697
4824
  }
4698
- try {
4699
- const evalResult = await this.evaluateProviderScript("resolveAction", {
4700
- ACTION: action,
4701
- BUTTON: button
4702
- });
4703
- if (evalResult) return { success: true };
4704
- } catch (e) {
4705
- return { success: false, error: `resolveAction failed: ${e.message}` };
4825
+ if (provider2?.scripts?.resolveAction) {
4826
+ const script = provider2.scripts.resolveAction({ action, button, buttonText: button });
4827
+ if (script) {
4828
+ const cdp2 = this.getCdp();
4829
+ if (!cdp2?.isConnected) return { success: false, error: "CDP not connected" };
4830
+ try {
4831
+ const raw = await cdp2.evaluate(script, 3e4);
4832
+ let result = raw;
4833
+ if (typeof raw === "string") {
4834
+ try {
4835
+ result = JSON.parse(raw);
4836
+ } catch {
4837
+ }
4838
+ }
4839
+ console.log(`[resolveAction] script result:`, JSON.stringify(result));
4840
+ if (result?.resolved) {
4841
+ console.log(`[resolveAction] script-click resolved \u2014 "${result.clicked}"`);
4842
+ return { success: true, clicked: result.clicked };
4843
+ }
4844
+ if (result?.found && result.x != null && result.y != null) {
4845
+ const x = result.x;
4846
+ const y = result.y;
4847
+ await cdp2.send("Input.dispatchMouseEvent", {
4848
+ type: "mousePressed",
4849
+ x,
4850
+ y,
4851
+ button: "left",
4852
+ clickCount: 1
4853
+ });
4854
+ await cdp2.send("Input.dispatchMouseEvent", {
4855
+ type: "mouseReleased",
4856
+ x,
4857
+ y,
4858
+ button: "left",
4859
+ clickCount: 1
4860
+ });
4861
+ console.log(`[resolveAction] CDP click at (${x}, ${y}) \u2014 "${result.text}"`);
4862
+ return { success: true, clicked: result.text };
4863
+ }
4864
+ return { success: false, error: result?.found === false ? `Button not found: ${button}` : "No coordinates" };
4865
+ } catch (e) {
4866
+ return { success: false, error: `resolveAction failed: ${e.message}` };
4867
+ }
4868
+ }
4706
4869
  }
4707
4870
  return { success: false, error: "resolveAction script not available for this provider" };
4708
4871
  }
@@ -5097,6 +5260,103 @@ var init_daemon_commands = __esm({
5097
5260
  }
5098
5261
  return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
5099
5262
  }
5263
+ // ─── Extension Script Execution (Model/Mode) ─────
5264
+ /**
5265
+ * Extension provider의 CDP 스크립트를 실행하는 범용 핸들러.
5266
+ * args: { agentType, ideType?, model?, mode?, ... }
5267
+ * scriptName: 'listModels' | 'setModel' | 'listModes' | 'setMode'
5268
+ */
5269
+ async handleExtensionScript(args, scriptName) {
5270
+ const { agentType, ideType } = args || {};
5271
+ console.log(`[ExtScript] ${scriptName} agentType=${agentType} ideType=${ideType} _currentIdeType=${this._currentIdeType}`);
5272
+ if (!agentType) return { success: false, error: "agentType is required" };
5273
+ const loader = this.ctx.providerLoader;
5274
+ if (!loader) return { success: false, error: "ProviderLoader not initialized" };
5275
+ const provider2 = loader.get(agentType);
5276
+ if (!provider2) return { success: false, error: `Provider not found: ${agentType}` };
5277
+ if (!provider2.scripts?.[scriptName]) {
5278
+ return { success: false, error: `Script '${scriptName}' not available for ${agentType}` };
5279
+ }
5280
+ const script = provider2.scripts[scriptName](args);
5281
+ if (!script) return { success: false, error: `Script '${scriptName}' returned null` };
5282
+ const cdpKey = provider2.category === "ide" ? this._currentIdeType || agentType : this._currentIdeType || ideType;
5283
+ console.log(`[ExtScript] provider=${provider2.type} category=${provider2.category} cdpKey=${cdpKey}`);
5284
+ const cdp2 = this.getCdp(cdpKey);
5285
+ if (!cdp2?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
5286
+ try {
5287
+ let result;
5288
+ if (provider2.category === "extension") {
5289
+ const sessions = cdp2.getAgentSessions();
5290
+ let targetSessionId = null;
5291
+ for (const [sessionId, target] of sessions) {
5292
+ if (target.agentType === agentType) {
5293
+ targetSessionId = sessionId;
5294
+ break;
5295
+ }
5296
+ }
5297
+ if (!targetSessionId) {
5298
+ return { success: false, error: `No active session found for ${agentType}` };
5299
+ }
5300
+ result = await cdp2.evaluateInSession(targetSessionId, script);
5301
+ } else {
5302
+ result = await cdp2.evaluate(script, 3e4);
5303
+ }
5304
+ if (typeof result === "string") {
5305
+ try {
5306
+ const parsed = JSON.parse(result);
5307
+ return { success: true, ...parsed };
5308
+ } catch {
5309
+ return { success: true, result };
5310
+ }
5311
+ }
5312
+ return { success: true, result };
5313
+ } catch (e) {
5314
+ return { success: false, error: `Script execution failed: ${e.message}` };
5315
+ }
5316
+ }
5317
+ // ─── IDE Extension Settings (per-IDE on/off) ─────
5318
+ handleGetIdeExtensions(args) {
5319
+ const { ideType } = args || {};
5320
+ const loader = this.ctx.providerLoader;
5321
+ if (!loader) return { success: false, error: "ProviderLoader not initialized" };
5322
+ const allExtProviders = loader.getByCategory?.("extension") || [];
5323
+ const config = loadConfig();
5324
+ if (ideType) {
5325
+ const extensions = allExtProviders.map((p) => ({
5326
+ type: p.type,
5327
+ name: p.name,
5328
+ extensionId: p.extensionId,
5329
+ enabled: config.ideSettings?.[ideType]?.extensions?.[p.type]?.enabled === true
5330
+ }));
5331
+ return { success: true, ideType, extensions };
5332
+ }
5333
+ const connectedIdes = [...this.ctx.cdpManagers?.keys?.() || []];
5334
+ const result = {};
5335
+ for (const ide of connectedIdes) {
5336
+ result[ide] = allExtProviders.map((p) => ({
5337
+ type: p.type,
5338
+ name: p.name,
5339
+ extensionId: p.extensionId,
5340
+ enabled: config.ideSettings?.[ide]?.extensions?.[p.type]?.enabled === true
5341
+ }));
5342
+ }
5343
+ return { success: true, ides: result };
5344
+ }
5345
+ handleSetIdeExtension(args) {
5346
+ const { ideType, extensionType, enabled } = args || {};
5347
+ if (!ideType || !extensionType || enabled === void 0) {
5348
+ return { success: false, error: "ideType, extensionType, and enabled are required" };
5349
+ }
5350
+ const loader = this.ctx.providerLoader;
5351
+ if (!loader?.setIdeExtensionEnabled) {
5352
+ return { success: false, error: "ProviderLoader not initialized" };
5353
+ }
5354
+ const ok = loader.setIdeExtensionEnabled(ideType, extensionType, !!enabled);
5355
+ if (ok) {
5356
+ return { success: true, ideType, extensionType, enabled: !!enabled };
5357
+ }
5358
+ return { success: false, error: "Failed to save setting" };
5359
+ }
5100
5360
  };
5101
5361
  }
5102
5362
  });
@@ -5239,12 +5499,12 @@ var init_manager = __esm({
5239
5499
  constructor(logFn, providerLoader) {
5240
5500
  this.logFn = logFn || console.log;
5241
5501
  if (providerLoader) {
5242
- const extensionProviders = providerLoader.getByCategory("extension");
5243
- this.allAdapters = extensionProviders.map((p) => {
5502
+ const allExtProviders = providerLoader.getByCategory("extension");
5503
+ for (const p of allExtProviders) {
5244
5504
  const adapter = new ProviderStreamAdapter(p);
5505
+ this.allAdapters.push(adapter);
5245
5506
  this.logFn(`[AgentStream] Adapter created: ${p.type} (${p.name})`);
5246
- return adapter;
5247
- });
5507
+ }
5248
5508
  }
5249
5509
  }
5250
5510
  setLocalServer(server) {
@@ -5498,14 +5758,238 @@ var init_agent_stream = __esm({
5498
5758
  }
5499
5759
  });
5500
5760
 
5761
+ // src/daemon-logger.ts
5762
+ var daemon_logger_exports = {};
5763
+ __export(daemon_logger_exports, {
5764
+ LOG: () => LOG,
5765
+ LOG_DIR_PATH: () => LOG_DIR_PATH,
5766
+ LOG_PATH: () => LOG_PATH,
5767
+ cdpLogFn: () => cdpLogFn,
5768
+ daemonLog: () => daemonLog,
5769
+ getLogBufferSize: () => getLogBufferSize,
5770
+ getLogLevel: () => getLogLevel,
5771
+ getLogPath: () => getLogPath,
5772
+ getRecentLogs: () => getRecentLogs,
5773
+ installGlobalInterceptor: () => installGlobalInterceptor,
5774
+ setLogLevel: () => setLogLevel
5775
+ });
5776
+ function setLogLevel(level) {
5777
+ currentLevel = level;
5778
+ daemonLog("Logger", `Log level set to: ${level}`, "info");
5779
+ }
5780
+ function getLogLevel() {
5781
+ return currentLevel;
5782
+ }
5783
+ function getDateStr() {
5784
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5785
+ }
5786
+ function checkDateRotation() {
5787
+ const today = getDateStr();
5788
+ if (today !== currentDate) {
5789
+ currentDate = today;
5790
+ currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
5791
+ cleanOldLogs();
5792
+ }
5793
+ }
5794
+ function cleanOldLogs() {
5795
+ try {
5796
+ const files = fs5.readdirSync(LOG_DIR).filter((f) => f.startsWith("daemon-") && f.endsWith(".log"));
5797
+ const cutoff = /* @__PURE__ */ new Date();
5798
+ cutoff.setDate(cutoff.getDate() - MAX_LOG_DAYS);
5799
+ const cutoffStr = cutoff.toISOString().slice(0, 10);
5800
+ for (const file of files) {
5801
+ const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
5802
+ if (dateMatch && dateMatch[1] < cutoffStr) {
5803
+ try {
5804
+ fs5.unlinkSync(path6.join(LOG_DIR, file));
5805
+ } catch {
5806
+ }
5807
+ }
5808
+ }
5809
+ } catch {
5810
+ }
5811
+ }
5812
+ function rotateSizeIfNeeded() {
5813
+ try {
5814
+ const stat = fs5.statSync(currentLogFile);
5815
+ if (stat.size > MAX_LOG_SIZE) {
5816
+ const backup = currentLogFile.replace(".log", ".1.log");
5817
+ try {
5818
+ fs5.unlinkSync(backup);
5819
+ } catch {
5820
+ }
5821
+ fs5.renameSync(currentLogFile, backup);
5822
+ }
5823
+ } catch {
5824
+ }
5825
+ }
5826
+ function writeToFile(line) {
5827
+ try {
5828
+ if (++writeCount % 1e3 === 0) {
5829
+ checkDateRotation();
5830
+ rotateSizeIfNeeded();
5831
+ }
5832
+ fs5.appendFileSync(currentLogFile, line + "\n");
5833
+ } catch {
5834
+ }
5835
+ }
5836
+ function getRecentLogs(count = 50, minLevel = "info") {
5837
+ const minNum = LEVEL_NUM[minLevel];
5838
+ const filtered = ringBuffer.filter((e) => LEVEL_NUM[e.level] >= minNum);
5839
+ return filtered.slice(-count);
5840
+ }
5841
+ function getLogBufferSize() {
5842
+ return ringBuffer.length;
5843
+ }
5844
+ function ts() {
5845
+ return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
5846
+ }
5847
+ function fullTs() {
5848
+ return (/* @__PURE__ */ new Date()).toISOString();
5849
+ }
5850
+ function daemonLog(category, msg, level = "info") {
5851
+ const shouldOutput = LEVEL_NUM[level] >= LEVEL_NUM[currentLevel];
5852
+ const label = LEVEL_LABEL[level];
5853
+ const line = `[${ts()}] [${label}] [${category}] ${msg}`;
5854
+ writeToFile(line);
5855
+ ringBuffer.push({ ts: Date.now(), level, category, message: msg });
5856
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
5857
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
5858
+ }
5859
+ if (shouldOutput) {
5860
+ origConsoleLog(line);
5861
+ }
5862
+ }
5863
+ function cdpLogFn(ideType) {
5864
+ return (msg) => {
5865
+ const isDebug = msg.includes("discoverAgentWebviews:") && !msg.includes("Found agent") || msg.includes("[P2P] sent");
5866
+ daemonLog(`CDP:${ideType}`, msg, isDebug ? "debug" : "info");
5867
+ };
5868
+ }
5869
+ function installGlobalInterceptor() {
5870
+ if (interceptorInstalled) return;
5871
+ interceptorInstalled = true;
5872
+ const stripAnsi2 = (str) => str.replace(/\x1B\[[0-9;]*m/g, "");
5873
+ const isDaemonLogLine = (msg) => /\[(DBG|INF|WRN|ERR)\]/.test(msg);
5874
+ console.log = (...args) => {
5875
+ origConsoleLog(...args);
5876
+ try {
5877
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
5878
+ const clean = stripAnsi2(msg);
5879
+ if (isDaemonLogLine(clean)) return;
5880
+ const line = clean.startsWith("[20") ? clean : `[${fullTs()}] ${clean}`;
5881
+ writeToFile(line);
5882
+ const catMatch = clean.match(/\[([^\]]+)\]/);
5883
+ ringBuffer.push({
5884
+ ts: Date.now(),
5885
+ level: "info",
5886
+ category: catMatch?.[1] || "System",
5887
+ message: clean
5888
+ });
5889
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
5890
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
5891
+ }
5892
+ } catch {
5893
+ }
5894
+ };
5895
+ console.error = (...args) => {
5896
+ origConsoleError(...args);
5897
+ try {
5898
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
5899
+ const clean = stripAnsi2(msg);
5900
+ if (isDaemonLogLine(clean)) return;
5901
+ const line = `[${fullTs()}] [ERROR] ${clean}`;
5902
+ writeToFile(line);
5903
+ ringBuffer.push({ ts: Date.now(), level: "error", category: "System", message: clean });
5904
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
5905
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
5906
+ }
5907
+ } catch {
5908
+ }
5909
+ };
5910
+ console.warn = (...args) => {
5911
+ origConsoleWarn(...args);
5912
+ try {
5913
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
5914
+ const clean = stripAnsi2(msg);
5915
+ if (isDaemonLogLine(clean)) return;
5916
+ const line = `[${fullTs()}] [WARN] ${clean}`;
5917
+ writeToFile(line);
5918
+ ringBuffer.push({ ts: Date.now(), level: "warn", category: "System", message: clean });
5919
+ if (ringBuffer.length > RING_BUFFER_SIZE) {
5920
+ ringBuffer.splice(0, ringBuffer.length - RING_BUFFER_SIZE);
5921
+ }
5922
+ } catch {
5923
+ }
5924
+ };
5925
+ writeToFile(`
5926
+ === ADHDev Daemon started at ${fullTs()} ===`);
5927
+ writeToFile(`Log file: ${currentLogFile}`);
5928
+ writeToFile(`Log level: ${currentLevel}`);
5929
+ }
5930
+ function getLogPath() {
5931
+ return currentLogFile;
5932
+ }
5933
+ var fs5, path6, os6, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
5934
+ var init_daemon_logger = __esm({
5935
+ "src/daemon-logger.ts"() {
5936
+ "use strict";
5937
+ fs5 = __toESM(require("fs"));
5938
+ path6 = __toESM(require("path"));
5939
+ os6 = __toESM(require("os"));
5940
+ LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
5941
+ LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
5942
+ currentLevel = "info";
5943
+ LOG_DIR = process.platform === "darwin" ? path6.join(os6.homedir(), "Library", "Logs", "adhdev") : path6.join(os6.homedir(), ".local", "share", "adhdev", "logs");
5944
+ MAX_LOG_SIZE = 5 * 1024 * 1024;
5945
+ MAX_LOG_DAYS = 7;
5946
+ try {
5947
+ fs5.mkdirSync(LOG_DIR, { recursive: true });
5948
+ } catch {
5949
+ }
5950
+ currentDate = getDateStr();
5951
+ currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
5952
+ cleanOldLogs();
5953
+ try {
5954
+ const oldLog = path6.join(LOG_DIR, "daemon.log");
5955
+ if (fs5.existsSync(oldLog)) {
5956
+ const stat = fs5.statSync(oldLog);
5957
+ const oldDate = stat.mtime.toISOString().slice(0, 10);
5958
+ fs5.renameSync(oldLog, path6.join(LOG_DIR, `daemon-${oldDate}.log`));
5959
+ }
5960
+ const oldLogBackup = path6.join(LOG_DIR, "daemon.log.old");
5961
+ if (fs5.existsSync(oldLogBackup)) {
5962
+ fs5.unlinkSync(oldLogBackup);
5963
+ }
5964
+ } catch {
5965
+ }
5966
+ writeCount = 0;
5967
+ RING_BUFFER_SIZE = 200;
5968
+ ringBuffer = [];
5969
+ origConsoleLog = console.log.bind(console);
5970
+ origConsoleError = console.error.bind(console);
5971
+ origConsoleWarn = console.warn.bind(console);
5972
+ LOG = {
5973
+ debug: (category, msg) => daemonLog(category, msg, "debug"),
5974
+ info: (category, msg) => daemonLog(category, msg, "info"),
5975
+ warn: (category, msg) => daemonLog(category, msg, "warn"),
5976
+ error: (category, msg) => daemonLog(category, msg, "error")
5977
+ };
5978
+ interceptorInstalled = false;
5979
+ LOG_PATH = path6.join(LOG_DIR, `daemon-${getDateStr()}.log`);
5980
+ LOG_DIR_PATH = LOG_DIR;
5981
+ }
5982
+ });
5983
+
5501
5984
  // src/daemon-status.ts
5502
- var os6, path6, DaemonStatusReporter;
5985
+ var os7, path7, DaemonStatusReporter;
5503
5986
  var init_daemon_status = __esm({
5504
5987
  "src/daemon-status.ts"() {
5505
5988
  "use strict";
5506
- os6 = __toESM(require("os"));
5507
- path6 = __toESM(require("path"));
5989
+ os7 = __toESM(require("os"));
5990
+ path7 = __toESM(require("path"));
5508
5991
  init_config();
5992
+ init_daemon_logger();
5509
5993
  DaemonStatusReporter = class {
5510
5994
  deps;
5511
5995
  log;
@@ -5563,7 +6047,7 @@ var init_daemon_status = __esm({
5563
6047
  }
5564
6048
  }
5565
6049
  emitStatusEvent(event) {
5566
- this.log(`[${this.ts()}] [StatusEvent\u2192Server] ${event.event} (${event.providerType || event.ideType || ""})`);
6050
+ LOG.info("StatusEvent", `${event.event} (${event.providerType || event.ideType || ""})`);
5567
6051
  this.deps.bridge?.sendMessage("status_event", event);
5568
6052
  }
5569
6053
  removeAgentTracking(_key) {
@@ -5590,7 +6074,8 @@ var init_daemon_status = __esm({
5590
6074
  return `${s.type}(${s.status},${msgs}msg,${exts}ext${s.currentModel ? ",model=" + s.currentModel : ""})`;
5591
6075
  }).join(", ");
5592
6076
  const cliSummary = cliStates.map((s) => `${s.type}(${s.status})`).join(", ");
5593
- this.log(`[${this.ts()}] [StatusReport\u2192${target}] IDE: ${ideStates.length} [${ideSummary}] CLI: ${cliStates.length} [${cliSummary}]`);
6077
+ const logLevel = opts?.p2pOnly ? "debug" : "info";
6078
+ LOG[logLevel]("StatusReport", `\u2192${target} IDE: ${ideStates.length} [${ideSummary}] CLI: ${cliStates.length} [${cliSummary}]`);
5594
6079
  const managedIdes = ideStates.map((s) => ({
5595
6080
  ideType: s.type,
5596
6081
  ideVersion: "",
@@ -5616,23 +6101,45 @@ var init_daemon_status = __esm({
5616
6101
  currentAutoApprove: s.currentAutoApprove
5617
6102
  }));
5618
6103
  const instanceIdeTypes = new Set(ideStates.map((s) => s.type));
6104
+ const instanceBaseTypes = /* @__PURE__ */ new Set();
6105
+ for (const t of instanceIdeTypes) {
6106
+ instanceBaseTypes.add(t);
6107
+ const base = t.split("_")[0];
6108
+ if (base) instanceBaseTypes.add(base);
6109
+ }
5619
6110
  const perExtData = localServer?.getPerExtensionData() || [];
5620
6111
  for (const ext of perExtData) {
5621
6112
  const ideKey = ext.ideType.toLowerCase();
5622
- if (instanceIdeTypes.has(ideKey)) continue;
5623
- managedIdes.push({
5624
- ideType: ext.ideType,
5625
- ideVersion: ext.ideVersion,
5626
- instanceId: ext.instanceId,
5627
- workspaceFolders: ext.workspaceFolders,
5628
- activeFile: ext.activeFile,
5629
- terminals: ext.terminals,
5630
- aiAgents: ext.aiAgents,
5631
- activeChat: ext.activeChat,
5632
- chats: ext.chats || [],
5633
- agentStreams: ext.agentStreams || [],
5634
- cdpConnected: false
5635
- });
6113
+ if (instanceBaseTypes.has(ideKey)) {
6114
+ const match = managedIdes.find(
6115
+ (m) => m.ideType === ideKey || m.ideType.split("_")[0] === ideKey
6116
+ );
6117
+ if (match) {
6118
+ if (!match.workspaceFolders?.length && ext.workspaceFolders?.length) {
6119
+ match.workspaceFolders = ext.workspaceFolders;
6120
+ }
6121
+ if (!match.activeFile && ext.activeFile) match.activeFile = ext.activeFile;
6122
+ if (!match.terminals && ext.terminals) match.terminals = ext.terminals;
6123
+ if ((!match.aiAgents || match.aiAgents.length === 0) && ext.aiAgents?.length) {
6124
+ match.aiAgents = ext.aiAgents;
6125
+ }
6126
+ if (!match.ideVersion && ext.ideVersion) match.ideVersion = ext.ideVersion;
6127
+ }
6128
+ } else {
6129
+ managedIdes.push({
6130
+ ideType: ext.ideType,
6131
+ ideVersion: ext.ideVersion,
6132
+ instanceId: ext.instanceId,
6133
+ workspaceFolders: ext.workspaceFolders,
6134
+ activeFile: ext.activeFile,
6135
+ terminals: ext.terminals,
6136
+ aiAgents: ext.aiAgents,
6137
+ activeChat: ext.activeChat,
6138
+ chats: ext.chats || [],
6139
+ agentStreams: ext.agentStreams || [],
6140
+ cdpConnected: false
6141
+ });
6142
+ }
5636
6143
  }
5637
6144
  const managedClis = cliStates.map((s) => ({
5638
6145
  id: s.instanceId,
@@ -5654,15 +6161,15 @@ var init_daemon_status = __esm({
5654
6161
  daemonMode: true,
5655
6162
  machineNickname: loadConfig().machineNickname || null,
5656
6163
  machine: {
5657
- hostname: os6.hostname(),
5658
- platform: os6.platform(),
5659
- release: os6.release(),
5660
- arch: os6.arch(),
5661
- cpus: os6.cpus().length,
5662
- totalMem: os6.totalmem(),
5663
- freeMem: os6.freemem(),
5664
- loadavg: os6.loadavg(),
5665
- uptime: os6.uptime()
6164
+ hostname: os7.hostname(),
6165
+ platform: os7.platform(),
6166
+ release: os7.release(),
6167
+ arch: os7.arch(),
6168
+ cpus: os7.cpus().length,
6169
+ totalMem: os7.totalmem(),
6170
+ freeMem: os7.freemem(),
6171
+ loadavg: os7.loadavg(),
6172
+ uptime: os7.uptime()
5666
6173
  },
5667
6174
  managedIdes,
5668
6175
  managedClis,
@@ -5684,7 +6191,7 @@ var init_daemon_status = __esm({
5684
6191
  })),
5685
6192
  timestamp: now,
5686
6193
  activeFile: extSummary.activeFile,
5687
- workspaceFolders: extSummary.workspaceFolders?.length > 0 ? extSummary.workspaceFolders : Array.from(adapters.values()).map((a) => ({ name: path6.basename(a.workingDir), path: a.workingDir })),
6194
+ workspaceFolders: extSummary.workspaceFolders?.length > 0 ? extSummary.workspaceFolders : Array.from(adapters.values()).map((a) => ({ name: path7.basename(a.workingDir), path: a.workingDir })),
5688
6195
  terminals: extSummary.terminals,
5689
6196
  aiAgents: [
5690
6197
  ...managedIdes.flatMap((ide) => (ide.aiAgents || []).map((a) => ({
@@ -5692,17 +6199,17 @@ var init_daemon_status = __esm({
5692
6199
  name: a.name,
5693
6200
  status: a.status,
5694
6201
  ideType: ide.ideType
5695
- }))),
5696
- ...managedClis.map((c) => ({ id: c.cliType, name: c.cliName, status: c.status }))
6202
+ })))
6203
+ // CLIs are already in managedClis don't duplicate here
5697
6204
  ],
5698
6205
  activeChat: managedClis[0]?.activeChat || managedIdes[0]?.activeChat || null,
5699
6206
  agentStreams: managedIdes.flatMap((ide) => ide.agentStreams || []),
5700
6207
  connectedExtensions: extSummary.connectedIdes,
5701
- system: { platform: os6.platform(), hostname: os6.hostname() }
6208
+ system: { platform: os7.platform(), hostname: os7.hostname() }
5702
6209
  };
5703
6210
  const p2pSent = this.sendP2PPayload(payload);
5704
6211
  if (p2pSent) {
5705
- this.log(`[${this.ts()}] [P2P] sent (${JSON.stringify(payload).length} bytes)`);
6212
+ LOG.debug("P2P", `sent (${JSON.stringify(payload).length} bytes)`);
5706
6213
  }
5707
6214
  if (opts?.p2pOnly) return;
5708
6215
  const plan = bridge.getUserPlan();
@@ -5711,11 +6218,11 @@ var init_daemon_status = __esm({
5711
6218
  daemonMode: true,
5712
6219
  machineNickname: payload.machineNickname,
5713
6220
  machine: {
5714
- hostname: os6.hostname(),
5715
- platform: os6.platform(),
5716
- arch: os6.arch(),
5717
- cpus: os6.cpus().length,
5718
- totalMem: os6.totalmem()
6221
+ hostname: os7.hostname(),
6222
+ platform: os7.platform(),
6223
+ arch: os7.arch(),
6224
+ cpus: os7.cpus().length,
6225
+ totalMem: os7.totalmem()
5719
6226
  },
5720
6227
  managedIdes: managedIdes.map((ide) => ({
5721
6228
  ideType: ide.ideType,
@@ -5739,7 +6246,7 @@ var init_daemon_status = __esm({
5739
6246
  timestamp: now
5740
6247
  };
5741
6248
  bridge.sendMessage("status_report", wsPayload);
5742
- this.log(`[${this.ts()}] [Server] sent status_report (${JSON.stringify(wsPayload).length} bytes)`);
6249
+ LOG.debug("Server", `sent status_report (${JSON.stringify(wsPayload).length} bytes)`);
5743
6250
  }
5744
6251
  }
5745
6252
  // ─── P2P ─────────────────────────────────────────
@@ -5774,7 +6281,7 @@ function stripAnsi(str) {
5774
6281
  return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][^\x1B]*\x1B\\/g, "");
5775
6282
  }
5776
6283
  function findBinary(name) {
5777
- const isWin = os7.platform() === "win32";
6284
+ const isWin = os8.platform() === "win32";
5778
6285
  try {
5779
6286
  const cmd = isWin ? `where ${name}` : `which ${name}`;
5780
6287
  return (0, import_child_process4.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n")[0].trim();
@@ -5782,11 +6289,11 @@ function findBinary(name) {
5782
6289
  return isWin ? `${name}.cmd` : name;
5783
6290
  }
5784
6291
  }
5785
- var os7, import_child_process4, pty, ProviderCliAdapter;
6292
+ var os8, import_child_process4, pty, ProviderCliAdapter;
5786
6293
  var init_provider_cli_adapter = __esm({
5787
6294
  "src/cli-adapters/provider-cli-adapter.ts"() {
5788
6295
  "use strict";
5789
- os7 = __toESM(require("os"));
6296
+ os8 = __toESM(require("os"));
5790
6297
  import_child_process4 = require("child_process");
5791
6298
  try {
5792
6299
  pty = require("node-pty");
@@ -5799,7 +6306,7 @@ var init_provider_cli_adapter = __esm({
5799
6306
  this.provider = provider2;
5800
6307
  this.cliType = provider2.type;
5801
6308
  this.cliName = provider2.name;
5802
- this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os7.homedir()) : workingDir;
6309
+ this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os8.homedir()) : workingDir;
5803
6310
  const t = provider2.timeouts || {};
5804
6311
  this.timeouts = {
5805
6312
  ptyFlush: t.ptyFlush ?? 50,
@@ -5857,7 +6364,7 @@ var init_provider_cli_adapter = __esm({
5857
6364
  if (!pty) throw new Error("node-pty is not installed");
5858
6365
  const { spawn: spawnConfig } = this.provider;
5859
6366
  const binaryPath = findBinary(spawnConfig.command);
5860
- const isWin = os7.platform() === "win32";
6367
+ const isWin = os8.platform() === "win32";
5861
6368
  const allArgs = [...spawnConfig.args, ...this.extraArgs];
5862
6369
  console.log(`[${this.cliType}] Spawning in ${this.workingDir}`);
5863
6370
  let shellCmd;
@@ -6099,7 +6606,7 @@ var init_provider_cli_adapter = __esm({
6099
6606
 
6100
6607
  // src/cli-detector.ts
6101
6608
  async function detectCLIs() {
6102
- const platform7 = os8.platform();
6609
+ const platform7 = os9.platform();
6103
6610
  const whichCmd = platform7 === "win32" ? "where" : "which";
6104
6611
  const results = [];
6105
6612
  for (const cli of KNOWN_CLIS) {
@@ -6133,12 +6640,12 @@ async function detectCLI(cliId) {
6133
6640
  const all = await detectCLIs();
6134
6641
  return all.find((c) => c.id === normalizedId && c.installed) || null;
6135
6642
  }
6136
- var import_child_process5, os8, KNOWN_CLIS;
6643
+ var import_child_process5, os9, KNOWN_CLIS;
6137
6644
  var init_cli_detector = __esm({
6138
6645
  "src/cli-detector.ts"() {
6139
6646
  "use strict";
6140
6647
  import_child_process5 = require("child_process");
6141
- os8 = __toESM(require("os"));
6648
+ os9 = __toESM(require("os"));
6142
6649
  KNOWN_CLIS = [
6143
6650
  { id: "gemini-cli", displayName: "Gemini CLI", icon: "\u264A", command: "gemini" },
6144
6651
  { id: "claude-code", displayName: "Claude Code", icon: "\u{1F916}", command: "claude" },
@@ -6147,17 +6654,267 @@ var init_cli_detector = __esm({
6147
6654
  }
6148
6655
  });
6149
6656
 
6150
- // src/daemon-cli.ts
6151
- var os9, path7, import_chalk, DaemonCliManager;
6152
- var init_daemon_cli = __esm({
6153
- "src/daemon-cli.ts"() {
6154
- "use strict";
6155
- os9 = __toESM(require("os"));
6156
- path7 = __toESM(require("path"));
6657
+ // src/providers/status-monitor.ts
6658
+ var DEFAULT_MONITOR_CONFIG, StatusMonitor;
6659
+ var init_status_monitor = __esm({
6660
+ "src/providers/status-monitor.ts"() {
6661
+ "use strict";
6662
+ DEFAULT_MONITOR_CONFIG = {
6663
+ approvalAlert: true,
6664
+ longGeneratingAlert: true,
6665
+ longGeneratingThresholdSec: 180,
6666
+ // 3분
6667
+ alertCooldownSec: 60
6668
+ // 1분 쿨다운
6669
+ };
6670
+ StatusMonitor = class {
6671
+ config;
6672
+ lastAlertTime = /* @__PURE__ */ new Map();
6673
+ generatingStartTimes = /* @__PURE__ */ new Map();
6674
+ constructor(config) {
6675
+ this.config = { ...DEFAULT_MONITOR_CONFIG, ...config };
6676
+ }
6677
+ /** 설정 업데이트 (Provider Settings에서 호출) */
6678
+ updateConfig(partial) {
6679
+ Object.assign(this.config, partial);
6680
+ }
6681
+ /** 현재 설정 반환 */
6682
+ getConfig() {
6683
+ return { ...this.config };
6684
+ }
6685
+ /**
6686
+ * 상태 전이 체크 → 알림 이벤트 배열 반환
6687
+ * 각 onTick()에서 호출.
6688
+ */
6689
+ check(agentKey, status, now) {
6690
+ const events = [];
6691
+ if (this.config.approvalAlert && status === "waiting_approval") {
6692
+ if (this.shouldAlert(agentKey + ":approval", now)) {
6693
+ events.push({
6694
+ type: "monitor:approval_waiting",
6695
+ agentKey,
6696
+ timestamp: now,
6697
+ message: `${agentKey} is waiting for approval`
6698
+ });
6699
+ }
6700
+ }
6701
+ if (status === "generating" || status === "streaming") {
6702
+ if (!this.generatingStartTimes.has(agentKey)) {
6703
+ this.generatingStartTimes.set(agentKey, now);
6704
+ }
6705
+ if (this.config.longGeneratingAlert) {
6706
+ const startedAt = this.generatingStartTimes.get(agentKey);
6707
+ const elapsedSec = Math.round((now - startedAt) / 1e3);
6708
+ if (elapsedSec > this.config.longGeneratingThresholdSec) {
6709
+ if (this.shouldAlert(agentKey + ":long_gen", now)) {
6710
+ events.push({
6711
+ type: "monitor:long_generating",
6712
+ agentKey,
6713
+ elapsedSec,
6714
+ timestamp: now,
6715
+ message: `${agentKey} has been generating for ${Math.round(elapsedSec / 60)}min`
6716
+ });
6717
+ }
6718
+ }
6719
+ }
6720
+ } else {
6721
+ this.generatingStartTimes.delete(agentKey);
6722
+ }
6723
+ return events;
6724
+ }
6725
+ /** 쿨다운 체크 — 같은 알림을 너무 자주 보내지 않도록 */
6726
+ shouldAlert(key, now) {
6727
+ const last = this.lastAlertTime.get(key) || 0;
6728
+ if (now - last > this.config.alertCooldownSec * 1e3) {
6729
+ this.lastAlertTime.set(key, now);
6730
+ return true;
6731
+ }
6732
+ return false;
6733
+ }
6734
+ /** 리셋 (에이전트 종료/재시작 시) */
6735
+ reset(agentKey) {
6736
+ if (agentKey) {
6737
+ this.generatingStartTimes.delete(agentKey);
6738
+ for (const k of this.lastAlertTime.keys()) {
6739
+ if (k.startsWith(agentKey)) this.lastAlertTime.delete(k);
6740
+ }
6741
+ } else {
6742
+ this.generatingStartTimes.clear();
6743
+ this.lastAlertTime.clear();
6744
+ }
6745
+ }
6746
+ };
6747
+ }
6748
+ });
6749
+
6750
+ // src/providers/cli-provider-instance.ts
6751
+ var path8, CliProviderInstance;
6752
+ var init_cli_provider_instance = __esm({
6753
+ "src/providers/cli-provider-instance.ts"() {
6754
+ "use strict";
6755
+ path8 = __toESM(require("path"));
6756
+ init_provider_cli_adapter();
6757
+ init_status_monitor();
6758
+ CliProviderInstance = class {
6759
+ constructor(provider2, workingDir, cliArgs = []) {
6760
+ this.provider = provider2;
6761
+ this.workingDir = workingDir;
6762
+ this.cliArgs = cliArgs;
6763
+ this.type = provider2.type;
6764
+ this.adapter = new ProviderCliAdapter(provider2, workingDir, cliArgs);
6765
+ this.monitor = new StatusMonitor();
6766
+ }
6767
+ type;
6768
+ category = "cli";
6769
+ adapter;
6770
+ context = null;
6771
+ events = [];
6772
+ lastStatus = "starting";
6773
+ generatingStartedAt = 0;
6774
+ settings = {};
6775
+ monitor;
6776
+ // ─── Lifecycle ─────────────────────────────────
6777
+ async init(context) {
6778
+ this.context = context;
6779
+ this.settings = context.settings || {};
6780
+ this.monitor.updateConfig({
6781
+ approvalAlert: this.settings.approvalAlert !== false,
6782
+ longGeneratingAlert: this.settings.longGeneratingAlert !== false,
6783
+ longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
6784
+ });
6785
+ if (context.bridge) {
6786
+ this.adapter.setBridge(context.bridge);
6787
+ }
6788
+ if (context.onPtyData) {
6789
+ this.adapter.setOnPtyData(context.onPtyData);
6790
+ }
6791
+ this.adapter.setOnStatusChange(() => {
6792
+ this.detectStatusTransition();
6793
+ });
6794
+ await this.adapter.spawn();
6795
+ }
6796
+ async onTick() {
6797
+ }
6798
+ getState() {
6799
+ const adapterStatus = this.adapter.getStatus();
6800
+ const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
6801
+ const recentMessages = adapterStatus.messages.slice(-50).map((m) => ({
6802
+ role: m.role,
6803
+ content: m.content.length > 2e3 ? m.content.slice(0, 2e3) + "\n... (truncated)" : m.content,
6804
+ timestamp: m.timestamp
6805
+ }));
6806
+ const partial = this.adapter.getPartialResponse();
6807
+ if (adapterStatus.status === "generating" && partial) {
6808
+ const cleaned = partial.trim();
6809
+ if (cleaned && cleaned !== "(generating...)") {
6810
+ recentMessages.push({
6811
+ role: "assistant",
6812
+ content: (cleaned.length > 2e3 ? cleaned.slice(0, 2e3) + "..." : cleaned) + "...",
6813
+ timestamp: Date.now()
6814
+ });
6815
+ }
6816
+ }
6817
+ return {
6818
+ type: this.type,
6819
+ name: this.provider.name,
6820
+ category: "cli",
6821
+ status: adapterStatus.status,
6822
+ mode: this.settings.mode || "terminal",
6823
+ activeChat: {
6824
+ id: `${this.type}_${this.workingDir}`,
6825
+ title: `${this.provider.name} \xB7 ${dirName}`,
6826
+ status: adapterStatus.status,
6827
+ messages: recentMessages,
6828
+ activeModal: adapterStatus.activeModal,
6829
+ inputContent: ""
6830
+ },
6831
+ workingDir: this.workingDir,
6832
+ instanceId: `${this.type}_${require("crypto").createHash("md5").update(path8.resolve(this.workingDir)).digest("hex").slice(0, 8)}`,
6833
+ lastUpdated: Date.now(),
6834
+ settings: this.settings,
6835
+ pendingEvents: this.flushEvents()
6836
+ };
6837
+ }
6838
+ onEvent(event, data) {
6839
+ if (event === "send_message" && data?.text) {
6840
+ this.adapter.sendMessage(data.text);
6841
+ } else if (event === "bridge_connected" && data?.bridge) {
6842
+ this.adapter.setBridge(data.bridge);
6843
+ }
6844
+ }
6845
+ dispose() {
6846
+ this.adapter.shutdown();
6847
+ this.monitor.reset();
6848
+ }
6849
+ // ─── 상태 전이 감지 (daemon-status.ts에서 이동) ──────
6850
+ detectStatusTransition() {
6851
+ const now = Date.now();
6852
+ const adapterStatus = this.adapter.getStatus();
6853
+ const newStatus = adapterStatus.status;
6854
+ const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
6855
+ const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
6856
+ if (newStatus !== this.lastStatus) {
6857
+ if (this.lastStatus === "idle" && newStatus === "generating") {
6858
+ this.generatingStartedAt = now;
6859
+ this.pushEvent({ event: "agent:generating_started", chatTitle, timestamp: now });
6860
+ } else if (newStatus === "waiting_approval") {
6861
+ if (!this.generatingStartedAt) this.generatingStartedAt = now;
6862
+ this.pushEvent({
6863
+ event: "agent:waiting_approval",
6864
+ chatTitle,
6865
+ timestamp: now,
6866
+ modalMessage: adapterStatus.activeModal?.message
6867
+ });
6868
+ } else if (newStatus === "idle" && (this.lastStatus === "generating" || this.lastStatus === "waiting_approval")) {
6869
+ const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
6870
+ this.pushEvent({ event: "agent:generating_completed", chatTitle, duration, timestamp: now });
6871
+ this.generatingStartedAt = 0;
6872
+ } else if (newStatus === "stopped") {
6873
+ this.pushEvent({ event: "agent:stopped", chatTitle, timestamp: now });
6874
+ }
6875
+ this.lastStatus = newStatus;
6876
+ }
6877
+ const agentKey = `${this.type}:cli`;
6878
+ const monitorEvents = this.monitor.check(agentKey, newStatus, now);
6879
+ for (const me of monitorEvents) {
6880
+ this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
6881
+ }
6882
+ }
6883
+ pushEvent(event) {
6884
+ this.events.push(event);
6885
+ if (this.events.length > 50) this.events = this.events.slice(-50);
6886
+ }
6887
+ flushEvents() {
6888
+ const events = [...this.events];
6889
+ this.events = [];
6890
+ return events;
6891
+ }
6892
+ // ─── Adapter 접근 (하위 호환) ──────────────────
6893
+ getAdapter() {
6894
+ return this.adapter;
6895
+ }
6896
+ get cliType() {
6897
+ return this.type;
6898
+ }
6899
+ get cliName() {
6900
+ return this.provider.name;
6901
+ }
6902
+ };
6903
+ }
6904
+ });
6905
+
6906
+ // src/daemon-cli.ts
6907
+ var os10, path9, import_chalk, DaemonCliManager;
6908
+ var init_daemon_cli = __esm({
6909
+ "src/daemon-cli.ts"() {
6910
+ "use strict";
6911
+ os10 = __toESM(require("os"));
6912
+ path9 = __toESM(require("path"));
6157
6913
  import_chalk = __toESM(require("chalk"));
6158
6914
  init_provider_cli_adapter();
6159
6915
  init_cli_detector();
6160
6916
  init_config();
6917
+ init_cli_provider_instance();
6161
6918
  DaemonCliManager = class {
6162
6919
  adapters = /* @__PURE__ */ new Map();
6163
6920
  deps;
@@ -6191,8 +6948,8 @@ var init_daemon_cli = __esm({
6191
6948
  }
6192
6949
  // ─── 세션 시작/중지 ──────────────────────────────
6193
6950
  async startSession(cliType, workingDir, cliArgs) {
6194
- const trimmed = (workingDir || process.cwd()).trim();
6195
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os9.homedir()) : path7.resolve(trimmed);
6951
+ const trimmed = (workingDir || os10.homedir()).trim();
6952
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path9.resolve(trimmed);
6196
6953
  const cliInfo = await detectCLI(cliType);
6197
6954
  if (!cliInfo) throw new Error(`${cliType} not found`);
6198
6955
  const key = this.getCliKey(cliType, resolvedDir);
@@ -6200,34 +6957,57 @@ var init_daemon_cli = __esm({
6200
6957
  console.log(import_chalk.default.yellow(` \u26A1 CLI ${cliType} already running in ${resolvedDir}`));
6201
6958
  return;
6202
6959
  }
6960
+ const typeMap = {
6961
+ "claude-code": "claude-cli",
6962
+ "codex": "codex-cli"
6963
+ };
6964
+ const normalizedType = typeMap[cliType] || cliType;
6965
+ const provider2 = this.providerLoader.get(normalizedType);
6203
6966
  console.log(import_chalk.default.yellow(` \u26A1 Starting CLI ${cliType} in ${resolvedDir}...`));
6204
- const adapter = this.createAdapter(cliType, resolvedDir, cliArgs);
6205
- await adapter.spawn();
6206
- const bridge = this.deps.getBridge();
6207
- if (bridge && typeof adapter.setBridge === "function") {
6208
- adapter.setBridge(bridge);
6967
+ if (provider2) {
6968
+ console.log(import_chalk.default.cyan(` \u{1F4E6} Using provider: ${provider2.name} (${provider2.type})`));
6209
6969
  }
6210
- adapter.setOnStatusChange(() => {
6211
- this.deps.onStatusChange();
6212
- const status = adapter.getStatus?.();
6213
- if (status?.status === "stopped") {
6214
- setTimeout(() => {
6215
- if (this.adapters.get(key) === adapter) {
6216
- this.adapters.delete(key);
6217
- this.deps.removeAgentTracking(key);
6218
- console.log(import_chalk.default.yellow(` \u{1F9F9} Auto-cleaned stopped CLI: ${adapter.cliType}`));
6219
- this.deps.onStatusChange();
6220
- }
6221
- }, 3e3);
6222
- }
6223
- });
6224
- if (typeof adapter.setOnPtyData === "function") {
6225
- adapter.setOnPtyData((data) => {
6226
- this.deps.getP2p()?.broadcastPtyOutput(key, data);
6970
+ const instanceManager = this.deps.getInstanceManager();
6971
+ if (provider2 && instanceManager) {
6972
+ const cliInstance = new CliProviderInstance(provider2, resolvedDir, cliArgs);
6973
+ await instanceManager.addInstance(key, cliInstance, {
6974
+ bridge: this.deps.getBridge(),
6975
+ settings: {},
6976
+ onPtyData: (data) => {
6977
+ this.deps.getP2p()?.broadcastPtyOutput(key, data);
6978
+ }
6979
+ });
6980
+ this.adapters.set(key, cliInstance.getAdapter());
6981
+ console.log(import_chalk.default.green(` \u2713 CLI started: ${cliInfo.displayName} v${cliInfo.version || "unknown"} in ${resolvedDir}`));
6982
+ } else {
6983
+ const adapter = this.createAdapter(cliType, resolvedDir, cliArgs);
6984
+ await adapter.spawn();
6985
+ const bridge = this.deps.getBridge();
6986
+ if (bridge && typeof adapter.setBridge === "function") {
6987
+ adapter.setBridge(bridge);
6988
+ }
6989
+ adapter.setOnStatusChange(() => {
6990
+ this.deps.onStatusChange();
6991
+ const status = adapter.getStatus?.();
6992
+ if (status?.status === "stopped") {
6993
+ setTimeout(() => {
6994
+ if (this.adapters.get(key) === adapter) {
6995
+ this.adapters.delete(key);
6996
+ this.deps.removeAgentTracking(key);
6997
+ console.log(import_chalk.default.yellow(` \u{1F9F9} Auto-cleaned stopped CLI: ${adapter.cliType}`));
6998
+ this.deps.onStatusChange();
6999
+ }
7000
+ }, 3e3);
7001
+ }
6227
7002
  });
7003
+ if (typeof adapter.setOnPtyData === "function") {
7004
+ adapter.setOnPtyData((data) => {
7005
+ this.deps.getP2p()?.broadcastPtyOutput(key, data);
7006
+ });
7007
+ }
7008
+ this.adapters.set(key, adapter);
7009
+ console.log(import_chalk.default.green(` \u2713 CLI started: ${cliInfo.displayName} v${cliInfo.version || "unknown"} in ${resolvedDir}`));
6228
7010
  }
6229
- this.adapters.set(key, adapter);
6230
- console.log(import_chalk.default.green(` \u2713 CLI started: ${cliInfo.displayName} v${cliInfo.version || "unknown"} in ${resolvedDir}`));
6231
7011
  try {
6232
7012
  addCliHistory({ cliType, dir: resolvedDir, cliArgs });
6233
7013
  } catch (e) {
@@ -6241,6 +7021,7 @@ var init_daemon_cli = __esm({
6241
7021
  adapter.shutdown();
6242
7022
  this.adapters.delete(key);
6243
7023
  this.deps.removeAgentTracking(key);
7024
+ this.deps.getInstanceManager()?.removeInstance(key);
6244
7025
  console.log(import_chalk.default.yellow(` \u{1F6D1} CLI Agent stopped: ${adapter.cliType} in ${adapter.workingDir}`));
6245
7026
  this.deps.onStatusChange();
6246
7027
  }
@@ -6268,7 +7049,8 @@ var init_daemon_cli = __esm({
6268
7049
  switch (cmd) {
6269
7050
  case "launch_cli": {
6270
7051
  const cliType = args?.cliType;
6271
- const dir = args?.dir || process.cwd();
7052
+ const defaultedToHome = !args?.dir;
7053
+ const dir = args?.dir || os10.homedir();
6272
7054
  if (!cliType) throw new Error("cliType required");
6273
7055
  const key = this.getCliKey(cliType, dir);
6274
7056
  if (!this.adapters.has(key)) {
@@ -6286,7 +7068,7 @@ var init_daemon_cli = __esm({
6286
7068
  console.error(import_chalk.default.red(` \u2717 Failed to save recent workspace: ${e}`));
6287
7069
  }
6288
7070
  }
6289
- return { success: true, cliType, dir, id: key };
7071
+ return { success: true, cliType, dir, id: key, defaultedToHome };
6290
7072
  }
6291
7073
  case "stop_cli": {
6292
7074
  const cliType = args?.cliType;
@@ -6490,99 +7272,6 @@ var init_provider_instance_manager = __esm({
6490
7272
  }
6491
7273
  });
6492
7274
 
6493
- // src/providers/status-monitor.ts
6494
- var DEFAULT_MONITOR_CONFIG, StatusMonitor;
6495
- var init_status_monitor = __esm({
6496
- "src/providers/status-monitor.ts"() {
6497
- "use strict";
6498
- DEFAULT_MONITOR_CONFIG = {
6499
- approvalAlert: true,
6500
- longGeneratingAlert: true,
6501
- longGeneratingThresholdSec: 180,
6502
- // 3분
6503
- alertCooldownSec: 60
6504
- // 1분 쿨다운
6505
- };
6506
- StatusMonitor = class {
6507
- config;
6508
- lastAlertTime = /* @__PURE__ */ new Map();
6509
- generatingStartTimes = /* @__PURE__ */ new Map();
6510
- constructor(config) {
6511
- this.config = { ...DEFAULT_MONITOR_CONFIG, ...config };
6512
- }
6513
- /** 설정 업데이트 (Provider Settings에서 호출) */
6514
- updateConfig(partial) {
6515
- Object.assign(this.config, partial);
6516
- }
6517
- /** 현재 설정 반환 */
6518
- getConfig() {
6519
- return { ...this.config };
6520
- }
6521
- /**
6522
- * 상태 전이 체크 → 알림 이벤트 배열 반환
6523
- * 각 onTick()에서 호출.
6524
- */
6525
- check(agentKey, status, now) {
6526
- const events = [];
6527
- if (this.config.approvalAlert && status === "waiting_approval") {
6528
- if (this.shouldAlert(agentKey + ":approval", now)) {
6529
- events.push({
6530
- type: "monitor:approval_waiting",
6531
- agentKey,
6532
- timestamp: now,
6533
- message: `${agentKey} is waiting for approval`
6534
- });
6535
- }
6536
- }
6537
- if (status === "generating" || status === "streaming") {
6538
- if (!this.generatingStartTimes.has(agentKey)) {
6539
- this.generatingStartTimes.set(agentKey, now);
6540
- }
6541
- if (this.config.longGeneratingAlert) {
6542
- const startedAt = this.generatingStartTimes.get(agentKey);
6543
- const elapsedSec = Math.round((now - startedAt) / 1e3);
6544
- if (elapsedSec > this.config.longGeneratingThresholdSec) {
6545
- if (this.shouldAlert(agentKey + ":long_gen", now)) {
6546
- events.push({
6547
- type: "monitor:long_generating",
6548
- agentKey,
6549
- elapsedSec,
6550
- timestamp: now,
6551
- message: `${agentKey} has been generating for ${Math.round(elapsedSec / 60)}min`
6552
- });
6553
- }
6554
- }
6555
- }
6556
- } else {
6557
- this.generatingStartTimes.delete(agentKey);
6558
- }
6559
- return events;
6560
- }
6561
- /** 쿨다운 체크 — 같은 알림을 너무 자주 보내지 않도록 */
6562
- shouldAlert(key, now) {
6563
- const last = this.lastAlertTime.get(key) || 0;
6564
- if (now - last > this.config.alertCooldownSec * 1e3) {
6565
- this.lastAlertTime.set(key, now);
6566
- return true;
6567
- }
6568
- return false;
6569
- }
6570
- /** 리셋 (에이전트 종료/재시작 시) */
6571
- reset(agentKey) {
6572
- if (agentKey) {
6573
- this.generatingStartTimes.delete(agentKey);
6574
- for (const k of this.lastAlertTime.keys()) {
6575
- if (k.startsWith(agentKey)) this.lastAlertTime.delete(k);
6576
- }
6577
- } else {
6578
- this.generatingStartTimes.clear();
6579
- this.lastAlertTime.clear();
6580
- }
6581
- }
6582
- };
6583
- }
6584
- });
6585
-
6586
7275
  // src/providers/extension-provider-instance.ts
6587
7276
  var ExtensionProviderInstance;
6588
7277
  var init_extension_provider_instance = __esm({
@@ -6705,11 +7394,11 @@ var init_extension_provider_instance = __esm({
6705
7394
  });
6706
7395
 
6707
7396
  // src/providers/ide-provider-instance.ts
6708
- var os10, crypto, IdeProviderInstance;
7397
+ var os11, crypto, IdeProviderInstance;
6709
7398
  var init_ide_provider_instance = __esm({
6710
7399
  "src/providers/ide-provider-instance.ts"() {
6711
7400
  "use strict";
6712
- os10 = __toESM(require("os"));
7401
+ os11 = __toESM(require("os"));
6713
7402
  crypto = __toESM(require("crypto"));
6714
7403
  init_extension_provider_instance();
6715
7404
  init_status_monitor();
@@ -6735,10 +7424,10 @@ var init_ide_provider_instance = __esm({
6735
7424
  activeFile = null;
6736
7425
  // ─── 자식 Extension Instances ────────────────────
6737
7426
  extensions = /* @__PURE__ */ new Map();
6738
- constructor(provider2) {
7427
+ constructor(provider2, instanceKey) {
6739
7428
  this.type = provider2.type;
6740
7429
  this.provider = provider2;
6741
- this.instanceId = `${provider2.type}_${crypto.createHash("md5").update(os10.hostname() + provider2.type).digest("hex").slice(0, 8)}`;
7430
+ this.instanceId = instanceKey ? `${instanceKey}_${crypto.createHash("md5").update(os11.hostname() + instanceKey).digest("hex").slice(0, 8)}` : `${provider2.type}_${crypto.createHash("md5").update(os11.hostname() + provider2.type).digest("hex").slice(0, 8)}`;
6742
7431
  this.monitor = new StatusMonitor();
6743
7432
  }
6744
7433
  // ─── Lifecycle ─────────────────────────────────
@@ -6869,7 +7558,14 @@ var init_ide_provider_instance = __esm({
6869
7558
  const readChatScript = this.getReadChatScript();
6870
7559
  if (!readChatScript) return;
6871
7560
  try {
6872
- const raw = await cdp2.evaluate(readChatScript, 3e4);
7561
+ let raw = await cdp2.evaluate(readChatScript, 3e4);
7562
+ if (typeof raw === "string") {
7563
+ try {
7564
+ raw = JSON.parse(raw);
7565
+ } catch {
7566
+ return;
7567
+ }
7568
+ }
6873
7569
  if (!raw || typeof raw !== "object") return;
6874
7570
  let { activeModal } = raw;
6875
7571
  if (activeModal) {
@@ -6962,107 +7658,6 @@ var init_ide_provider_instance = __esm({
6962
7658
  }
6963
7659
  });
6964
7660
 
6965
- // src/daemon-logger.ts
6966
- var daemon_logger_exports = {};
6967
- __export(daemon_logger_exports, {
6968
- LOG_DIR_PATH: () => LOG_DIR_PATH,
6969
- LOG_PATH: () => LOG_PATH,
6970
- cdpLogFn: () => cdpLogFn,
6971
- daemonLog: () => daemonLog,
6972
- installGlobalInterceptor: () => installGlobalInterceptor
6973
- });
6974
- function rotateIfNeeded() {
6975
- try {
6976
- const stat = fs5.statSync(LOG_FILE);
6977
- if (stat.size > MAX_LOG_SIZE) {
6978
- const backup = LOG_FILE + ".old";
6979
- try {
6980
- fs5.unlinkSync(backup);
6981
- } catch {
6982
- }
6983
- fs5.renameSync(LOG_FILE, backup);
6984
- }
6985
- } catch {
6986
- }
6987
- }
6988
- function writeToFile(line) {
6989
- try {
6990
- fs5.appendFileSync(LOG_FILE, line + "\n");
6991
- } catch {
6992
- }
6993
- }
6994
- function daemonLog(category, msg) {
6995
- const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${category}] ${msg}`;
6996
- origConsoleLog(line);
6997
- writeToFile(line);
6998
- }
6999
- function cdpLogFn(ideType) {
7000
- return (msg) => {
7001
- const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [CDP:${ideType}] ${msg}`;
7002
- origConsoleLog(line);
7003
- writeToFile(line);
7004
- };
7005
- }
7006
- function installGlobalInterceptor() {
7007
- if (interceptorInstalled) return;
7008
- interceptorInstalled = true;
7009
- const stripAnsi2 = (str) => str.replace(/\x1B\[[0-9;]*m/g, "");
7010
- console.log = (...args) => {
7011
- origConsoleLog(...args);
7012
- try {
7013
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
7014
- const clean = stripAnsi2(msg);
7015
- const line = clean.startsWith("[20") ? clean : `[${(/* @__PURE__ */ new Date()).toISOString()}] ${clean}`;
7016
- writeToFile(line);
7017
- } catch {
7018
- }
7019
- };
7020
- console.error = (...args) => {
7021
- origConsoleError(...args);
7022
- try {
7023
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
7024
- const clean = stripAnsi2(msg);
7025
- writeToFile(`[${(/* @__PURE__ */ new Date()).toISOString()}] [ERROR] ${clean}`);
7026
- } catch {
7027
- }
7028
- };
7029
- console.warn = (...args) => {
7030
- origConsoleWarn(...args);
7031
- try {
7032
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
7033
- const clean = stripAnsi2(msg);
7034
- writeToFile(`[${(/* @__PURE__ */ new Date()).toISOString()}] [WARN] ${clean}`);
7035
- } catch {
7036
- }
7037
- };
7038
- writeToFile(`
7039
- === ADHDev Daemon started at ${(/* @__PURE__ */ new Date()).toISOString()} ===`);
7040
- writeToFile(`Log file: ${LOG_FILE}`);
7041
- }
7042
- var fs5, path8, os11, LOG_DIR, LOG_FILE, MAX_LOG_SIZE, origConsoleLog, origConsoleError, origConsoleWarn, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
7043
- var init_daemon_logger = __esm({
7044
- "src/daemon-logger.ts"() {
7045
- "use strict";
7046
- fs5 = __toESM(require("fs"));
7047
- path8 = __toESM(require("path"));
7048
- os11 = __toESM(require("os"));
7049
- LOG_DIR = process.platform === "darwin" ? path8.join(os11.homedir(), "Library", "Logs", "adhdev") : path8.join(os11.homedir(), ".local", "share", "adhdev", "logs");
7050
- LOG_FILE = path8.join(LOG_DIR, "daemon.log");
7051
- MAX_LOG_SIZE = 10 * 1024 * 1024;
7052
- try {
7053
- fs5.mkdirSync(LOG_DIR, { recursive: true });
7054
- } catch {
7055
- }
7056
- rotateIfNeeded();
7057
- origConsoleLog = console.log.bind(console);
7058
- origConsoleError = console.error.bind(console);
7059
- origConsoleWarn = console.warn.bind(console);
7060
- interceptorInstalled = false;
7061
- LOG_PATH = LOG_FILE;
7062
- LOG_DIR_PATH = LOG_DIR;
7063
- }
7064
- });
7065
-
7066
7661
  // src/adhdev-daemon.ts
7067
7662
  var adhdev_daemon_exports = {};
7068
7663
  __export(adhdev_daemon_exports, {
@@ -7071,9 +7666,9 @@ __export(adhdev_daemon_exports, {
7071
7666
  stopDaemon: () => stopDaemon
7072
7667
  });
7073
7668
  function getDaemonPidFile() {
7074
- const dir = path9.join(os12.homedir(), ".adhdev");
7669
+ const dir = path10.join(os12.homedir(), ".adhdev");
7075
7670
  if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
7076
- return path9.join(dir, "daemon.pid");
7671
+ return path10.join(dir, "daemon.pid");
7077
7672
  }
7078
7673
  function writeDaemonPid(pid) {
7079
7674
  fs6.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
@@ -7109,7 +7704,7 @@ function stopDaemon() {
7109
7704
  return false;
7110
7705
  }
7111
7706
  }
7112
- var os12, fs6, path9, crypto2, import_chalk2, DANGEROUS_PATTERNS, AdhdevDaemon;
7707
+ var os12, fs6, path10, crypto2, import_chalk2, DANGEROUS_PATTERNS, AdhdevDaemon;
7113
7708
  var init_adhdev_daemon = __esm({
7114
7709
  "src/adhdev-daemon.ts"() {
7115
7710
  "use strict";
@@ -7129,9 +7724,10 @@ var init_adhdev_daemon = __esm({
7129
7724
  init_provider_instance_manager();
7130
7725
  init_ide_provider_instance();
7131
7726
  init_ipc_protocol();
7727
+ init_daemon_logger();
7132
7728
  os12 = __toESM(require("os"));
7133
7729
  fs6 = __toESM(require("fs"));
7134
- path9 = __toESM(require("path"));
7730
+ path10 = __toESM(require("path"));
7135
7731
  crypto2 = __toESM(require("crypto"));
7136
7732
  import_chalk2 = __toESM(require("chalk"));
7137
7733
  DANGEROUS_PATTERNS = [
@@ -7173,7 +7769,8 @@ var init_adhdev_daemon = __esm({
7173
7769
  getBridge: () => this.bridge,
7174
7770
  getP2p: () => this.p2p,
7175
7771
  onStatusChange: () => this.statusReporter?.onStatusChange(),
7176
- removeAgentTracking: (key) => this.statusReporter?.removeAgentTracking(key)
7772
+ removeAgentTracking: (key) => this.statusReporter?.removeAgentTracking(key),
7773
+ getInstanceManager: () => this.instanceManager
7177
7774
  }, this.providerLoader);
7178
7775
  }
7179
7776
  async start(options = {}) {
@@ -7328,8 +7925,8 @@ var init_adhdev_daemon = __esm({
7328
7925
  });
7329
7926
  this.p2p.onStateChange((state) => {
7330
7927
  if (state === "connected") {
7331
- console.log("[P2P] Peer connected \u2192 sending immediate full status report");
7332
- this.statusReporter?.sendUnifiedStatusReport().catch((e) => console.warn("[P2P] Immediate status report failed:", e?.message));
7928
+ LOG.info("P2P", "Peer connected \u2192 sending immediate full status report");
7929
+ this.statusReporter?.sendUnifiedStatusReport().catch((e) => LOG.warn("P2P", `Immediate status report failed: ${e?.message}`));
7333
7930
  }
7334
7931
  });
7335
7932
  let ssDebugCount = 0;
@@ -7340,18 +7937,18 @@ var init_adhdev_daemon = __esm({
7340
7937
  if (!active || !cdp2) return;
7341
7938
  ssDebugCount++;
7342
7939
  if (ssDebugCount <= 3 || ssDebugCount % 25 === 0) {
7343
- console.log(`[SS] Capturing screenshot... (tick ${ssDebugCount}, active=${active}, cdp=true)`);
7940
+ LOG.debug("Screenshot", `Capturing... (tick ${ssDebugCount}, active=${active}, cdp=true)`);
7344
7941
  }
7345
7942
  try {
7346
7943
  const buf = await cdp2.captureScreenshot();
7347
7944
  if (buf) {
7348
7945
  const sent = this.p2p.sendScreenshot(buf.toString("base64"));
7349
- if (ssDebugCount <= 3) console.log(`[SS] Screenshot sent: ${buf.length} bytes, delivered=${sent}`);
7946
+ if (ssDebugCount <= 3) LOG.debug("Screenshot", `sent: ${buf.length} bytes, delivered=${sent}`);
7350
7947
  } else {
7351
- if (ssDebugCount <= 5) console.log(`[SS] captureScreenshot returned null`);
7948
+ if (ssDebugCount <= 5) LOG.debug("Screenshot", "captureScreenshot returned null");
7352
7949
  }
7353
7950
  } catch (e) {
7354
- if (ssDebugCount <= 5) console.log(`[SS] Screenshot error: ${e?.message}`);
7951
+ if (ssDebugCount <= 5) LOG.warn("Screenshot", `error: ${e?.message}`);
7355
7952
  }
7356
7953
  }, 200);
7357
7954
  } else {
@@ -7494,10 +8091,7 @@ var init_adhdev_daemon = __esm({
7494
8091
  }
7495
8092
  }
7496
8093
  async handleCommand(msg, cmd, args) {
7497
- console.log(import_chalk2.default.magenta(` \u2699 Command: ${cmd}`));
7498
- if (cmd.startsWith("agent_stream") && args?._targetInstance) {
7499
- console.log(import_chalk2.default.yellow(` \u{1F3AF} Target: ${args._targetType}:${args._targetInstance.split("_")[0]} agent=${args.agentType || ""}`));
7500
- }
8094
+ LOG.info("Command", `${cmd}${args?._targetInstance ? ` \u2192 ${args._targetType}:${args._targetInstance.split("_")[0]}` : ""}`);
7501
8095
  try {
7502
8096
  switch (cmd) {
7503
8097
  case "open_panel":
@@ -7569,17 +8163,22 @@ var init_adhdev_daemon = __esm({
7569
8163
  return { success: true, nickname };
7570
8164
  }
7571
8165
  case "get_daemon_logs": {
7572
- const lines = parseInt(data.lines) || 100;
8166
+ const count = parseInt(data.lines) || 100;
8167
+ const minLevel = data.minLevel || "info";
7573
8168
  try {
7574
- const logPath = require("path").join(require("os").tmpdir(), "adhdev-daemon.log");
8169
+ const { getRecentLogs: getRecentLogs2, LOG_PATH: LOG_PATH2 } = (init_daemon_logger(), __toCommonJS(daemon_logger_exports));
8170
+ const entries = getRecentLogs2(count, minLevel);
8171
+ if (entries.length > 0) {
8172
+ return { success: true, entries, totalBuffered: entries.length };
8173
+ }
7575
8174
  const logFs = require("fs");
7576
- if (logFs.existsSync(logPath)) {
7577
- const content = logFs.readFileSync(logPath, "utf-8");
8175
+ if (logFs.existsSync(LOG_PATH2)) {
8176
+ const content = logFs.readFileSync(LOG_PATH2, "utf-8");
7578
8177
  const allLines = content.split("\n");
7579
- const recent = allLines.slice(-lines).join("\n");
8178
+ const recent = allLines.slice(-count).join("\n");
7580
8179
  return { success: true, logs: recent, totalLines: allLines.length };
7581
8180
  }
7582
- return { success: true, logs: "", totalLines: 0 };
8181
+ return { success: true, entries: [], totalBuffered: 0 };
7583
8182
  } catch (e) {
7584
8183
  return { success: false, error: e.message };
7585
8184
  }
@@ -7612,7 +8211,7 @@ var init_adhdev_daemon = __esm({
7612
8211
  case "launch_ide": {
7613
8212
  const launchArgs = { ...args, ideId: args?.ideId || args?.ideType };
7614
8213
  const ideKey = launchArgs.ideId;
7615
- console.log(`[launch_ide] target=${ideKey || "auto"}`);
8214
+ LOG.info("LaunchIDE", `target=${ideKey || "auto"}`);
7616
8215
  const result = await launchWithCdp(launchArgs);
7617
8216
  if (result.success && result.port && result.ideId && !this.cdpManagers.has(result.ideId)) {
7618
8217
  console.log(import_chalk2.default.cyan(`[launch_ide] Connecting CDP for ${result.ideId} on port ${result.port}...`));
@@ -7621,6 +8220,12 @@ var init_adhdev_daemon = __esm({
7621
8220
  });
7622
8221
  const connected = await manager.connect();
7623
8222
  if (connected) {
8223
+ const enabledExtProviders = this.providerLoader.getEnabledExtensionProviders(result.ideId).map((p) => ({
8224
+ agentType: p.type,
8225
+ extensionId: p.extensionId || "",
8226
+ extensionIdPattern: p.extensionIdPattern
8227
+ }));
8228
+ manager.setExtensionProviders(enabledExtProviders);
7624
8229
  this.cdpManagers.set(result.ideId, manager);
7625
8230
  console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${result.ideId} (port ${result.port})`));
7626
8231
  console.log(import_chalk2.default.green(` \u{1F4E1} CDP: ${this.cdpManagers.size} IDE(s) connected`));
@@ -7702,6 +8307,37 @@ var init_adhdev_daemon = __esm({
7702
8307
  if (this.agentStreamTimer) return;
7703
8308
  this.agentStreamTimer = setInterval(async () => {
7704
8309
  if (!this.agentStreamManager || this.cdpManagers.size === 0) return;
8310
+ for (const [ideType, cdp2] of this.cdpManagers) {
8311
+ const enabledExtProviders = this.providerLoader.getEnabledExtensionProviders(ideType).map((p) => ({
8312
+ agentType: p.type,
8313
+ extensionId: p.extensionId || "",
8314
+ extensionIdPattern: p.extensionIdPattern
8315
+ }));
8316
+ cdp2.setExtensionProviders(enabledExtProviders);
8317
+ const ideInstance = this.instanceManager.getInstance(`ide:${ideType}`);
8318
+ if (ideInstance?.getExtensionTypes && ideInstance?.addExtension && ideInstance?.removeExtension) {
8319
+ const currentExtTypes = new Set(ideInstance.getExtensionTypes());
8320
+ const enabledExtTypes = new Set(
8321
+ this.providerLoader.getEnabledByCategory("extension", ideType).map((p) => p.type)
8322
+ );
8323
+ for (const extType of currentExtTypes) {
8324
+ if (!enabledExtTypes.has(extType)) {
8325
+ ideInstance.removeExtension(extType);
8326
+ LOG.info("AgentStream", `Extension removed: ${extType} (disabled for ${ideType})`);
8327
+ }
8328
+ }
8329
+ for (const extType of enabledExtTypes) {
8330
+ if (!currentExtTypes.has(extType)) {
8331
+ const extProvider = this.providerLoader.get(extType);
8332
+ if (extProvider) {
8333
+ const extSettings = this.providerLoader.getSettings(extType);
8334
+ ideInstance.addExtension(extProvider, extSettings);
8335
+ LOG.info("AgentStream", `Extension added: ${extType} (enabled for ${ideType})`);
8336
+ }
8337
+ }
8338
+ }
8339
+ }
8340
+ }
7705
8341
  if (this._agentStreamCdpIdeType) {
7706
8342
  const cdp2 = this.cdpManagers.get(this._agentStreamCdpIdeType);
7707
8343
  if (cdp2?.isConnected) {
@@ -7723,7 +8359,7 @@ var init_adhdev_daemon = __esm({
7723
8359
  if (discovered.length > 0) {
7724
8360
  this._agentStreamCdpIdeType = ideType;
7725
8361
  await this.agentStreamManager.switchActiveAgent(cdp2, discovered[0].agentType);
7726
- console.log(`[AgentStream] Auto-activated: ${discovered[0].agentType} (${ideType})`);
8362
+ LOG.info("AgentStream", `Auto-activated: ${discovered[0].agentType} (${ideType})`);
7727
8363
  await this.agentStreamManager.syncAgentSessions(cdp2);
7728
8364
  const streams = await this.agentStreamManager.collectAgentStreams(cdp2);
7729
8365
  this.statusReporter?.updateAgentStreams(ideType, streams);
@@ -7735,6 +8371,39 @@ var init_adhdev_daemon = __esm({
7735
8371
  }
7736
8372
  }, 5e3);
7737
8373
  }
8374
+ /**
8375
+ * CdpManager 초기화 공통 로직 — extension provider 등록, ideType 설정, ProviderInstance 생성
8376
+ * @param ideType provider 기반 IDE 타입 (e.g., 'antigravity', 'cursor')
8377
+ * @param manager 연결된 CdpManager
8378
+ */
8379
+ setupCdpManager(ideType, manager) {
8380
+ const enabledExtProviders = this.providerLoader.getEnabledExtensionProviders(ideType).map((p) => ({
8381
+ agentType: p.type,
8382
+ extensionId: p.extensionId || "",
8383
+ extensionIdPattern: p.extensionIdPattern
8384
+ }));
8385
+ manager.setExtensionProviders(enabledExtProviders);
8386
+ if (this.ideType === "unknown") {
8387
+ this.ideType = ideType;
8388
+ }
8389
+ const ideProvider = this.providerLoader.get(ideType);
8390
+ if (ideProvider) {
8391
+ const managerKey = [...this.cdpManagers.entries()].find(([, m]) => m === manager)?.[0] || ideType;
8392
+ const ideInstance = new IdeProviderInstance(ideProvider, managerKey !== ideType ? managerKey : void 0);
8393
+ const resolvedSettings = this.providerLoader.getSettings(ideType);
8394
+ this.instanceManager.addInstance(`ide:${managerKey}`, ideInstance, {
8395
+ cdp: manager,
8396
+ bridge: this.bridge || void 0,
8397
+ settings: resolvedSettings
8398
+ }).then(async () => {
8399
+ const extensionProviders = this.providerLoader.getEnabledByCategory("extension", ideType);
8400
+ for (const extProvider of extensionProviders) {
8401
+ const extSettings = this.providerLoader.getSettings(extProvider.type);
8402
+ await ideInstance.addExtension(extProvider, extSettings);
8403
+ }
8404
+ }).catch((e) => console.warn(`[Instance] Failed to init IDE instance ${managerKey}:`, e?.message));
8405
+ }
8406
+ }
7738
8407
  async initCdp() {
7739
8408
  const providerCdpMap = this.providerLoader.getCdpPortMap();
7740
8409
  const cdpPortMap = {};
@@ -7755,49 +8424,87 @@ var init_adhdev_daemon = __esm({
7755
8424
  const enabledIdes = loadConfig().enabledIdes || [];
7756
8425
  const filteredPorts = enabledIdes.length > 0 ? portsToTry.filter((p) => enabledIdes.includes(p.ide)) : portsToTry;
7757
8426
  for (const { port, ide } of filteredPorts) {
7758
- const manager = new DaemonCdpManager(port, (msg) => {
7759
- console.log(`[CDP:${ide}] ${msg}`);
7760
- });
7761
- const connected = await manager.connect();
7762
- if (connected) {
7763
- const actualPort = manager.getPort();
7764
- const actualIde = Object.entries(cdpPortMap).find(([, p]) => p === actualPort)?.[0] || ide;
7765
- this.cdpManagers.set(actualIde, manager);
7766
- console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${actualIde} (port ${actualPort})`));
7767
- if (this.ideType === "unknown") {
7768
- this.ideType = actualIde;
8427
+ const allTargets = await DaemonCdpManager.listAllTargets(port);
8428
+ if (allTargets.length === 0) {
8429
+ const manager = new DaemonCdpManager(port, (msg) => {
8430
+ console.log(`[CDP:${ide}] ${msg}`);
8431
+ });
8432
+ const connected = await manager.connect();
8433
+ if (connected) {
8434
+ this.cdpManagers.set(ide, manager);
8435
+ console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${ide} (port ${port})`));
8436
+ this.setupCdpManager(ide, manager);
7769
8437
  }
7770
- const ideProvider = this.providerLoader.get(actualIde);
7771
- if (ideProvider) {
7772
- const ideInstance = new IdeProviderInstance(ideProvider);
7773
- const resolvedSettings = this.providerLoader.getSettings(actualIde);
7774
- this.instanceManager.addInstance(`ide:${actualIde}`, ideInstance, {
7775
- cdp: manager,
7776
- bridge: this.bridge || void 0,
7777
- settings: resolvedSettings
7778
- }).then(async () => {
7779
- const extensionProviders = this.providerLoader.getAll().filter((p) => p.category === "extension");
7780
- for (const extProvider of extensionProviders) {
7781
- const extSettings = this.providerLoader.getSettings(extProvider.type);
7782
- await ideInstance.addExtension(extProvider, extSettings);
7783
- }
7784
- }).catch((e) => console.warn(`[Instance] Failed to init IDE instance ${actualIde}:`, e?.message));
8438
+ continue;
8439
+ }
8440
+ for (let i = 0; i < allTargets.length; i++) {
8441
+ const target = allTargets[i];
8442
+ let managerKey;
8443
+ if (allTargets.length === 1) {
8444
+ managerKey = ide;
8445
+ } else {
8446
+ const workspaceName = (target.title || "").split(" \u2014 ")[0].trim() || `window_${i}`;
8447
+ managerKey = `${ide}_${workspaceName}`;
8448
+ }
8449
+ if (this.cdpManagers.has(managerKey)) continue;
8450
+ const manager = new DaemonCdpManager(port, (msg) => {
8451
+ console.log(`[CDP:${managerKey}] ${msg}`);
8452
+ }, target.id);
8453
+ const connected = await manager.connect();
8454
+ if (connected) {
8455
+ this.cdpManagers.set(managerKey, manager);
8456
+ console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${managerKey} (port ${port}, page "${target.title}")`));
8457
+ this.setupCdpManager(ide, manager);
7785
8458
  }
7786
8459
  }
7787
8460
  }
7788
8461
  if (this.cdpManagers.size > 0) {
7789
8462
  console.log(import_chalk2.default.green(` \u{1F4E1} CDP: ${this.cdpManagers.size} IDE(s) connected`));
7790
- this.cdpDiscoveryTimer = setInterval(async () => {
7791
- for (const m of this.cdpManagers.values()) {
7792
- if (m.isConnected) {
7793
- await m.discoverAgentWebviews();
7794
- }
7795
- }
7796
- }, 3e4);
7797
8463
  } else {
7798
8464
  console.log(import_chalk2.default.yellow(` \u26A0 CDP not available \u2014 tried ports: ${portsToTry.map((p) => `${p.ide}:${p.port}`).join(", ")}`));
7799
8465
  console.log(import_chalk2.default.yellow(` IDE may need relaunch with --remote-debugging-port`));
7800
8466
  }
8467
+ this.cdpDiscoveryTimer = setInterval(async () => {
8468
+ for (const m of this.cdpManagers.values()) {
8469
+ if (m.isConnected) {
8470
+ await m.discoverAgentWebviews();
8471
+ }
8472
+ }
8473
+ }, 3e4);
8474
+ setInterval(async () => {
8475
+ const portMap = this.providerLoader.getCdpPortMap();
8476
+ for (const [ide, ports] of Object.entries(portMap)) {
8477
+ const primaryPort = ports[0];
8478
+ const alreadyConnected = [...this.cdpManagers.entries()].some(
8479
+ ([key, m]) => m.isConnected && (key === ide || key.startsWith(ide + "_"))
8480
+ );
8481
+ if (alreadyConnected) continue;
8482
+ const targets = await DaemonCdpManager.listAllTargets(primaryPort);
8483
+ if (targets.length === 0) continue;
8484
+ console.log(import_chalk2.default.cyan(`[CDP-Scan] Found ${targets.length} page(s) on ${ide}:${primaryPort}`));
8485
+ for (let i = 0; i < targets.length; i++) {
8486
+ const target = targets[i];
8487
+ let managerKey;
8488
+ if (targets.length === 1) {
8489
+ managerKey = ide;
8490
+ } else {
8491
+ const workspaceName = (target.title || "").split(" \u2014 ")[0].trim() || `window_${i}`;
8492
+ managerKey = `${ide}_${workspaceName}`;
8493
+ }
8494
+ if (this.cdpManagers.has(managerKey)) continue;
8495
+ const manager = new DaemonCdpManager(primaryPort, (msg) => {
8496
+ console.log(`[CDP:${managerKey}] ${msg}`);
8497
+ }, target.id);
8498
+ const connected = await manager.connect();
8499
+ if (connected) {
8500
+ this.cdpManagers.set(managerKey, manager);
8501
+ console.log(import_chalk2.default.green(` \u{1F50D} CDP auto-connected: ${managerKey} (port ${primaryPort}, page "${target.title}")`));
8502
+ this.setupCdpManager(ide, manager);
8503
+ this.startAgentStreamPolling();
8504
+ }
8505
+ }
8506
+ }
8507
+ }, 3e4);
7801
8508
  }
7802
8509
  };
7803
8510
  }
@@ -7945,10 +8652,10 @@ async function installExtension(ide, extension) {
7945
8652
  const buffer = Buffer.from(await res.arrayBuffer());
7946
8653
  const fs7 = await import("fs");
7947
8654
  fs7.writeFileSync(vsixPath, buffer);
7948
- return new Promise((resolve5) => {
8655
+ return new Promise((resolve6) => {
7949
8656
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
7950
8657
  (0, import_child_process2.exec)(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
7951
- resolve5({
8658
+ resolve6({
7952
8659
  extensionId: extension.id,
7953
8660
  marketplaceId: extension.marketplaceId,
7954
8661
  success: !error,
@@ -7979,11 +8686,11 @@ async function installExtension(ide, extension) {
7979
8686
  }
7980
8687
  }
7981
8688
  }
7982
- return new Promise((resolve5) => {
8689
+ return new Promise((resolve6) => {
7983
8690
  const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
7984
8691
  (0, import_child_process2.exec)(cmd, { timeout: 6e4 }, (error, stdout, stderr) => {
7985
8692
  if (error) {
7986
- resolve5({
8693
+ resolve6({
7987
8694
  extensionId: extension.id,
7988
8695
  marketplaceId: extension.marketplaceId,
7989
8696
  success: false,
@@ -7991,7 +8698,7 @@ async function installExtension(ide, extension) {
7991
8698
  error: stderr || error.message
7992
8699
  });
7993
8700
  } else {
7994
- resolve5({
8701
+ resolve6({
7995
8702
  extensionId: extension.id,
7996
8703
  marketplaceId: extension.marketplaceId,
7997
8704
  success: true,
@@ -8167,11 +8874,12 @@ async function quickSetup() {
8167
8874
  console.log(` ${import_chalk3.default.bold("Status:")} ${import_chalk3.default.green("Ready to connect")}`);
8168
8875
  console.log();
8169
8876
  console.log(import_chalk3.default.gray(" Commands:"));
8170
- console.log(import_chalk3.default.gray(" adhdev daemon \u2014 Start daemon (IDE monitor + agent hub)"));
8171
- console.log(import_chalk3.default.gray(" adhdev launch cursor \u2014 Launch IDE with CDP"));
8172
- console.log(import_chalk3.default.gray(" adhdev launch gemini \u2014 Start CLI agent via daemon"));
8877
+ console.log(import_chalk3.default.gray(" adhdev daemon \u2014 Start the main daemon (required for all features)"));
8878
+ console.log(import_chalk3.default.gray(" adhdev launch <ide> \u2014 Launch an IDE with remote control (e.g. cursor)"));
8879
+ console.log(import_chalk3.default.gray(" adhdev launch <agent> \u2014 Start a CLI agent via daemon (e.g. gemini, claude)"));
8880
+ console.log(import_chalk3.default.gray(" adhdev cdp \u2014 Interactive CDP debug console"));
8173
8881
  console.log(import_chalk3.default.gray(" adhdev status \u2014 Check setup status"));
8174
- console.log(import_chalk3.default.gray(" adhdev setup \u2014 Reconfigure"));
8882
+ console.log(import_chalk3.default.gray(" adhdev setup \u2014 Reconfigure ADHDev"));
8175
8883
  console.log();
8176
8884
  await installCliOnly();
8177
8885
  await startDaemonFlow();
@@ -8343,16 +9051,16 @@ async function injectTokenToIDE(ide, connectionToken) {
8343
9051
  try {
8344
9052
  const os13 = await import("os");
8345
9053
  const fs7 = await import("fs");
8346
- const path10 = await import("path");
9054
+ const path11 = await import("path");
8347
9055
  const platform7 = os13.platform();
8348
9056
  const home = os13.homedir();
8349
9057
  const getSettingsPath = (appName2) => {
8350
9058
  if (platform7 === "darwin") {
8351
- return path10.join(home, "Library", "Application Support", appName2, "User", "settings.json");
9059
+ return path11.join(home, "Library", "Application Support", appName2, "User", "settings.json");
8352
9060
  } else if (platform7 === "win32") {
8353
- return path10.join(process.env.APPDATA || path10.join(home, "AppData", "Roaming"), appName2, "User", "settings.json");
9061
+ return path11.join(process.env.APPDATA || path11.join(home, "AppData", "Roaming"), appName2, "User", "settings.json");
8354
9062
  } else {
8355
- return path10.join(home, ".config", appName2, "User", "settings.json");
9063
+ return path11.join(home, ".config", appName2, "User", "settings.json");
8356
9064
  }
8357
9065
  };
8358
9066
  const loader = new ProviderLoader({ logFn: () => {
@@ -8370,7 +9078,7 @@ async function injectTokenToIDE(ide, connectionToken) {
8370
9078
  settings = {};
8371
9079
  }
8372
9080
  } else {
8373
- fs7.mkdirSync(path10.dirname(settingsPath), { recursive: true });
9081
+ fs7.mkdirSync(path11.dirname(settingsPath), { recursive: true });
8374
9082
  }
8375
9083
  settings["adhdev.connectionToken"] = connectionToken;
8376
9084
  settings["adhdev.autoConnect"] = true;
@@ -8392,7 +9100,7 @@ async function startDaemonFlow() {
8392
9100
  {
8393
9101
  type: "confirm",
8394
9102
  name: "startDaemon",
8395
- message: "Start ADHDev Daemon now? (IDE monitor + agent hub)",
9103
+ message: "Start the ADHDev Daemon now? (Required for all agent/IDE routing features)",
8396
9104
  default: true
8397
9105
  }
8398
9106
  ]);
@@ -8406,8 +9114,8 @@ async function startDaemonFlow() {
8406
9114
  const daemon = new AdhdevDaemon2();
8407
9115
  const { execSync: execSync6 } = await import("child_process");
8408
9116
  const os13 = await import("os");
8409
- const path10 = await import("path");
8410
- const logPath = path10.join(os13.homedir(), ".adhdev", "daemon.log");
9117
+ const path11 = await import("path");
9118
+ const logPath = path11.join(os13.homedir(), ".adhdev", "daemon.log");
8411
9119
  try {
8412
9120
  execSync6(`nohup adhdev daemon > "${logPath}" 2>&1 &`, {
8413
9121
  timeout: 3e3,
@@ -8449,10 +9157,11 @@ async function installCliOnly() {
8449
9157
  const isNpx = process.env.npm_execpath?.includes("npx") || process.argv[1]?.includes("npx") || process.argv[1]?.includes("_npx");
8450
9158
  console.log(import_chalk3.default.bold("\n\u{1F527} ADHDev CLI\n"));
8451
9159
  console.log(import_chalk3.default.gray(" The `adhdev` command lets you:"));
8452
- console.log(import_chalk3.default.gray(" \u2022 Start daemon \u2014 IDE monitor + agent hub (adhdev daemon)"));
8453
- console.log(import_chalk3.default.gray(" \u2022 Launch IDE with CDP (adhdev launch cursor)"));
8454
- console.log(import_chalk3.default.gray(" \u2022 Start CLI agents (adhdev launch gemini)"));
8455
- console.log(import_chalk3.default.gray(" \u2022 Check status (adhdev status)"));
9160
+ console.log(import_chalk3.default.gray(" \u2022 Start the main daemon (adhdev daemon)"));
9161
+ console.log(import_chalk3.default.gray(" \u2022 Launch IDE with CDP (adhdev launch <ide>)"));
9162
+ console.log(import_chalk3.default.gray(" \u2022 Start CLI agents (adhdev launch <agent>)"));
9163
+ console.log(import_chalk3.default.gray(" \u2022 CDP Debugging Console (adhdev cdp)"));
9164
+ console.log(import_chalk3.default.gray(" \u2022 Check setup status (adhdev status)"));
8456
9165
  console.log();
8457
9166
  if (currentVersion) {
8458
9167
  console.log(import_chalk3.default.green(` \u2713 Currently installed: v${currentVersion}`));
@@ -8834,7 +9543,7 @@ program.command("daemon:stop").description("Stop ADHDev Daemon").action(async ()
8834
9543
  async function sendDaemonCommand(cmd, args = {}, port = 19222) {
8835
9544
  const WebSocket4 = (await import("ws")).default;
8836
9545
  const { DAEMON_WS_PATH: DAEMON_WS_PATH2 } = await Promise.resolve().then(() => (init_ipc_protocol(), ipc_protocol_exports));
8837
- return new Promise((resolve5, reject) => {
9546
+ return new Promise((resolve6, reject) => {
8838
9547
  const wsUrl = `ws://127.0.0.1:${port}${DAEMON_WS_PATH2 || "/daemon"}`;
8839
9548
  const ws = new WebSocket4(wsUrl);
8840
9549
  const timeout = setTimeout(() => {
@@ -8865,7 +9574,7 @@ async function sendDaemonCommand(cmd, args = {}, port = 19222) {
8865
9574
  if (msg.type === "daemon:command_result" || msg.type === "command_result") {
8866
9575
  clearTimeout(timeout);
8867
9576
  ws.close();
8868
- resolve5(msg.payload?.result || msg.payload || msg);
9577
+ resolve6(msg.payload?.result || msg.payload || msg);
8869
9578
  }
8870
9579
  } catch {
8871
9580
  }
@@ -8882,13 +9591,13 @@ Is 'adhdev daemon' running?`));
8882
9591
  }
8883
9592
  async function directCdpEval(expression, port = 9222) {
8884
9593
  const http3 = await import("http");
8885
- const targets = await new Promise((resolve5, reject) => {
9594
+ const targets = await new Promise((resolve6, reject) => {
8886
9595
  http3.get(`http://127.0.0.1:${port}/json`, (res) => {
8887
9596
  let data = "";
8888
9597
  res.on("data", (c) => data += c);
8889
9598
  res.on("end", () => {
8890
9599
  try {
8891
- resolve5(JSON.parse(data));
9600
+ resolve6(JSON.parse(data));
8892
9601
  } catch {
8893
9602
  reject(new Error("Invalid JSON"));
8894
9603
  }
@@ -8901,7 +9610,7 @@ async function directCdpEval(expression, port = 9222) {
8901
9610
  const target = (mainPages.length > 0 ? mainPages[0] : pages[0]) || targets[0];
8902
9611
  if (!target?.webSocketDebuggerUrl) throw new Error("No CDP target found");
8903
9612
  const WebSocket4 = (await import("ws")).default;
8904
- return new Promise((resolve5, reject) => {
9613
+ return new Promise((resolve6, reject) => {
8905
9614
  const ws = new WebSocket4(target.webSocketDebuggerUrl);
8906
9615
  const timeout = setTimeout(() => {
8907
9616
  ws.close();
@@ -8923,11 +9632,11 @@ async function directCdpEval(expression, port = 9222) {
8923
9632
  clearTimeout(timeout);
8924
9633
  ws.close();
8925
9634
  if (msg.result?.result?.value !== void 0) {
8926
- resolve5(msg.result.result.value);
9635
+ resolve6(msg.result.result.value);
8927
9636
  } else if (msg.result?.exceptionDetails) {
8928
9637
  reject(new Error(msg.result.exceptionDetails.text));
8929
9638
  } else {
8930
- resolve5(msg.result);
9639
+ resolve6(msg.result);
8931
9640
  }
8932
9641
  }
8933
9642
  });
@@ -8981,7 +9690,7 @@ provider.command("list").description("List all loaded providers").option("-j, --
8981
9690
  provider.command("reload").description("Hot-reload all providers (requires daemon --dev)").action(async () => {
8982
9691
  try {
8983
9692
  const http3 = await import("http");
8984
- const result = await new Promise((resolve5, reject) => {
9693
+ const result = await new Promise((resolve6, reject) => {
8985
9694
  const req = http3.request({
8986
9695
  hostname: "127.0.0.1",
8987
9696
  port: 19280,
@@ -8993,9 +9702,9 @@ provider.command("reload").description("Hot-reload all providers (requires daemo
8993
9702
  res.on("data", (c) => data += c);
8994
9703
  res.on("end", () => {
8995
9704
  try {
8996
- resolve5(JSON.parse(data));
9705
+ resolve6(JSON.parse(data));
8997
9706
  } catch {
8998
- resolve5({ raw: data });
9707
+ resolve6({ raw: data });
8999
9708
  }
9000
9709
  });
9001
9710
  });
@@ -9029,7 +9738,7 @@ provider.command("create <type>").description("Scaffold a new provider.js from t
9029
9738
  const category = options.category;
9030
9739
  const location = options.builtin ? "builtin" : "user";
9031
9740
  const http3 = await import("http");
9032
- const result = await new Promise((resolve5, reject) => {
9741
+ const result = await new Promise((resolve6, reject) => {
9033
9742
  const postData = JSON.stringify({ type, name, category, location });
9034
9743
  const req = http3.request({
9035
9744
  hostname: "127.0.0.1",
@@ -9042,9 +9751,9 @@ provider.command("create <type>").description("Scaffold a new provider.js from t
9042
9751
  res.on("data", (c) => data += c);
9043
9752
  res.on("end", () => {
9044
9753
  try {
9045
- resolve5(JSON.parse(data));
9754
+ resolve6(JSON.parse(data));
9046
9755
  } catch {
9047
- resolve5({ raw: data });
9756
+ resolve6({ raw: data });
9048
9757
  }
9049
9758
  });
9050
9759
  });
@@ -9126,7 +9835,7 @@ provider.command("run <type> <script>").description("Run a specific provider scr
9126
9835
  script,
9127
9836
  params: options.param ? { text: options.param, sessionId: options.param, buttonText: options.param } : {}
9128
9837
  });
9129
- const result = await new Promise((resolve5, reject) => {
9838
+ const result = await new Promise((resolve6, reject) => {
9130
9839
  const req = http3.request({
9131
9840
  hostname: "127.0.0.1",
9132
9841
  port: 19280,
@@ -9138,9 +9847,9 @@ provider.command("run <type> <script>").description("Run a specific provider scr
9138
9847
  res.on("data", (c) => data += c);
9139
9848
  res.on("end", () => {
9140
9849
  try {
9141
- resolve5(JSON.parse(data));
9850
+ resolve6(JSON.parse(data));
9142
9851
  } catch {
9143
- resolve5({ raw: data });
9852
+ resolve6({ raw: data });
9144
9853
  }
9145
9854
  });
9146
9855
  });
@@ -9176,15 +9885,15 @@ provider.command("run <type> <script>").description("Run a specific provider scr
9176
9885
  provider.command("source <type>").description("View source code of a provider").action(async (type) => {
9177
9886
  try {
9178
9887
  const http3 = await import("http");
9179
- const result = await new Promise((resolve5, reject) => {
9888
+ const result = await new Promise((resolve6, reject) => {
9180
9889
  http3.get(`http://127.0.0.1:19280/api/providers/${type}/source`, (res) => {
9181
9890
  let data = "";
9182
9891
  res.on("data", (c) => data += c);
9183
9892
  res.on("end", () => {
9184
9893
  try {
9185
- resolve5(JSON.parse(data));
9894
+ resolve6(JSON.parse(data));
9186
9895
  } catch {
9187
- resolve5({ raw: data });
9896
+ resolve6({ raw: data });
9188
9897
  }
9189
9898
  });
9190
9899
  }).on("error", () => {
@@ -9388,13 +10097,13 @@ cdp.command("eval <expression>").description("Execute JavaScript expression via
9388
10097
  cdp.command("screenshot").description("Capture IDE screenshot").option("-p, --port <port>", "CDP port", "9222").option("-o, --output <file>", "Output file path", "/tmp/cdp_screenshot.jpg").action(async (options) => {
9389
10098
  try {
9390
10099
  const http3 = await import("http");
9391
- const targets = await new Promise((resolve5, reject) => {
10100
+ const targets = await new Promise((resolve6, reject) => {
9392
10101
  http3.get(`http://127.0.0.1:${options.port}/json`, (res) => {
9393
10102
  let data = "";
9394
10103
  res.on("data", (c) => data += c);
9395
10104
  res.on("end", () => {
9396
10105
  try {
9397
- resolve5(JSON.parse(data));
10106
+ resolve6(JSON.parse(data));
9398
10107
  } catch {
9399
10108
  reject(new Error("Invalid JSON"));
9400
10109
  }
@@ -9408,7 +10117,7 @@ cdp.command("screenshot").description("Capture IDE screenshot").option("-p, --po
9408
10117
  if (!target?.webSocketDebuggerUrl) throw new Error("No CDP target");
9409
10118
  const WebSocket4 = (await import("ws")).default;
9410
10119
  const ws = new WebSocket4(target.webSocketDebuggerUrl);
9411
- await new Promise((resolve5, reject) => {
10120
+ await new Promise((resolve6, reject) => {
9412
10121
  ws.on("open", () => {
9413
10122
  ws.send(JSON.stringify({ id: 1, method: "Page.captureScreenshot", params: { format: "jpeg", quality: 50 } }));
9414
10123
  });
@@ -9421,7 +10130,7 @@ cdp.command("screenshot").description("Capture IDE screenshot").option("-p, --po
9421
10130
  \u2713 Screenshot saved to ${options.output}
9422
10131
  `));
9423
10132
  ws.close();
9424
- resolve5();
10133
+ resolve6();
9425
10134
  }
9426
10135
  });
9427
10136
  ws.on("error", (e) => reject(e));
@@ -9437,10 +10146,10 @@ if (process.argv.length <= 2) {
9437
10146
  program.outputHelp();
9438
10147
  console.log();
9439
10148
  console.log(import_chalk4.default.gray(" Quick start:"));
9440
- console.log(import_chalk4.default.gray(" adhdev setup \u2014 First-time setup"));
9441
- console.log(import_chalk4.default.gray(" adhdev daemon \u2014 Start unified daemon"));
9442
- console.log(import_chalk4.default.gray(" adhdev launch claude \u2014 Launch CLI agent"));
9443
- console.log(import_chalk4.default.gray(" adhdev launch cursor \u2014 Launch Cursor with CDP"));
10149
+ console.log(import_chalk4.default.gray(" adhdev daemon \u2014 Start unified daemon (Required)"));
10150
+ console.log(import_chalk4.default.gray(" adhdev launch cursor \u2014 Launch IDE with CDP (e.g. cursor, windsurf)"));
10151
+ console.log(import_chalk4.default.gray(" adhdev launch claude \u2014 Launch CLI agent (e.g. gemini, claude)"));
10152
+ console.log(import_chalk4.default.gray(" adhdev setup \u2014 First-time setup & configuration"));
9444
10153
  console.log(import_chalk4.default.gray(" adhdev status \u2014 Check current setup"));
9445
10154
  console.log();
9446
10155
  } else {