@adhdev/daemon-core 0.6.35 → 0.6.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.js +112 -26
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/providers/_builtin/acp/codex-cli/provider.json +2 -2
  6. package/providers/_builtin/cli/codex-cli/provider.json +8 -18
  7. package/providers/_builtin/ide/antigravity/provider.json +2 -3
  8. package/providers/_builtin/ide/cursor/provider.json +2 -2
  9. package/providers/_builtin/ide/cursor/scripts/{0.49 → 1.0}/read_chat.js +1 -1
  10. package/providers/_builtin/ide/cursor/scripts/{0.49 → 1.0}/resolve_action.js +1 -1
  11. package/providers/_builtin/ide/windsurf/scripts/1.0/focus_editor.js +16 -10
  12. package/providers/_builtin/ide/windsurf/scripts/1.0/list_chats.js +9 -103
  13. package/providers/_builtin/ide/windsurf/scripts/1.0/list_models.js +43 -26
  14. package/providers/_builtin/ide/windsurf/scripts/1.0/list_modes.js +51 -27
  15. package/providers/_builtin/ide/windsurf/scripts/1.0/list_sessions.js +79 -0
  16. package/providers/_builtin/ide/windsurf/scripts/1.0/new_session.js +30 -41
  17. package/providers/_builtin/ide/windsurf/scripts/1.0/open_panel.js +44 -35
  18. package/providers/_builtin/ide/windsurf/scripts/1.0/read_chat.js +211 -273
  19. package/providers/_builtin/ide/windsurf/scripts/1.0/resolve_action.js +64 -50
  20. package/providers/_builtin/ide/windsurf/scripts/1.0/send_message.js +14 -81
  21. package/providers/_builtin/ide/windsurf/scripts/1.0/set_mode.js +34 -6
  22. package/providers/_builtin/ide/windsurf/scripts/1.0/set_model.js +40 -6
  23. package/providers/_builtin/ide/windsurf/scripts/1.0/switch_session.js +66 -42
  24. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/focus_editor.js +30 -0
  25. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/list_chats.js +117 -0
  26. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/list_models.js +39 -0
  27. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/list_modes.js +39 -0
  28. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/new_session.js +69 -0
  29. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/open_panel.js +58 -0
  30. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/read_chat.js +315 -0
  31. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/resolve_action.js +68 -0
  32. package/providers/_builtin/ide/{cursor/scripts/0.49 → windsurf/scripts/1.0.bak}/scripts.js +27 -23
  33. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/send_message.js +87 -0
  34. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/set_mode.js +15 -0
  35. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/set_model.js +15 -0
  36. package/providers/_builtin/ide/windsurf/scripts/1.0.bak/switch_session.js +58 -0
  37. package/providers/_builtin/registry.json +5 -9
  38. package/src/cdp/manager.ts +38 -12
  39. package/src/commands/handler.ts +1 -8
  40. package/src/daemon/dev-server.ts +66 -8
  41. package/src/detection/ide-detector.ts +0 -3
  42. package/src/providers/provider-loader.ts +4 -2
  43. package/providers/_builtin/ide/antigravity/scripts/1.106/list_models.js +0 -38
  44. package/providers/_builtin/ide/antigravity/scripts/1.106/list_modes.js +0 -48
  45. package/providers/_builtin/ide/antigravity/scripts/1.106/read_chat.js +0 -290
  46. package/providers/_builtin/ide/antigravity/scripts/1.106/scripts.js +0 -57
  47. package/providers/_builtin/ide/antigravity/scripts/1.106/set_mode.js +0 -34
  48. package/providers/_builtin/ide/antigravity/scripts/1.106/set_model.js +0 -47
  49. package/providers/_builtin/ide/antigravity/scripts/1.107/focus_editor.js +0 -20
  50. package/providers/_builtin/ide/antigravity/scripts/1.107/list_chats.js +0 -137
  51. package/providers/_builtin/ide/antigravity/scripts/1.107/new_session.js +0 -75
  52. package/providers/_builtin/ide/antigravity/scripts/1.107/resolve_action.js +0 -63
  53. package/providers/_builtin/ide/antigravity/scripts/1.107/send_message.js +0 -56
  54. package/providers/_builtin/ide/antigravity/scripts/1.107/switch_session.js +0 -114
  55. package/providers/_builtin/ide/cursor/scripts/0.49/dismiss_notification.js +0 -30
  56. package/providers/_builtin/ide/cursor/scripts/0.49/focus_editor.js +0 -13
  57. package/providers/_builtin/ide/cursor/scripts/0.49/list_models.js +0 -78
  58. package/providers/_builtin/ide/cursor/scripts/0.49/list_modes.js +0 -40
  59. package/providers/_builtin/ide/cursor/scripts/0.49/list_notifications.js +0 -23
  60. package/providers/_builtin/ide/cursor/scripts/0.49/new_session.js +0 -20
  61. package/providers/_builtin/ide/cursor/scripts/0.49/open_panel.js +0 -23
  62. package/providers/_builtin/ide/cursor/scripts/0.49/send_message.js +0 -23
  63. package/providers/_builtin/ide/cursor/scripts/0.49/set_mode.js +0 -38
  64. package/providers/_builtin/ide/cursor/scripts/0.49/set_model.js +0 -81
  65. package/providers/_builtin/ide/cursor/scripts/0.49/switch_session.js +0 -28
  66. package/providers/_builtin/ide/cursor/scripts/0.49.bak/list_sessions.js +0 -42
  67. package/providers/_builtin/ide/cursor/scripts/0.49.bak/read_chat.js +0 -79
  68. package/providers/_builtin/ide/cursor/scripts/0.49.bak/resolve_action.js +0 -19
  69. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/focus_editor.js +0 -0
  70. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/list_chats.js +0 -0
  71. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/list_models.js +0 -0
  72. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/list_modes.js +0 -0
  73. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/new_session.js +0 -0
  74. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/read_chat.js +0 -0
  75. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/resolve_action.js +0 -0
  76. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/scripts.js +0 -0
  77. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/send_message.js +0 -0
  78. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/set_mode.js +0 -0
  79. /package/providers/_builtin/ide/antigravity/scripts/{1.107 → 1.0}/set_model.js +0 -0
  80. /package/providers/_builtin/ide/antigravity/scripts/{1.106 → 1.0}/switch_session.js +0 -0
  81. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/dismiss_notification.js +0 -0
  82. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/focus_editor.js +0 -0
  83. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/list_models.js +0 -0
  84. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/list_modes.js +0 -0
  85. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/list_notifications.js +0 -0
  86. /package/providers/_builtin/ide/cursor/scripts/{0.49 → 1.0}/list_sessions.js +0 -0
  87. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/new_session.js +0 -0
  88. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/open_panel.js +0 -0
  89. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/scripts.js +0 -0
  90. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/send_message.js +0 -0
  91. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/set_mode.js +0 -0
  92. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/set_model.js +0 -0
  93. /package/providers/_builtin/ide/cursor/scripts/{0.49.bak → 1.0}/switch_session.js +0 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Windsurf v1 — send_message
3
+ *
4
+ * Windsurf는 Lexical 에디터 ([data-lexical-editor="true"])를 사용합니다.
5
+ * contenteditable div이므로 execCommand('insertText') 또는 InputEvent로 입력 가능.
6
+ *
7
+ * ⚠️ Lexical은 입력 이벤트를 정밀하게 감지하므로:
8
+ * - execCommand('insertText')가 가장 안정적
9
+ * - nativeSetter 방식은 동작하지 않음
10
+ * - InputEvent('insertText')를 폴백으로 사용
11
+ *
12
+ * Enter 키는 KeyboardEvent 전체 시퀀스 필요 (keydown + keypress + keyup).
13
+ *
14
+ * 파라미터: ${ MESSAGE }
15
+ * 최종 확인: Windsurf 1.108.x (2026-03-10)
16
+ */
17
+ (async () => {
18
+ try {
19
+ const msg = ${ MESSAGE };
20
+
21
+ // ─── 1. Lexical 에디터 찾기 (폴백 체인) ───
22
+ const editor =
23
+ document.querySelector('[data-lexical-editor="true"]') ||
24
+ document.querySelector('[contenteditable="true"][role="textbox"]') ||
25
+ document.querySelector('.cascade-input [contenteditable="true"]') ||
26
+ document.querySelector('.chat-input textarea') ||
27
+ document.querySelector('textarea:not(.xterm-helper-textarea)');
28
+
29
+ if (!editor) return 'error: no input found';
30
+
31
+ const isTextarea = editor.tagName === 'TEXTAREA';
32
+
33
+ if (isTextarea) {
34
+ // ─── textarea 폴백 (미로그인 등) ───
35
+ editor.focus();
36
+ const proto = HTMLTextAreaElement.prototype;
37
+ const nativeSetter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
38
+ if (nativeSetter) nativeSetter.call(editor, msg);
39
+ else editor.value = msg;
40
+
41
+ editor.dispatchEvent(new Event('input', { bubbles: true }));
42
+ editor.dispatchEvent(new Event('change', { bubbles: true }));
43
+
44
+ await new Promise(r => setTimeout(r, 300));
45
+
46
+ const enterOpts = {
47
+ key: 'Enter', code: 'Enter',
48
+ keyCode: 13, which: 13,
49
+ bubbles: true, cancelable: true, composed: true,
50
+ };
51
+ editor.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
52
+ editor.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
53
+ editor.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
54
+
55
+ return 'sent';
56
+ }
57
+
58
+ // ─── 2. contenteditable (Lexical) 에디터 ───
59
+ editor.focus();
60
+
61
+ // 기존 내용 선택 후 삭제
62
+ document.execCommand('selectAll', false, null);
63
+ document.execCommand('delete', false, null);
64
+
65
+ // 텍스트 삽입 (Lexical은 execCommand('insertText')를 인식)
66
+ document.execCommand('insertText', false, msg);
67
+
68
+ // React/Lexical에 변경 알림
69
+ editor.dispatchEvent(new Event('input', { bubbles: true }));
70
+
71
+ await new Promise(r => setTimeout(r, 300));
72
+
73
+ // ─── 3. Enter 키 전송 (전체 시퀀스) ───
74
+ const enterOpts = {
75
+ key: 'Enter', code: 'Enter',
76
+ keyCode: 13, which: 13,
77
+ bubbles: true, cancelable: true, composed: true,
78
+ };
79
+ editor.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
80
+ editor.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
81
+ editor.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
82
+
83
+ return 'sent';
84
+ } catch (e) {
85
+ return 'error: ' + e.message;
86
+ }
87
+ })()
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Generic fallback — set_model
3
+ * ${ MODEL }
4
+ */
5
+ (() => {
6
+ try {
7
+ const want = ${ MODEL } || '';
8
+ const norm = (t) => t.toLowerCase().trim();
9
+
10
+ // Very basic click attempt
11
+ return JSON.stringify({ success: false, error: 'Model selection requires UI interaction not supported by generic script' });
12
+ } catch (e) {
13
+ return JSON.stringify({ success: false, error: e.message });
14
+ }
15
+ })()
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Generic fallback — set_model
3
+ * ${ MODEL }
4
+ */
5
+ (() => {
6
+ try {
7
+ const want = ${ MODEL } || '';
8
+ const norm = (t) => t.toLowerCase().trim();
9
+
10
+ // Very basic click attempt
11
+ return JSON.stringify({ success: false, error: 'Model selection requires UI interaction not supported by generic script' });
12
+ } catch (e) {
13
+ return JSON.stringify({ success: false, error: e.message });
14
+ }
15
+ })()
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Windsurf v1 — switch_session
3
+ *
4
+ * Cascade 세션(대화) 탭을 전환합니다.
5
+ * cascade-tab-{id} 내부의 실제 클릭 가능한 자식 DIV의
6
+ * React onClick 핸들러를 직접 호출합니다.
7
+ *
8
+ * 파라미터: ${ SESSION_ID } — 전환할 세션 ID (cascade-tab의 UUID)
9
+ *
10
+ * 최종 확인: Windsurf 1.108.x (2026-03-10)
11
+ */
12
+ (() => {
13
+ try {
14
+ const id = ${ SESSION_ID };
15
+
16
+ // Helper: find React onClick on an element or its children
17
+ function clickReact(el) {
18
+ // Check element itself and children for React onClick
19
+ const targets = [el, ...el.querySelectorAll('*')];
20
+ for (const t of targets) {
21
+ const rp = Object.keys(t).find(k => k.startsWith('__reactProps'));
22
+ if (rp && typeof t[rp].onClick === 'function') {
23
+ t[rp].onClick({
24
+ preventDefault: () => { },
25
+ stopPropagation: () => { },
26
+ nativeEvent: { stopImmediatePropagation: () => { } },
27
+ target: t, currentTarget: t, button: 0, type: 'click'
28
+ });
29
+ return true;
30
+ }
31
+ }
32
+ return false;
33
+ }
34
+
35
+ // 1. cascade-tab-{id} 요소 찾기
36
+ const tab = document.getElementById('cascade-tab-' + id);
37
+ if (tab) {
38
+ if (clickReact(tab)) return 'switched';
39
+ // Fallback: DOM click
40
+ tab.click();
41
+ return 'switched-dom';
42
+ }
43
+
44
+ // 2. 제목으로 매칭
45
+ const tabs = document.querySelectorAll('[id^="cascade-tab-"]');
46
+ for (const t of tabs) {
47
+ if (t.textContent?.trim() === id) {
48
+ if (clickReact(t)) return 'switched-by-title';
49
+ t.click();
50
+ return 'switched-by-title-dom';
51
+ }
52
+ }
53
+
54
+ return 'not_found';
55
+ } catch (e) {
56
+ return 'error: ' + e.message;
57
+ }
58
+ })()
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2026.03.26",
2
+ "version": "2026.03.27",
3
3
  "providers": {
4
4
  "agentpool-acp": {
5
5
  "providerVersion": "0.0.0",
@@ -212,12 +212,8 @@
212
212
  "name": "Antigravity",
213
213
  "compatibility": [
214
214
  {
215
- "ideVersion": ">=1.107.0",
216
- "scriptDir": "scripts/1.107"
217
- },
218
- {
219
- "ideVersion": ">=1.105.0",
220
- "scriptDir": "scripts/1.106"
215
+ "ideVersion": ">=1.0.0",
216
+ "scriptDir": "scripts/1.0"
221
217
  }
222
218
  ]
223
219
  },
@@ -227,8 +223,8 @@
227
223
  "name": "Cursor",
228
224
  "compatibility": [
229
225
  {
230
- "ideVersion": ">=0.49.0",
231
- "scriptDir": "scripts/0.49"
226
+ "ideVersion": ">=1.0.0",
227
+ "scriptDir": "scripts/1.0"
232
228
  }
233
229
  ]
234
230
  },
@@ -503,18 +503,31 @@ export class DaemonCdpManager {
503
503
  });
504
504
  await new Promise(r => setTimeout(r, 150));
505
505
 
506
- // Step 3: Insert text
507
- await this.sendInternal('Input.insertText', { text });
506
+ // Step 3: Insert text & handle newlines
507
+ const lines = text.split('\n');
508
+ for (let i = 0; i < lines.length; i++) {
509
+ if (lines[i].length > 0) {
510
+ await this.sendInternal('Input.insertText', { text: lines[i] });
511
+ await new Promise(r => setTimeout(r, 100));
512
+ }
513
+ if (i < lines.length - 1) {
514
+ await this.sendInternal('Input.dispatchKeyEvent', { type: 'rawKeyDown', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, modifiers: 8 }); // Shift
515
+ await this.sendInternal('Input.dispatchKeyEvent', { type: 'char', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, text: '\r', unmodifiedText: '\r', modifiers: 8 });
516
+ await this.sendInternal('Input.dispatchKeyEvent', { type: 'keyUp', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, modifiers: 8 });
517
+ await new Promise(r => setTimeout(r, 100));
518
+ }
519
+ }
508
520
  await new Promise(r => setTimeout(r, 200));
509
521
 
510
522
  // Step 4: Press Enter
511
523
  await this.sendInternal('Input.dispatchKeyEvent', {
512
- type: 'rawKeyDown', key: 'Enter', code: 'Enter',
513
- windowsVirtualKeyCode: 13, nativeVirtualKeyCode: 13,
524
+ type: 'rawKeyDown', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, text: '\r', unmodifiedText: '\r'
525
+ });
526
+ await this.sendInternal('Input.dispatchKeyEvent', {
527
+ type: 'char', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, text: '\r', unmodifiedText: '\r'
514
528
  });
515
529
  await this.sendInternal('Input.dispatchKeyEvent', {
516
- type: 'keyUp', key: 'Enter', code: 'Enter',
517
- windowsVirtualKeyCode: 13, nativeVirtualKeyCode: 13,
530
+ type: 'keyUp', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13
518
531
  });
519
532
 
520
533
  this.log(`[CDP] typeAndSend: sent "${text.substring(0, 50)}..."`);
@@ -558,18 +571,31 @@ export class DaemonCdpManager {
558
571
  });
559
572
  await new Promise(r => setTimeout(r, 150));
560
573
 
561
- // Step 3: Insert text
562
- await this.sendInternal('Input.insertText', { text });
574
+ // Step 3: Insert text & handle newlines
575
+ const lines = text.split('\n');
576
+ for (let i = 0; i < lines.length; i++) {
577
+ if (lines[i].length > 0) {
578
+ await this.sendInternal('Input.insertText', { text: lines[i] });
579
+ await new Promise(r => setTimeout(r, 100));
580
+ }
581
+ if (i < lines.length - 1) {
582
+ await this.sendInternal('Input.dispatchKeyEvent', { type: 'rawKeyDown', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, modifiers: 8 }); // Shift
583
+ await this.sendInternal('Input.dispatchKeyEvent', { type: 'char', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, text: '\r', unmodifiedText: '\r', modifiers: 8 });
584
+ await this.sendInternal('Input.dispatchKeyEvent', { type: 'keyUp', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, modifiers: 8 });
585
+ await new Promise(r => setTimeout(r, 100));
586
+ }
587
+ }
563
588
  await new Promise(r => setTimeout(r, 200));
564
589
 
565
590
  // Step 4: Press Enter
566
591
  await this.sendInternal('Input.dispatchKeyEvent', {
567
- type: 'rawKeyDown', key: 'Enter', code: 'Enter',
568
- windowsVirtualKeyCode: 13, nativeVirtualKeyCode: 13,
592
+ type: 'rawKeyDown', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, text: '\r', unmodifiedText: '\r'
593
+ });
594
+ await this.sendInternal('Input.dispatchKeyEvent', {
595
+ type: 'char', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, text: '\r', unmodifiedText: '\r'
569
596
  });
570
597
  await this.sendInternal('Input.dispatchKeyEvent', {
571
- type: 'keyUp', key: 'Enter', code: 'Enter',
572
- windowsVirtualKeyCode: 13, nativeVirtualKeyCode: 13,
598
+ type: 'keyUp', key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13
573
599
  });
574
600
 
575
601
  this.log(`[CDP] typeAndSendAt(${Math.round(x)},${Math.round(y)}): sent "${text.substring(0, 50)}..."`);
@@ -317,14 +317,7 @@ export class DaemonCommandHandler implements CommandHelpers {
317
317
  }
318
318
  return { success: false, error: `VSCode command not available: ${resolvedCmd || cmd}` };
319
319
  }
320
- case 'get_open_editors':
321
- case 'open_tab':
322
- case 'close_tab':
323
- case 'open_folder':
324
- case 'open_folder_picker':
325
- case 'open_recent':
326
- case 'get_commands':
327
- return { success: false, error: `${cmd} requires bridge-extension (removed)` };
320
+ // ─── Workspace cmds ──────────────
328
321
  case 'get_recent_workspaces': return this.handleGetRecentWorkspaces(args);
329
322
  case 'get_cli_history': {
330
323
  const config = loadConfig();
@@ -78,6 +78,7 @@ export class DevServer {
78
78
  { method: 'GET', pattern: '/api/providers/versions', handler: (q, s) => this.handleDetectVersions(q, s) },
79
79
  { method: 'POST', pattern: '/api/providers/reload', handler: (q, s) => this.handleReload(q, s) },
80
80
  { method: 'POST', pattern: '/api/cdp/evaluate', handler: (q, s) => this.handleCdpEvaluate(q, s) },
81
+ { method: 'POST', pattern: '/api/cdp/click', handler: (q, s) => this.handleCdpClick(q, s) },
81
82
  { method: 'POST', pattern: '/api/cdp/dom/query', handler: (q, s) => this.handleCdpDomQuery(q, s) },
82
83
  { method: 'POST', pattern: '/api/cdp/dom/inspect', handler: (q, s) => this.handleDomInspect(q, s) },
83
84
  { method: 'POST', pattern: '/api/cdp/dom/children', handler: (q, s) => this.handleDomChildren(q, s) },
@@ -108,6 +109,7 @@ export class DevServer {
108
109
  { method: 'POST', pattern: /^\/api\/providers\/([^/]+)\/file$/, handler: (q, s, p) => this.handleWriteFile(p![0], q, s) },
109
110
  { method: 'GET', pattern: /^\/api\/providers\/([^/]+)\/source$/, handler: (q, s, p) => this.handleSource(p![0], q, s) },
110
111
  { method: 'POST', pattern: /^\/api\/providers\/([^/]+)\/save$/, handler: (q, s, p) => this.handleSave(p![0], q, s) },
112
+ { method: 'POST', pattern: /^\/api\/providers\/([^/]+)\/typeAndSend$/, handler: (q, s, p) => this.handleTypeAndSend(p![0], q, s) },
111
113
  { method: 'GET', pattern: /^\/api\/providers\/([^/]+)\/config$/, handler: (q, s, p) => this.handleProviderConfig(p![0], q, s) },
112
114
  { method: 'POST', pattern: /^\/api\/providers\/([^/]+)\/dom-context$/, handler: (q, s, p) => this.handleDomContext(p![0], q, s) },
113
115
  { method: 'POST', pattern: /^\/api\/providers\/([^/]+)\/auto-implement$/, handler: (q, s, p) => this.handleAutoImplement(p![0], q, s) },
@@ -330,9 +332,18 @@ export class DevServer {
330
332
  }
331
333
 
332
334
  try {
333
- // Pass all params to script (flexible parameter support)
334
- // Backward compat: legacy single argument scripts (sendMessage(text)) also work
335
- const scriptCode = params ? fn(params) : fn();
335
+ // Emulate production CommandHandler behavior
336
+ let scriptCode: string | null = null;
337
+ if (['sendMessage', 'webviewSendMessage', 'switchSession', 'webviewSwitchSession', 'setMode', 'webviewSetMode', 'setModel', 'webviewSetModel'].includes(scriptName)) {
338
+ // Production daemon's getProviderScript always unpacks the object and sends the first value
339
+ const firstVal = params && typeof params === 'object' && Object.keys(params).length > 0
340
+ ? Object.values(params)[0]
341
+ : params;
342
+ scriptCode = firstVal !== undefined ? fn(firstVal) : fn();
343
+ } else {
344
+ // Scripts like resolveAction are passed the raw parameters object in production
345
+ scriptCode = params !== undefined ? fn(params) : fn();
346
+ }
336
347
  if (!scriptCode) {
337
348
  this.json(res, 500, { error: 'Script function returned null' });
338
349
  return;
@@ -371,6 +382,7 @@ export class DevServer {
371
382
  if (typeof raw === 'string') {
372
383
  try { result = JSON.parse(raw); } catch { /* keep */ }
373
384
  }
385
+ this.log(`Script raw debug: typeof=${typeof raw}, raw=${JSON.stringify(raw)}, parsed=${JSON.stringify(result)}`);
374
386
  this.json(res, 200, { type, script: scriptName, result });
375
387
  } catch (e: any) {
376
388
  this.json(res, 500, { error: `Script execution failed: ${e.message}` });
@@ -406,6 +418,29 @@ export class DevServer {
406
418
  }
407
419
  }
408
420
 
421
+ private async handleCdpClick(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
422
+ const body = await this.readBody(req);
423
+ const { ideType, x, y } = body;
424
+ if (x == null || y == null) {
425
+ this.json(res, 400, { error: 'x and y coordinates required' });
426
+ return;
427
+ }
428
+
429
+ const cdp = this.getCdp(ideType);
430
+ if (!cdp?.isConnected) {
431
+ this.json(res, 503, { error: 'No CDP connection available' });
432
+ return;
433
+ }
434
+
435
+ try {
436
+ await cdp.send('Input.dispatchMouseEvent', { type: 'mousePressed', x, y, button: 'left', clickCount: 1 });
437
+ await cdp.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x, y, button: 'left', clickCount: 1 });
438
+ this.json(res, 200, { success: true, clicked: true, x, y });
439
+ } catch (e: any) {
440
+ this.json(res, 500, { error: e.message });
441
+ }
442
+ }
443
+
409
444
  private async handleCdpDomQuery(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
410
445
  const body = await this.readBody(req);
411
446
  const { selector, limit = 10, ideType } = body;
@@ -522,6 +557,11 @@ export class DevServer {
522
557
  const providers = this.providerLoader.getAll().map(p => ({
523
558
  type: p.type, name: p.name, category: p.category,
524
559
  }));
560
+ for (const cdp of this.cdpManagers.values()) {
561
+ if (!cdp.isConnected) {
562
+ (cdp as any)._targetId = null;
563
+ }
564
+ }
525
565
  this.json(res, 200, { reloaded: true, providers });
526
566
  } catch (e: any) {
527
567
  this.json(res, 500, { error: e.message });
@@ -692,7 +732,7 @@ export class DevServer {
692
732
  const provider = this.providerLoader.getMeta(type);
693
733
  if (!provider) return null;
694
734
  const cat = provider.category;
695
- const builtinDir = (this.providerLoader as any).builtinDir || path.resolve(__dirname, '../providers/_builtin');
735
+ const builtinDir = (this.providerLoader as any).builtinDirs?.[0] || path.resolve(__dirname, '../providers/_builtin');
696
736
  const userDir = path.join(os.homedir(), '.adhdev', 'providers');
697
737
 
698
738
  // Direct match first
@@ -833,6 +873,24 @@ export class DevServer {
833
873
  }
834
874
  }
835
875
 
876
+ private async handleTypeAndSend(type: string, req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
877
+ const body = await this.readBody(req);
878
+ const { selector, text } = body;
879
+ if (!selector || typeof selector !== 'string' || !text || typeof text !== 'string') {
880
+ this.json(res, 400, { error: 'selector and text strings required' }); return;
881
+ }
882
+ const cdp = this.getCdp(type);
883
+ if (!cdp) {
884
+ this.json(res, 503, { error: `CDP not connected for '${type}'` }); return;
885
+ }
886
+ try {
887
+ const sent = await cdp.typeAndSend(selector, text);
888
+ this.json(res, 200, { sent });
889
+ } catch (e: any) {
890
+ this.json(res, 500, { error: e.message });
891
+ }
892
+ }
893
+
836
894
  private async handleScriptHints(type: string, _req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
837
895
  const dir = this.findProviderDir(type);
838
896
  if (!dir) { this.json(res, 404, { error: `Provider not found: ${type}` }); return; }
@@ -1065,7 +1123,7 @@ export class DevServer {
1065
1123
  if (location === 'user') {
1066
1124
  targetDir = path.join(os.homedir(), '.adhdev', 'providers', type);
1067
1125
  } else {
1068
- const builtinDir = path.resolve(__dirname, '../providers/_builtin');
1126
+ const builtinDir = (this.providerLoader as any).builtinDirs?.[0] || path.resolve(__dirname, '../providers/_builtin');
1069
1127
  targetDir = path.join(builtinDir, category, type);
1070
1128
  }
1071
1129
 
@@ -1851,7 +1909,7 @@ export class DevServer {
1851
1909
  this.sendAutoImplSSE({ event: 'progress', data: { function: '_init', status: 'loading_reference', message: `레퍼런스 스크립트 로드 중 (${reference})...` } });
1852
1910
 
1853
1911
  let referenceScripts: Record<string, string> = {};
1854
- const builtinDir = (this.providerLoader as any).builtinDir || path.resolve(__dirname, '../providers/_builtin');
1912
+ const builtinDir = (this.providerLoader as any).builtinDirs?.[0] || path.resolve(__dirname, '../providers/_builtin');
1855
1913
  const refDir = path.join(builtinDir, 'ide', reference);
1856
1914
  if (fs.existsSync(refDir)) {
1857
1915
  // Find latest versioned scripts dir
@@ -2053,7 +2111,7 @@ export class DevServer {
2053
2111
  }
2054
2112
  if (model) args.push('--model', model);
2055
2113
  const escapedArgs = args.map(a => `'${a.replace(/'/g, "'\\''")}'`).join(' ');
2056
- const metaPrompt = `Read the file at ${promptFile} and follow ALL instructions recursively. You have full authority to implement ALL required script files, update provider.json configurations based on the reference patterns, and independently test them against 127.0.0.1:19280 via CDP CURL. Upon complete validation of ALL assigned files, print exactly "AUTO_IMPLEMENT_FINISHED" to gracefully close the pipeline. DO NOT WAIT FOR APPROVAL, execute completely autonomously.`;
2114
+ const metaPrompt = `Read the file at ${promptFile} and follow ALL instructions recursively. You have full authority to implement ALL required script files, update provider.json configurations based on the reference patterns, and independently test them against 127.0.0.1:19280 via CDP CURL. Upon complete validation of ALL assigned files, print exactly "_PIPELINE_COMPLETE_SIGNAL_" to gracefully close the pipeline. DO NOT WAIT FOR APPROVAL, execute completely autonomously.`;
2057
2115
  shellCmd = `${command} ${escapedArgs} "${metaPrompt}"`;
2058
2116
  } else {
2059
2117
  // Generic fallback: pipe prompt via stdin
@@ -2131,7 +2189,7 @@ export class DevServer {
2131
2189
 
2132
2190
  // Force exit on completion signal (ignore during first 15s — prompt echo contains the token)
2133
2191
  const elapsed = Date.now() - spawnedAt;
2134
- if (elapsed > 15000 && approvalBuffer.includes('AUTO_IMPLEMENT_FINISHED')) {
2192
+ if (elapsed > 15000 && approvalBuffer.includes('_PIPELINE_COMPLETE_SIGNAL_')) {
2135
2193
  this.log(`Agent finished task after ${Math.round(elapsed/1000)}s. Terminating interactive CLI session to unblock pipeline.`);
2136
2194
  this.sendAutoImplSSE({ event: 'output', data: { chunk: `\n[🤖 ADHDev Pipeline] Completion token detected. Proceeding...\n`, stream: 'stdout' } });
2137
2195
  approvalBuffer = '';
@@ -22,7 +22,6 @@ export interface IDEInfo {
22
22
  cliCommand: string | null;
23
23
  version: string | null;
24
24
  icon: string;
25
- extensionSupport: 'full' | 'partial' | 'none';
26
25
  notes?: string;
27
26
  }
28
27
 
@@ -31,7 +30,6 @@ export interface IDEDefinition {
31
30
  name: string;
32
31
  displayName: string;
33
32
  icon: string;
34
- extensionSupport: 'full' | 'partial' | 'none';
35
33
  cli: string;
36
34
  paths: {
37
35
  darwin?: string[];
@@ -149,7 +147,6 @@ export async function detectIDEs(): Promise<IDEInfo[]> {
149
147
  cliCommand: resolvedCli || null,
150
148
  version,
151
149
  icon: def.icon,
152
- extensionSupport: def.extensionSupport,
153
150
  });
154
151
  }
155
152
 
@@ -57,7 +57,10 @@ export class ProviderLoader {
57
57
  if (options?.builtinDir) {
58
58
  this.builtinDirs = Array.isArray(options.builtinDir) ? options.builtinDir : [options.builtinDir];
59
59
  } else {
60
- this.builtinDirs = [path.resolve(__dirname, '../providers/_builtin')];
60
+ // Handle both development (src/providers -> ../../providers) and production (dist -> ../providers)
61
+ const bundledDir = path.resolve(__dirname, '../providers/_builtin');
62
+ const devDir = path.resolve(__dirname, '../../providers/_builtin');
63
+ this.builtinDirs = [fs.existsSync(devDir) ? devDir : bundledDir];
61
64
  }
62
65
  // User custom directory: ~/.adhdev/providers/
63
66
  this.userDir = options?.userDir ||
@@ -322,7 +325,6 @@ export class ProviderLoader {
322
325
  name: p.name,
323
326
  displayName: p.displayName || p.name,
324
327
  icon: p.icon || '💻',
325
- extensionSupport: 'full',
326
328
  cli: p.cli,
327
329
  paths: p.paths as { darwin?: string[]; win32?: string[]; linux?: string[] },
328
330
  });
@@ -1,38 +0,0 @@
1
- /**
2
- * Antigravity — list_models
3
- * antigravity-agent-side-panel 내부 모델 드롭다운에서 목록 + 현재 모델 추출
4
- * → { models: string[], current: string }
5
- */
6
- (() => {
7
- try {
8
- const models = [];
9
- let current = '';
10
-
11
- // 1. 모델 항목에서 목록 추출
12
- // 셀렉터: .px-2.py-1.flex.items-center.justify-between.cursor-pointer
13
- const items = document.querySelectorAll('.px-2.py-1.flex.items-center.justify-between.cursor-pointer');
14
- for (const item of items) {
15
- const label = item.querySelector('.text-xs.font-medium');
16
- const text = (label || item).textContent?.trim();
17
- if (!text || text.length > 60) continue;
18
- // 모델명 검증 (Claude, Gemini, GPT, etc.)
19
- models.push(text);
20
- // 선택된 항목: bg-gray-500/20
21
- if ((item.className || '').includes('bg-gray-500/20')) {
22
- current = text;
23
- }
24
- }
25
-
26
- // 2. 모델 목록이 없으면 (드롭다운 닫힘) → 트리거 버튼에서 현재 모델만
27
- if (models.length === 0) {
28
- const trigger = document.querySelector('.flex.min-w-0.max-w-full.cursor-pointer.items-center');
29
- if (trigger) {
30
- current = trigger.textContent?.trim() || '';
31
- }
32
- }
33
-
34
- return JSON.stringify({ models, current });
35
- } catch (e) {
36
- return JSON.stringify({ models: [], current: '', error: e.message });
37
- }
38
- })()
@@ -1,48 +0,0 @@
1
- /**
2
- * Antigravity — list_modes
3
- * Conversation mode: Planning / Fast 세그먼트 컨트롤
4
- * inputBox 근처의 "Conversation mode" 패널에서 읽기
5
- * → { modes: string[], current: string }
6
- */
7
- (() => {
8
- try {
9
- const modes = [];
10
- let current = '';
11
-
12
- // "Conversation mode" 헤더를 포함하는 패널 찾기
13
- const headers = document.querySelectorAll('.text-xs.px-2.pb-1.opacity-80');
14
- for (const header of headers) {
15
- if (header.textContent?.trim() === 'Conversation mode') {
16
- // 형제 요소들에서 모드 항목 추출
17
- const parent = header.parentElement;
18
- if (!parent) continue;
19
- const items = parent.querySelectorAll('.font-medium');
20
- for (const item of items) {
21
- const text = item.textContent?.trim();
22
- if (text && text.length < 20) {
23
- modes.push(text);
24
- }
25
- }
26
- break;
27
- }
28
- }
29
-
30
- // 현재 모드: Fast 버튼의 텍스트 (현재 활성 모드 표시)
31
- const modeBtn = [...document.querySelectorAll('button')].find(b => {
32
- const cls = b.className || '';
33
- return cls.includes('py-1') && cls.includes('pl-1') && cls.includes('pr-2') && b.offsetWidth > 0;
34
- });
35
- if (modeBtn) {
36
- current = modeBtn.textContent?.trim() || '';
37
- }
38
-
39
- // modes가 비어있으면 기본값
40
- if (modes.length === 0) {
41
- modes.push('Planning', 'Fast');
42
- }
43
-
44
- return JSON.stringify({ modes, current });
45
- } catch (e) {
46
- return JSON.stringify({ modes: [], current: '', error: e.message });
47
- }
48
- })()