adhdev 0.4.0 → 0.4.1
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/cli-entrypoint.js +685 -236
- package/dist/cli-entrypoint.js.map +1 -1
- package/dist/index.js +676 -226
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/providers/_builtin/COMPATIBILITY.md +0 -217
- package/providers/_builtin/CONTRIBUTING.md +0 -141
- package/providers/_builtin/README.md +0 -51
- package/providers/_builtin/_helpers/index.js +0 -188
- package/providers/_builtin/acp/agentpool/provider.json +0 -54
- package/providers/_builtin/acp/amp/provider.json +0 -52
- package/providers/_builtin/acp/auggie/provider.json +0 -57
- package/providers/_builtin/acp/autodev/provider.json +0 -54
- package/providers/_builtin/acp/autohand/provider.json +0 -52
- package/providers/_builtin/acp/blackbox-ai/provider.json +0 -54
- package/providers/_builtin/acp/claude-agent/provider.json +0 -57
- package/providers/_builtin/acp/cline-acp/provider.json +0 -54
- package/providers/_builtin/acp/codebuddy/provider.json +0 -54
- package/providers/_builtin/acp/codex-cli/provider.json +0 -57
- package/providers/_builtin/acp/corust-agent/provider.json +0 -52
- package/providers/_builtin/acp/crow-cli/provider.json +0 -54
- package/providers/_builtin/acp/cursor-acp/provider.json +0 -54
- package/providers/_builtin/acp/deepagents/provider.json +0 -52
- package/providers/_builtin/acp/dimcode/provider.json +0 -54
- package/providers/_builtin/acp/docker-cagent/provider.json +0 -57
- package/providers/_builtin/acp/factory-droid/provider.json +0 -60
- package/providers/_builtin/acp/fast-agent/provider.json +0 -52
- package/providers/_builtin/acp/gemini-cli/provider.json +0 -114
- package/providers/_builtin/acp/github-copilot/provider.json +0 -54
- package/providers/_builtin/acp/goose/provider.json +0 -57
- package/providers/_builtin/acp/junie/provider.json +0 -52
- package/providers/_builtin/acp/kilo/provider.json +0 -54
- package/providers/_builtin/acp/kimi-cli/provider.json +0 -57
- package/providers/_builtin/acp/minion-code/provider.json +0 -52
- package/providers/_builtin/acp/mistral-vibe/provider.json +0 -57
- package/providers/_builtin/acp/nova/provider.json +0 -54
- package/providers/_builtin/acp/openclaw/provider.json +0 -54
- package/providers/_builtin/acp/opencode/provider.json +0 -52
- package/providers/_builtin/acp/openhands/provider.json +0 -54
- package/providers/_builtin/acp/pi-acp/provider.json +0 -52
- package/providers/_builtin/acp/qoder/provider.json +0 -54
- package/providers/_builtin/acp/qwen-code/provider.json +0 -60
- package/providers/_builtin/acp/stakpak/provider.json +0 -54
- package/providers/_builtin/acp/vtcode/provider.json +0 -54
- package/providers/_builtin/cli/claude-cli/provider.json +0 -100
- package/providers/_builtin/cli/codex-cli/provider.json +0 -89
- package/providers/_builtin/cli/gemini-cli/provider.json +0 -93
- package/providers/_builtin/docs/CDP_SELECTOR_GUIDE.md +0 -370
- package/providers/_builtin/docs/PROVIDER_GUIDE.md +0 -916
- package/providers/_builtin/extension/cline/provider.json +0 -35
- package/providers/_builtin/extension/cline/scripts/focus_editor.js +0 -48
- package/providers/_builtin/extension/cline/scripts/list_chats.js +0 -100
- package/providers/_builtin/extension/cline/scripts/list_models.js +0 -43
- package/providers/_builtin/extension/cline/scripts/list_modes.js +0 -35
- package/providers/_builtin/extension/cline/scripts/new_session.js +0 -85
- package/providers/_builtin/extension/cline/scripts/open_panel.js +0 -25
- package/providers/_builtin/extension/cline/scripts/read_chat.js +0 -257
- package/providers/_builtin/extension/cline/scripts/resolve_action.js +0 -83
- package/providers/_builtin/extension/cline/scripts/send_message.js +0 -95
- package/providers/_builtin/extension/cline/scripts/set_mode.js +0 -36
- package/providers/_builtin/extension/cline/scripts/set_model.js +0 -36
- package/providers/_builtin/extension/cline/scripts/switch_session.js +0 -206
- package/providers/_builtin/extension/cline/scripts.js +0 -73
- package/providers/_builtin/extension/roo-code/provider.json +0 -35
- package/providers/_builtin/extension/roo-code/scripts.js +0 -659
- package/providers/_builtin/ide/antigravity/provider.json +0 -63
- package/providers/_builtin/ide/antigravity/scripts/focus_editor.js +0 -20
- package/providers/_builtin/ide/antigravity/scripts/legacy/list_models.js +0 -38
- package/providers/_builtin/ide/antigravity/scripts/legacy/list_modes.js +0 -48
- package/providers/_builtin/ide/antigravity/scripts/legacy/scripts.js +0 -64
- package/providers/_builtin/ide/antigravity/scripts/legacy/set_mode.js +0 -34
- package/providers/_builtin/ide/antigravity/scripts/legacy/set_model.js +0 -47
- package/providers/_builtin/ide/antigravity/scripts/list_chats.js +0 -137
- package/providers/_builtin/ide/antigravity/scripts/list_models.js +0 -61
- package/providers/_builtin/ide/antigravity/scripts/list_modes.js +0 -72
- package/providers/_builtin/ide/antigravity/scripts/new_session.js +0 -75
- package/providers/_builtin/ide/antigravity/scripts/read_chat.js +0 -262
- package/providers/_builtin/ide/antigravity/scripts/resolve_action.js +0 -68
- package/providers/_builtin/ide/antigravity/scripts/send_message.js +0 -56
- package/providers/_builtin/ide/antigravity/scripts/set_mode.js +0 -67
- package/providers/_builtin/ide/antigravity/scripts/set_model.js +0 -72
- package/providers/_builtin/ide/antigravity/scripts/switch_session.js +0 -114
- package/providers/_builtin/ide/antigravity/scripts.js +0 -67
- package/providers/_builtin/ide/cursor/provider.json +0 -59
- package/providers/_builtin/ide/cursor/scripts.js +0 -458
- package/providers/_builtin/ide/kiro/provider.json +0 -60
- package/providers/_builtin/ide/kiro/scripts/focus_editor.js +0 -20
- package/providers/_builtin/ide/kiro/scripts/open_panel.js +0 -47
- package/providers/_builtin/ide/kiro/scripts/resolve_action.js +0 -54
- package/providers/_builtin/ide/kiro/scripts/send_message.js +0 -29
- package/providers/_builtin/ide/kiro/scripts/webview_list_models.js +0 -39
- package/providers/_builtin/ide/kiro/scripts/webview_list_modes.js +0 -39
- package/providers/_builtin/ide/kiro/scripts/webview_list_sessions.js +0 -21
- package/providers/_builtin/ide/kiro/scripts/webview_new_session.js +0 -34
- package/providers/_builtin/ide/kiro/scripts/webview_read_chat.js +0 -68
- package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +0 -72
- package/providers/_builtin/ide/kiro/scripts/webview_set_mode.js +0 -15
- package/providers/_builtin/ide/kiro/scripts/webview_set_model.js +0 -15
- package/providers/_builtin/ide/kiro/scripts/webview_switch_session.js +0 -26
- package/providers/_builtin/ide/kiro/scripts.js +0 -62
- package/providers/_builtin/ide/pearai/provider.json +0 -60
- package/providers/_builtin/ide/pearai/scripts/focus_editor.js +0 -20
- package/providers/_builtin/ide/pearai/scripts/list_sessions.js +0 -38
- package/providers/_builtin/ide/pearai/scripts/new_session.js +0 -55
- package/providers/_builtin/ide/pearai/scripts/open_panel.js +0 -46
- package/providers/_builtin/ide/pearai/scripts/resolve_action.js +0 -54
- package/providers/_builtin/ide/pearai/scripts/send_message.js +0 -29
- package/providers/_builtin/ide/pearai/scripts/webview_list_models.js +0 -43
- package/providers/_builtin/ide/pearai/scripts/webview_list_modes.js +0 -35
- package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +0 -62
- package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +0 -49
- package/providers/_builtin/ide/pearai/scripts/webview_read_chat.js +0 -92
- package/providers/_builtin/ide/pearai/scripts/webview_resolve_action.js +0 -59
- package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +0 -72
- package/providers/_builtin/ide/pearai/scripts/webview_set_mode.js +0 -36
- package/providers/_builtin/ide/pearai/scripts/webview_set_model.js +0 -36
- package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +0 -34
- package/providers/_builtin/ide/pearai/scripts.js +0 -74
- package/providers/_builtin/ide/trae/provider.json +0 -59
- package/providers/_builtin/ide/trae/scripts/focus_editor.js +0 -20
- package/providers/_builtin/ide/trae/scripts/list_chats.js +0 -24
- package/providers/_builtin/ide/trae/scripts/list_models.js +0 -39
- package/providers/_builtin/ide/trae/scripts/list_modes.js +0 -39
- package/providers/_builtin/ide/trae/scripts/new_session.js +0 -30
- package/providers/_builtin/ide/trae/scripts/open_panel.js +0 -44
- package/providers/_builtin/ide/trae/scripts/read_chat.js +0 -113
- package/providers/_builtin/ide/trae/scripts/resolve_action.js +0 -54
- package/providers/_builtin/ide/trae/scripts/send_message.js +0 -69
- package/providers/_builtin/ide/trae/scripts/set_mode.js +0 -15
- package/providers/_builtin/ide/trae/scripts/set_model.js +0 -15
- package/providers/_builtin/ide/trae/scripts/switch_session.js +0 -23
- package/providers/_builtin/ide/trae/scripts.js +0 -57
- package/providers/_builtin/ide/vscode/provider.json +0 -57
- package/providers/_builtin/ide/vscode-insiders/provider.json +0 -55
- package/providers/_builtin/ide/vscodium/provider.json +0 -56
- package/providers/_builtin/ide/windsurf/provider.json +0 -46
- package/providers/_builtin/ide/windsurf/scripts/focus_editor.js +0 -30
- package/providers/_builtin/ide/windsurf/scripts/list_chats.js +0 -117
- package/providers/_builtin/ide/windsurf/scripts/list_models.js +0 -39
- package/providers/_builtin/ide/windsurf/scripts/list_modes.js +0 -39
- package/providers/_builtin/ide/windsurf/scripts/new_session.js +0 -69
- package/providers/_builtin/ide/windsurf/scripts/open_panel.js +0 -58
- package/providers/_builtin/ide/windsurf/scripts/read_chat.js +0 -297
- package/providers/_builtin/ide/windsurf/scripts/resolve_action.js +0 -68
- package/providers/_builtin/ide/windsurf/scripts/send_message.js +0 -87
- package/providers/_builtin/ide/windsurf/scripts/set_mode.js +0 -15
- package/providers/_builtin/ide/windsurf/scripts/set_model.js +0 -15
- package/providers/_builtin/ide/windsurf/scripts/switch_session.js +0 -58
- package/providers/_builtin/ide/windsurf/scripts.js +0 -57
- package/providers/_builtin/validate.js +0 -156
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Windsurf v1 — read_chat (v1 — Cascade DOM + Fiber)
|
|
3
|
-
*
|
|
4
|
-
* Windsurf는 VS Code 포크, 채팅 UI를 "Cascade"라고 부릅니다.
|
|
5
|
-
*
|
|
6
|
-
* DOM 구조:
|
|
7
|
-
* #windsurf.cascadePanel → .chat-client-root
|
|
8
|
-
* 스크롤: .cascade-scrollbar
|
|
9
|
-
* 메시지 리스트: .cascade-scrollbar .pb-20 > .flex.flex-col > .flex.flex-col.gap-2\.5
|
|
10
|
-
* 사용자 메시지: hasProse=false, hasWhitespace=true
|
|
11
|
-
* AI 메시지: [class*="prose"] (prose-sm)
|
|
12
|
-
* 피드백 UI: .mark-js-ignore (무시)
|
|
13
|
-
* 입력: [data-lexical-editor="true"]
|
|
14
|
-
*
|
|
15
|
-
* Fiber props:
|
|
16
|
-
* cascadeId: 세션 ID
|
|
17
|
-
* isRunning: 생성 중 여부
|
|
18
|
-
* hasPendingTerminalCommand: 승인 대기
|
|
19
|
-
* copyableText: AI 응답 마크다운 원본
|
|
20
|
-
*
|
|
21
|
-
* 최종 확인: Windsurf (2026-03-06)
|
|
22
|
-
*/
|
|
23
|
-
(() => {
|
|
24
|
-
try {
|
|
25
|
-
// ─── 1. 컨테이너 ───
|
|
26
|
-
const cascade = document.querySelector('#windsurf\\.cascadePanel')
|
|
27
|
-
|| document.querySelector('.chat-client-root');
|
|
28
|
-
if (!cascade) {
|
|
29
|
-
return { id: 'no_cascade', status: 'idle', title: 'No Cascade', messages: [], inputContent: '', activeModal: null };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// ─── 2. Fiber에서 cascadeId, isRunning 추출 (턴 요소에서 탐색) ───
|
|
33
|
-
let cascadeId = 'cascade';
|
|
34
|
-
let isRunning = false;
|
|
35
|
-
let hasPendingCmd = false;
|
|
36
|
-
try {
|
|
37
|
-
// 턴 요소에서 Fiber 탐색 (cascadePanel 루트보다 깊이 6에서 cascadeId 발견)
|
|
38
|
-
const scrollArea = cascade.querySelector('.cascade-scrollbar');
|
|
39
|
-
const gapEls = scrollArea ? scrollArea.querySelectorAll('[class*="gap-2"]') : [];
|
|
40
|
-
let firstTurn = null;
|
|
41
|
-
for (const el of gapEls) {
|
|
42
|
-
if (el.children.length >= 1 && el.closest('.cascade-scrollbar')) {
|
|
43
|
-
firstTurn = el.children[0]; break;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const fiberTarget = firstTurn || cascade;
|
|
47
|
-
const fk = Object.keys(fiberTarget).find(k => k.startsWith('__reactFiber'));
|
|
48
|
-
if (fk) {
|
|
49
|
-
let fiber = fiberTarget[fk];
|
|
50
|
-
for (let d = 0; d < 50 && fiber; d++) {
|
|
51
|
-
const p = fiber.memoizedProps || fiber.pendingProps || {};
|
|
52
|
-
if (p.cascadeId && typeof p.cascadeId === 'string') cascadeId = p.cascadeId;
|
|
53
|
-
if (p.isRunning === true) isRunning = true;
|
|
54
|
-
if (p.hasPendingTerminalCommand === true) hasPendingCmd = true;
|
|
55
|
-
fiber = fiber.return;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
} catch (_) { }
|
|
59
|
-
|
|
60
|
-
// ─── 3. 상태 감지 ───
|
|
61
|
-
let status = 'idle';
|
|
62
|
-
if (isRunning) status = 'generating';
|
|
63
|
-
|
|
64
|
-
// Signal A: Stop 버튼
|
|
65
|
-
if (status === 'idle') {
|
|
66
|
-
const allBtns = Array.from(document.querySelectorAll('button'));
|
|
67
|
-
const stopBtn = allBtns.find(b => {
|
|
68
|
-
if (b.offsetWidth === 0) return false;
|
|
69
|
-
const label = (b.getAttribute('aria-label') || '').toLowerCase();
|
|
70
|
-
const text = (b.textContent || '').trim().toLowerCase();
|
|
71
|
-
return label.includes('stop') || label === 'cancel generation'
|
|
72
|
-
|| text === 'stop' || text === 'stop generating';
|
|
73
|
-
});
|
|
74
|
-
if (stopBtn) status = 'generating';
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Signal B: 입력창 placeholder
|
|
78
|
-
if (status === 'idle') {
|
|
79
|
-
const editor = cascade.querySelector('[data-lexical-editor="true"]');
|
|
80
|
-
if (editor) {
|
|
81
|
-
const ph = (editor.getAttribute('placeholder') || '').toLowerCase();
|
|
82
|
-
if (ph.includes('wait') || ph.includes('generating')) status = 'generating';
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const title = document.title.split(' \u2014 ')[0].trim() || 'Cascade';
|
|
87
|
-
|
|
88
|
-
// ─── 4. HTML → Markdown 변환기 ───
|
|
89
|
-
function htmlToMd(node) {
|
|
90
|
-
if (node.nodeType === 3) return node.textContent || '';
|
|
91
|
-
if (node.nodeType !== 1) return '';
|
|
92
|
-
const tag = node.tagName;
|
|
93
|
-
if (tag === 'STYLE' || tag === 'SCRIPT' || tag === 'SVG') return '';
|
|
94
|
-
if (tag === 'TABLE') {
|
|
95
|
-
const rows = Array.from(node.querySelectorAll('tr'));
|
|
96
|
-
if (rows.length === 0) return '';
|
|
97
|
-
const table = rows.map(tr =>
|
|
98
|
-
Array.from(tr.querySelectorAll('th, td')).map(cell => (cell.textContent || '').trim().replace(/\|/g, '\\|'))
|
|
99
|
-
);
|
|
100
|
-
const colCount = Math.max(...table.map(r => r.length));
|
|
101
|
-
const header = table[0] || [];
|
|
102
|
-
const sep = Array(colCount).fill('---');
|
|
103
|
-
const body = table.slice(1);
|
|
104
|
-
let md = '| ' + header.join(' | ') + ' |\n';
|
|
105
|
-
md += '| ' + sep.join(' | ') + ' |\n';
|
|
106
|
-
for (const row of body) {
|
|
107
|
-
while (row.length < colCount) row.push('');
|
|
108
|
-
md += '| ' + row.join(' | ') + ' |\n';
|
|
109
|
-
}
|
|
110
|
-
return '\n' + md + '\n';
|
|
111
|
-
}
|
|
112
|
-
if (tag === 'UL') return '\n' + Array.from(node.children).map(li => '- ' + childrenToMd(li).trim()).join('\n') + '\n';
|
|
113
|
-
if (tag === 'OL') return '\n' + Array.from(node.children).map((li, i) => (i + 1) + '. ' + childrenToMd(li).trim()).join('\n') + '\n';
|
|
114
|
-
if (tag === 'LI') return childrenToMd(node);
|
|
115
|
-
if (tag === 'H1') return '\n# ' + childrenToMd(node).trim() + '\n';
|
|
116
|
-
if (tag === 'H2') return '\n## ' + childrenToMd(node).trim() + '\n';
|
|
117
|
-
if (tag === 'H3') return '\n### ' + childrenToMd(node).trim() + '\n';
|
|
118
|
-
if (tag === 'H4') return '\n#### ' + childrenToMd(node).trim() + '\n';
|
|
119
|
-
if (tag === 'STRONG' || tag === 'B') return '**' + childrenToMd(node).trim() + '**';
|
|
120
|
-
if (tag === 'EM' || tag === 'I') return '*' + childrenToMd(node).trim() + '*';
|
|
121
|
-
if (tag === 'PRE') {
|
|
122
|
-
const codeEl = node.querySelector('code');
|
|
123
|
-
const lang = codeEl ? (codeEl.className.match(/language-(\w+)/)?.[1] || '') : '';
|
|
124
|
-
const code = (codeEl || node).textContent || '';
|
|
125
|
-
return '\n```' + lang + '\n' + code.trim() + '\n```\n';
|
|
126
|
-
}
|
|
127
|
-
if (tag === 'CODE') {
|
|
128
|
-
if (node.parentElement && node.parentElement.tagName === 'PRE') return node.textContent || '';
|
|
129
|
-
return '`' + (node.textContent || '').trim() + '`';
|
|
130
|
-
}
|
|
131
|
-
if (tag === 'BLOCKQUOTE') return '\n> ' + childrenToMd(node).trim().replace(/\n/g, '\n> ') + '\n';
|
|
132
|
-
if (tag === 'A') return '[' + childrenToMd(node).trim() + '](' + (node.getAttribute('href') || '') + ')';
|
|
133
|
-
if (tag === 'BR') return '\n';
|
|
134
|
-
if (tag === 'P') return '\n' + childrenToMd(node).trim() + '\n';
|
|
135
|
-
return childrenToMd(node);
|
|
136
|
-
}
|
|
137
|
-
function childrenToMd(node) {
|
|
138
|
-
return Array.from(node.childNodes).map(htmlToMd).join('');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function getCleanMd(el) {
|
|
142
|
-
const clone = el.cloneNode(true);
|
|
143
|
-
clone.querySelectorAll('button, [role="button"], style, script, svg, .codicon, [class*="feedback"]').forEach(n => n.remove());
|
|
144
|
-
clone.querySelectorAll('*').forEach(child => {
|
|
145
|
-
if (!child.parentNode) return;
|
|
146
|
-
const t = (child.textContent || '').trim();
|
|
147
|
-
if (t.length > 60) return;
|
|
148
|
-
const low = t.toLowerCase();
|
|
149
|
-
if (/^(analyzed\s+\d|edited\s+\d|ran\s+\S|terminal\s|reading|searching)/i.test(low)) child.remove();
|
|
150
|
-
});
|
|
151
|
-
let md = htmlToMd(clone);
|
|
152
|
-
md = md.replace(/\n{3,}/g, '\n\n').trim();
|
|
153
|
-
return md;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// ─── 5. 메시지 수집 ───
|
|
157
|
-
const collected = [];
|
|
158
|
-
const seenHashes = new Set();
|
|
159
|
-
|
|
160
|
-
const scrollArea = cascade.querySelector('.cascade-scrollbar');
|
|
161
|
-
if (!scrollArea) {
|
|
162
|
-
return { id: cascadeId, status, title, messages: [], inputContent: '', activeModal: null };
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// 메시지 리스트 컨테이너 찾기: .gap-2.5 중 자식 2개 이상
|
|
166
|
-
let msgContainer = null;
|
|
167
|
-
const gapEls = scrollArea.querySelectorAll('[class*="gap-2"]');
|
|
168
|
-
for (const el of gapEls) {
|
|
169
|
-
if (el.children.length >= 2 && el.closest('.cascade-scrollbar')) {
|
|
170
|
-
msgContainer = el; break;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (msgContainer) {
|
|
175
|
-
const turns = Array.from(msgContainer.children);
|
|
176
|
-
for (const turn of turns) {
|
|
177
|
-
// .mark-js-ignore = 피드백 UI → 무시
|
|
178
|
-
if ((turn.className || '').includes('mark-js-ignore')) continue;
|
|
179
|
-
if (turn.offsetHeight < 10) continue;
|
|
180
|
-
|
|
181
|
-
// 역할 판별: .prose = AI, 아니면 user
|
|
182
|
-
const proseEl = turn.querySelector('[class*="prose"]');
|
|
183
|
-
const role = proseEl ? 'assistant' : 'user';
|
|
184
|
-
|
|
185
|
-
let text = '';
|
|
186
|
-
if (role === 'assistant') {
|
|
187
|
-
// AI: Fiber copyableText가 있으면 사용 (이미 마크다운!)
|
|
188
|
-
try {
|
|
189
|
-
const fk = Object.keys(turn).find(k => k.startsWith('__reactFiber'));
|
|
190
|
-
if (fk) {
|
|
191
|
-
let fiber = turn[fk];
|
|
192
|
-
for (let d = 0; d < 20 && fiber; d++) {
|
|
193
|
-
const p = fiber.memoizedProps || {};
|
|
194
|
-
if (p.copyableText && typeof p.copyableText === 'string' && p.copyableText.length > 5) {
|
|
195
|
-
text = p.copyableText;
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
fiber = fiber.return;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
} catch (_) { }
|
|
202
|
-
|
|
203
|
-
// Fiber에 없으면 HTML→Markdown
|
|
204
|
-
if (!text) {
|
|
205
|
-
const mdRoot = proseEl || turn;
|
|
206
|
-
text = getCleanMd(mdRoot);
|
|
207
|
-
}
|
|
208
|
-
} else {
|
|
209
|
-
// 사용자: whitespace-pre-wrap 요소에서 텍스트 추출
|
|
210
|
-
const whitespace = turn.querySelector('[class*="whitespace"]');
|
|
211
|
-
text = (whitespace || turn).innerText?.trim() || '';
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (!text || text.length < 1) continue;
|
|
215
|
-
|
|
216
|
-
const hash = role + ':' + text.slice(0, 200);
|
|
217
|
-
if (seenHashes.has(hash)) continue;
|
|
218
|
-
seenHashes.add(hash);
|
|
219
|
-
collected.push({ role, text, el: turn });
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// DOM 순서 정렬
|
|
224
|
-
collected.sort((a, b) => {
|
|
225
|
-
const pos = a.el.compareDocumentPosition(b.el);
|
|
226
|
-
if (pos & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
|
|
227
|
-
if (pos & Node.DOCUMENT_POSITION_PRECEDING) return 1;
|
|
228
|
-
return 0;
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
// 최신 30개만
|
|
232
|
-
const trimmed = collected.length > 30 ? collected.slice(-30) : collected;
|
|
233
|
-
|
|
234
|
-
const final = trimmed.map((m, i) => ({
|
|
235
|
-
id: 'msg_' + i,
|
|
236
|
-
role: m.role,
|
|
237
|
-
content: m.text.length > 6000 ? m.text.slice(0, 6000) + '\n[... truncated]' : m.text,
|
|
238
|
-
index: i,
|
|
239
|
-
kind: 'standard'
|
|
240
|
-
}));
|
|
241
|
-
|
|
242
|
-
// ─── 6. 입력창 ───
|
|
243
|
-
const editor = cascade.querySelector('[data-lexical-editor="true"]')
|
|
244
|
-
|| cascade.querySelector('[contenteditable="true"][role="textbox"]')
|
|
245
|
-
|| cascade.querySelector('textarea:not(.xterm-helper-textarea)');
|
|
246
|
-
const inputContent = editor ? (editor.innerText || editor.value || '').trim() : '';
|
|
247
|
-
|
|
248
|
-
// ─── 7. 모달/승인 감지 ───
|
|
249
|
-
let activeModal = null;
|
|
250
|
-
try {
|
|
251
|
-
// Fiber: hasPendingTerminalCommand
|
|
252
|
-
if (hasPendingCmd) {
|
|
253
|
-
const allBtns = Array.from(document.querySelectorAll('button')).filter(b => b.offsetWidth > 0);
|
|
254
|
-
const approvalBtns = allBtns.filter(b => {
|
|
255
|
-
const t = (b.textContent || '').trim().toLowerCase();
|
|
256
|
-
return /^(run|reject|skip|approve|allow|deny|cancel|accept)\b/i.test(t);
|
|
257
|
-
});
|
|
258
|
-
const btnTexts = [...new Set(approvalBtns.map(b => (b.textContent || '').trim()).filter(t => t.length < 40))];
|
|
259
|
-
activeModal = { message: 'Terminal command pending', buttons: btnTexts.length > 0 ? btnTexts : ['Run', 'Reject'] };
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Dialog 폴백
|
|
263
|
-
if (!activeModal) {
|
|
264
|
-
const dialog = document.querySelector('.monaco-dialog-box, [role="dialog"]');
|
|
265
|
-
if (dialog && dialog.offsetWidth > 80) {
|
|
266
|
-
const msg = (dialog.querySelector('.dialog-message') || dialog).innerText?.trim() || '';
|
|
267
|
-
const buttons = Array.from(dialog.querySelectorAll('.monaco-button, button'))
|
|
268
|
-
.map(b => (b.innerText || '').trim()).filter(t => t.length > 0 && t.length < 30);
|
|
269
|
-
if (msg || buttons.length > 0) {
|
|
270
|
-
activeModal = { message: msg.slice(0, 300), buttons };
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// 인라인 승인 버튼
|
|
276
|
-
if (!activeModal) {
|
|
277
|
-
const allBtns = Array.from(document.querySelectorAll('button')).filter(b => b.offsetWidth > 0);
|
|
278
|
-
const approvalBtns = allBtns.filter(b => {
|
|
279
|
-
const t = (b.textContent || '').trim().toLowerCase();
|
|
280
|
-
if (t.length > 40) return false;
|
|
281
|
-
return /^(run|reject|skip|approve|allow|deny)\b/i.test(t)
|
|
282
|
-
|| t === 'always allow' || t === 'always deny';
|
|
283
|
-
});
|
|
284
|
-
if (approvalBtns.length >= 2) {
|
|
285
|
-
const btnTexts = [...new Set(approvalBtns.map(b => (b.textContent || '').trim()))];
|
|
286
|
-
activeModal = { message: '', buttons: btnTexts };
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (activeModal) status = 'waiting_approval';
|
|
291
|
-
} catch (_) { activeModal = null; }
|
|
292
|
-
|
|
293
|
-
return { id: cascadeId, status, title, messages: final, inputContent, activeModal };
|
|
294
|
-
} catch (e) {
|
|
295
|
-
return { id: 'error', status: 'error', error: e.message, messages: [] };
|
|
296
|
-
}
|
|
297
|
-
})()
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Windsurf v1 — resolve_action
|
|
3
|
-
*
|
|
4
|
-
* 승인 다이얼로그/인라인 버튼을 클릭합니다.
|
|
5
|
-
*
|
|
6
|
-
* 파라미터: ${ BUTTON_TEXT } — 클릭할 버튼 텍스트 (lowercase)
|
|
7
|
-
*
|
|
8
|
-
* 최종 확인: Windsurf (2026-03-06)
|
|
9
|
-
*/
|
|
10
|
-
(() => {
|
|
11
|
-
try {
|
|
12
|
-
const want = ${ BUTTON_TEXT };
|
|
13
|
-
const normalize = (s) => (s || '').replace(/[\s\u200b\u00a0]+/g, ' ').trim().toLowerCase();
|
|
14
|
-
const matches = (text) => {
|
|
15
|
-
const t = normalize(text);
|
|
16
|
-
if (!t) return false;
|
|
17
|
-
if (t === want) return true;
|
|
18
|
-
if (t.indexOf(want) === 0) return true;
|
|
19
|
-
if (want === 'run' && (/^run\s*/.test(t) || t === 'enter' || t === '⏎')) return true;
|
|
20
|
-
if (want === 'approve' && (t.includes('approve') || t === 'always allow' || t === 'allow')) return true;
|
|
21
|
-
if (want === 'reject' && (t.includes('reject') || t === 'deny' || t === 'always deny')) return true;
|
|
22
|
-
return false;
|
|
23
|
-
};
|
|
24
|
-
const click = (el) => {
|
|
25
|
-
el.focus?.();
|
|
26
|
-
const rect = el.getBoundingClientRect();
|
|
27
|
-
const x = rect.left + rect.width / 2;
|
|
28
|
-
const y = rect.top + rect.height / 2;
|
|
29
|
-
for (const type of ['pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click']) {
|
|
30
|
-
el.dispatchEvent(new PointerEvent(type, {
|
|
31
|
-
bubbles: true, cancelable: true, view: window,
|
|
32
|
-
clientX: x, clientY: y, pointerId: 1, pointerType: 'mouse'
|
|
33
|
-
}));
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// 1. Dialog 내부
|
|
39
|
-
const dialog = document.querySelector('.monaco-dialog-box, [role="dialog"]');
|
|
40
|
-
if (dialog && dialog.offsetWidth > 80) {
|
|
41
|
-
const btns = dialog.querySelectorAll('.monaco-button, button');
|
|
42
|
-
for (const b of btns) {
|
|
43
|
-
if (matches(b.textContent)) return click(b);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 2. 모든 보이는 버튼
|
|
48
|
-
const sel = 'button, [role="button"], .monaco-button';
|
|
49
|
-
const allBtns = Array.from(document.querySelectorAll(sel))
|
|
50
|
-
.filter(b => b.offsetWidth > 0 && b.offsetHeight > 0);
|
|
51
|
-
for (const b of allBtns) {
|
|
52
|
-
if (matches(b.textContent)) return click(b);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// 3. Enter 키 폴백 (run)
|
|
56
|
-
if (want === 'run') {
|
|
57
|
-
document.activeElement?.dispatchEvent(new KeyboardEvent('keydown', {
|
|
58
|
-
key: 'Enter', code: 'Enter', keyCode: 13,
|
|
59
|
-
bubbles: true, cancelable: true
|
|
60
|
-
}));
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return false;
|
|
65
|
-
} catch (e) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
})()
|
|
@@ -1,87 +0,0 @@
|
|
|
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
|
-
})()
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
})()
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
})()
|
|
@@ -1,58 +0,0 @@
|
|
|
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,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CDP Scripts for Windsurf
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const SCRIPTS_DIR = path.join(__dirname, 'scripts');
|
|
8
|
-
function loadScript(name) {
|
|
9
|
-
try { return fs.readFileSync(path.join(SCRIPTS_DIR, name), 'utf8'); }
|
|
10
|
-
catch { return null; }
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
module.exports.readChat = function readChat() { return loadScript('read_chat.js'); };
|
|
15
|
-
|
|
16
|
-
module.exports.sendMessage = function sendMessage(text) {
|
|
17
|
-
const s = loadScript('send_message.js');
|
|
18
|
-
return s ? s.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text)) : null;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
module.exports.listSessions = function listSessions() { return loadScript('list_chats.js'); };
|
|
22
|
-
|
|
23
|
-
module.exports.switchSession = function switchSession(sessionId) {
|
|
24
|
-
const s = loadScript('switch_session.js');
|
|
25
|
-
return s ? s.replace(/\$\{\s*SESSION_ID\s*\}/g, JSON.stringify(sessionId)) : null;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
module.exports.newSession = function newSession() { return loadScript('new_session.js'); };
|
|
29
|
-
|
|
30
|
-
module.exports.resolveAction = function resolveAction(params) {
|
|
31
|
-
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
32
|
-
const buttonText = params?.button || params?.buttonText
|
|
33
|
-
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
|
|
34
|
-
const s = loadScript('resolve_action.js');
|
|
35
|
-
return s ? s.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText)) : null;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
module.exports.focusEditor = function focusEditor() { return loadScript('focus_editor.js'); };
|
|
39
|
-
|
|
40
|
-
module.exports.listModels = function listModels() { return loadScript('list_models.js'); };
|
|
41
|
-
|
|
42
|
-
module.exports.setModel = function setModel(params) {
|
|
43
|
-
const model = typeof params === 'string' ? params : params?.model;
|
|
44
|
-
const s = loadScript('set_model.js');
|
|
45
|
-
return s ? s.replace(/\$\{\s*MODEL\s*\}/g, JSON.stringify(model)) : null;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
module.exports.listModes = function listModes() { return loadScript('list_modes.js'); };
|
|
49
|
-
|
|
50
|
-
module.exports.setMode = function setMode(params) {
|
|
51
|
-
const mode = typeof params === 'string' ? params : params?.mode;
|
|
52
|
-
const s = loadScript('set_mode.js');
|
|
53
|
-
return s ? s.replace(/\$\{\s*MODE\s*\}/g, JSON.stringify(mode)) : null;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
module.exports.openPanel = function openPanel() { return loadScript('open_panel.js'); };
|
|
57
|
-
|