@adhdev/daemon-core 0.5.3
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.d.ts +2662 -0
- package/dist/index.js +11341 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
- package/providers/_builtin/.github/workflows/generate-registry.yml +57 -0
- package/providers/_builtin/COMPATIBILITY.md +217 -0
- package/providers/_builtin/CONTRIBUTING.md +200 -0
- package/providers/_builtin/README.md +119 -0
- package/providers/_builtin/_helpers/index.js +188 -0
- package/providers/_builtin/acp/agentpool/provider.json +54 -0
- package/providers/_builtin/acp/amp/provider.json +52 -0
- package/providers/_builtin/acp/auggie/provider.json +57 -0
- package/providers/_builtin/acp/autodev/provider.json +54 -0
- package/providers/_builtin/acp/autohand/provider.json +52 -0
- package/providers/_builtin/acp/blackbox-ai/provider.json +54 -0
- package/providers/_builtin/acp/claude-agent/provider.json +57 -0
- package/providers/_builtin/acp/cline-acp/provider.json +54 -0
- package/providers/_builtin/acp/codebuddy/provider.json +54 -0
- package/providers/_builtin/acp/codex-cli/provider.json +57 -0
- package/providers/_builtin/acp/corust-agent/provider.json +52 -0
- package/providers/_builtin/acp/crow-cli/provider.json +54 -0
- package/providers/_builtin/acp/cursor-acp/provider.json +54 -0
- package/providers/_builtin/acp/deepagents/provider.json +52 -0
- package/providers/_builtin/acp/dimcode/provider.json +54 -0
- package/providers/_builtin/acp/docker-cagent/provider.json +57 -0
- package/providers/_builtin/acp/factory-droid/provider.json +60 -0
- package/providers/_builtin/acp/fast-agent/provider.json +52 -0
- package/providers/_builtin/acp/gemini-cli/provider.json +114 -0
- package/providers/_builtin/acp/github-copilot/provider.json +54 -0
- package/providers/_builtin/acp/goose/provider.json +57 -0
- package/providers/_builtin/acp/junie/provider.json +52 -0
- package/providers/_builtin/acp/kilo/provider.json +54 -0
- package/providers/_builtin/acp/kimi-cli/provider.json +57 -0
- package/providers/_builtin/acp/minion-code/provider.json +52 -0
- package/providers/_builtin/acp/mistral-vibe/provider.json +57 -0
- package/providers/_builtin/acp/nova/provider.json +54 -0
- package/providers/_builtin/acp/openclaw/provider.json +54 -0
- package/providers/_builtin/acp/opencode/provider.json +52 -0
- package/providers/_builtin/acp/openhands/provider.json +54 -0
- package/providers/_builtin/acp/pi-acp/provider.json +52 -0
- package/providers/_builtin/acp/qoder/provider.json +54 -0
- package/providers/_builtin/acp/qwen-code/provider.json +60 -0
- package/providers/_builtin/acp/stakpak/provider.json +54 -0
- package/providers/_builtin/acp/vtcode/provider.json +54 -0
- package/providers/_builtin/cli/claude-cli/provider.json +100 -0
- package/providers/_builtin/cli/codex-cli/provider.json +89 -0
- package/providers/_builtin/cli/gemini-cli/provider.json +93 -0
- package/providers/_builtin/docs/CDP_SELECTOR_GUIDE.md +370 -0
- package/providers/_builtin/docs/PROVIDER_GUIDE.md +916 -0
- package/providers/_builtin/extension/cline/provider.json +35 -0
- package/providers/_builtin/extension/cline/scripts/focus_editor.js +48 -0
- package/providers/_builtin/extension/cline/scripts/list_chats.js +100 -0
- package/providers/_builtin/extension/cline/scripts/list_models.js +43 -0
- package/providers/_builtin/extension/cline/scripts/list_modes.js +35 -0
- package/providers/_builtin/extension/cline/scripts/new_session.js +85 -0
- package/providers/_builtin/extension/cline/scripts/open_panel.js +25 -0
- package/providers/_builtin/extension/cline/scripts/read_chat.js +257 -0
- package/providers/_builtin/extension/cline/scripts/resolve_action.js +83 -0
- package/providers/_builtin/extension/cline/scripts/send_message.js +95 -0
- package/providers/_builtin/extension/cline/scripts/set_mode.js +36 -0
- package/providers/_builtin/extension/cline/scripts/set_model.js +36 -0
- package/providers/_builtin/extension/cline/scripts/switch_session.js +206 -0
- package/providers/_builtin/extension/cline/scripts.js +73 -0
- package/providers/_builtin/extension/roo-code/provider.json +35 -0
- package/providers/_builtin/extension/roo-code/scripts.js +659 -0
- package/providers/_builtin/ide/antigravity/provider.json +68 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/focus_editor.js +20 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/list_chats.js +137 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/list_models.js +38 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/list_modes.js +48 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/new_session.js +75 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/read_chat.js +262 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/resolve_action.js +68 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/scripts.js +57 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/send_message.js +56 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/set_mode.js +34 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/set_model.js +47 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/switch_session.js +114 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/focus_editor.js +20 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/list_chats.js +137 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/list_models.js +61 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/list_modes.js +72 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/new_session.js +75 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/read_chat.js +262 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/resolve_action.js +68 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/scripts.js +67 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/send_message.js +56 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/set_mode.js +67 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/set_model.js +72 -0
- package/providers/_builtin/ide/antigravity/scripts/1.107/switch_session.js +114 -0
- package/providers/_builtin/ide/cursor/provider.json +70 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/dismiss_notification.js +30 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/focus_editor.js +13 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/list_models.js +78 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/list_modes.js +40 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/list_notifications.js +23 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/list_sessions.js +42 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/new_session.js +20 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/open_panel.js +23 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/read_chat.js +75 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/resolve_action.js +19 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/scripts.js +78 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/send_message.js +23 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/set_mode.js +38 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/set_model.js +81 -0
- package/providers/_builtin/ide/cursor/scripts/0.49/switch_session.js +28 -0
- package/providers/_builtin/ide/kiro/provider.json +67 -0
- package/providers/_builtin/ide/kiro/scripts/focus_editor.js +20 -0
- package/providers/_builtin/ide/kiro/scripts/open_panel.js +47 -0
- package/providers/_builtin/ide/kiro/scripts/resolve_action.js +54 -0
- package/providers/_builtin/ide/kiro/scripts/send_message.js +29 -0
- package/providers/_builtin/ide/kiro/scripts/webview_list_models.js +39 -0
- package/providers/_builtin/ide/kiro/scripts/webview_list_modes.js +39 -0
- package/providers/_builtin/ide/kiro/scripts/webview_list_sessions.js +21 -0
- package/providers/_builtin/ide/kiro/scripts/webview_new_session.js +34 -0
- package/providers/_builtin/ide/kiro/scripts/webview_read_chat.js +68 -0
- package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/kiro/scripts/webview_set_mode.js +15 -0
- package/providers/_builtin/ide/kiro/scripts/webview_set_model.js +15 -0
- package/providers/_builtin/ide/kiro/scripts/webview_switch_session.js +26 -0
- package/providers/_builtin/ide/kiro/scripts.js +62 -0
- package/providers/_builtin/ide/pearai/provider.json +67 -0
- package/providers/_builtin/ide/pearai/scripts/focus_editor.js +20 -0
- package/providers/_builtin/ide/pearai/scripts/list_sessions.js +38 -0
- package/providers/_builtin/ide/pearai/scripts/new_session.js +55 -0
- package/providers/_builtin/ide/pearai/scripts/open_panel.js +46 -0
- package/providers/_builtin/ide/pearai/scripts/resolve_action.js +54 -0
- package/providers/_builtin/ide/pearai/scripts/send_message.js +29 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_models.js +43 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_modes.js +35 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +62 -0
- package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +49 -0
- package/providers/_builtin/ide/pearai/scripts/webview_read_chat.js +92 -0
- package/providers/_builtin/ide/pearai/scripts/webview_resolve_action.js +59 -0
- package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/pearai/scripts/webview_set_mode.js +36 -0
- package/providers/_builtin/ide/pearai/scripts/webview_set_model.js +36 -0
- package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +34 -0
- package/providers/_builtin/ide/pearai/scripts.js +74 -0
- package/providers/_builtin/ide/trae/provider.json +66 -0
- package/providers/_builtin/ide/trae/scripts/focus_editor.js +20 -0
- package/providers/_builtin/ide/trae/scripts/list_chats.js +24 -0
- package/providers/_builtin/ide/trae/scripts/list_models.js +39 -0
- package/providers/_builtin/ide/trae/scripts/list_modes.js +39 -0
- package/providers/_builtin/ide/trae/scripts/new_session.js +30 -0
- package/providers/_builtin/ide/trae/scripts/open_panel.js +44 -0
- package/providers/_builtin/ide/trae/scripts/read_chat.js +113 -0
- package/providers/_builtin/ide/trae/scripts/resolve_action.js +54 -0
- package/providers/_builtin/ide/trae/scripts/send_message.js +69 -0
- package/providers/_builtin/ide/trae/scripts/set_mode.js +15 -0
- package/providers/_builtin/ide/trae/scripts/set_model.js +15 -0
- package/providers/_builtin/ide/trae/scripts/switch_session.js +23 -0
- package/providers/_builtin/ide/trae/scripts.js +57 -0
- package/providers/_builtin/ide/vscode/provider.json +64 -0
- package/providers/_builtin/ide/vscode-insiders/provider.json +62 -0
- package/providers/_builtin/ide/vscodium/provider.json +63 -0
- package/providers/_builtin/ide/windsurf/provider.json +53 -0
- package/providers/_builtin/ide/windsurf/scripts/focus_editor.js +30 -0
- package/providers/_builtin/ide/windsurf/scripts/list_chats.js +117 -0
- package/providers/_builtin/ide/windsurf/scripts/list_models.js +39 -0
- package/providers/_builtin/ide/windsurf/scripts/list_modes.js +39 -0
- package/providers/_builtin/ide/windsurf/scripts/new_session.js +69 -0
- package/providers/_builtin/ide/windsurf/scripts/open_panel.js +58 -0
- package/providers/_builtin/ide/windsurf/scripts/read_chat.js +297 -0
- package/providers/_builtin/ide/windsurf/scripts/resolve_action.js +68 -0
- package/providers/_builtin/ide/windsurf/scripts/send_message.js +87 -0
- package/providers/_builtin/ide/windsurf/scripts/set_mode.js +15 -0
- package/providers/_builtin/ide/windsurf/scripts/set_model.js +15 -0
- package/providers/_builtin/ide/windsurf/scripts/switch_session.js +58 -0
- package/providers/_builtin/ide/windsurf/scripts.js +57 -0
- package/providers/_builtin/registry.json +266 -0
- package/providers/_builtin/validate.js +156 -0
- package/src/agent-stream/index.ts +6 -0
- package/src/agent-stream/manager.ts +286 -0
- package/src/agent-stream/poller.ts +154 -0
- package/src/agent-stream/provider-adapter.ts +138 -0
- package/src/agent-stream/types.ts +61 -0
- package/src/boot/daemon-lifecycle.ts +252 -0
- package/src/cdp/devtools.ts +335 -0
- package/src/cdp/initializer.ts +191 -0
- package/src/cdp/manager.ts +897 -0
- package/src/cdp/scanner.ts +185 -0
- package/src/cdp/setup.ts +150 -0
- package/src/cli-adapter-types.ts +25 -0
- package/src/cli-adapters/provider-cli-adapter.ts +448 -0
- package/src/commands/cdp-commands.ts +208 -0
- package/src/commands/chat-commands.ts +675 -0
- package/src/commands/cli-manager.ts +353 -0
- package/src/commands/handler.ts +328 -0
- package/src/commands/router.ts +258 -0
- package/src/commands/stream-commands.ts +325 -0
- package/src/config/chat-history.ts +211 -0
- package/src/config/config.ts +219 -0
- package/src/daemon/dev-server.ts +2378 -0
- package/src/daemon/scaffold-template.ts +394 -0
- package/src/daemon-core.ts +50 -0
- package/src/detection/cli-detector.ts +89 -0
- package/src/detection/ide-detector.ts +157 -0
- package/src/index.ts +103 -0
- package/src/installer.ts +263 -0
- package/src/ipc-protocol.ts +133 -0
- package/src/launch.ts +433 -0
- package/src/logging/command-log.ts +180 -0
- package/src/logging/logger.ts +316 -0
- package/src/providers/acp-provider-instance.ts +1140 -0
- package/src/providers/cli-provider-instance.ts +207 -0
- package/src/providers/contracts.ts +524 -0
- package/src/providers/extension-provider-instance.ts +156 -0
- package/src/providers/ide-provider-instance.ts +377 -0
- package/src/providers/index.ts +18 -0
- package/src/providers/provider-instance-manager.ts +182 -0
- package/src/providers/provider-instance.ts +112 -0
- package/src/providers/provider-loader.ts +1031 -0
- package/src/providers/status-monitor.ts +125 -0
- package/src/providers/version-archive.ts +266 -0
- package/src/status/reporter.ts +294 -0
- package/src/types.ts +206 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity v1 — new_session
|
|
3
|
+
*
|
|
4
|
+
* Antigravity Agent 패널 상단의 + 아이콘 클릭으로 새 대화 시작.
|
|
5
|
+
*
|
|
6
|
+
* DOM 구조:
|
|
7
|
+
* .antigravity-agent-side-panel 상단 아이콘 바
|
|
8
|
+
* [0] <a> + (Plus) → 새 대화 ← 이것을 클릭
|
|
9
|
+
* [1] <a> 히스토리 (data-past-conversations-toggle)
|
|
10
|
+
* [2] <div> 더보기 (⋯)
|
|
11
|
+
* [3] <a> 닫기 (✕)
|
|
12
|
+
*
|
|
13
|
+
* 전략:
|
|
14
|
+
* 1. 히스토리 토글의 이전 형제 <a> 클릭 (Plus 아이콘)
|
|
15
|
+
* 2. SVG path "M12 4.5v15m7.5-7.5h-15" (+ 모양) 찾기
|
|
16
|
+
* 3. 패널 상단 첫 번째 <a> 클릭 폴백
|
|
17
|
+
*
|
|
18
|
+
* 최종 확인: 2026-03-11
|
|
19
|
+
*/
|
|
20
|
+
(() => {
|
|
21
|
+
try {
|
|
22
|
+
// ─── 1. 히스토리 토글의 이전 형제 요소 (Plus 아이콘) ───
|
|
23
|
+
const toggle = document.querySelector('[data-past-conversations-toggle="true"]');
|
|
24
|
+
if (toggle) {
|
|
25
|
+
const parent = toggle.parentElement;
|
|
26
|
+
if (parent) {
|
|
27
|
+
const children = Array.from(parent.children).filter(c => c.offsetWidth > 0);
|
|
28
|
+
const toggleIdx = children.indexOf(toggle);
|
|
29
|
+
// Plus 아이콘은 히스토리 바로 앞
|
|
30
|
+
if (toggleIdx > 0) {
|
|
31
|
+
const plusBtn = children[toggleIdx - 1];
|
|
32
|
+
plusBtn.click();
|
|
33
|
+
return 'clicked (sibling of toggle)';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── 2. SVG path 기반: Plus(+) 모양 SVG ───
|
|
39
|
+
const panel = document.querySelector('.antigravity-agent-side-panel') || document.querySelector('#conversation');
|
|
40
|
+
if (panel) {
|
|
41
|
+
const svgs = panel.querySelectorAll('svg');
|
|
42
|
+
for (const svg of svgs) {
|
|
43
|
+
const path = svg.querySelector('path');
|
|
44
|
+
if (path) {
|
|
45
|
+
const d = path.getAttribute('d') || '';
|
|
46
|
+
// Plus 아이콘 SVG: 세로선 + 가로선
|
|
47
|
+
if (d.includes('v15') && d.includes('h-15')) {
|
|
48
|
+
const clickable = svg.closest('a') || svg.closest('button') || svg.parentElement;
|
|
49
|
+
if (clickable) {
|
|
50
|
+
clickable.click();
|
|
51
|
+
return 'clicked (svg plus)';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── 3. 폴백: 패널 상단 영역의 첫 번째 <a> ───
|
|
59
|
+
if (panel) {
|
|
60
|
+
const topLinks = Array.from(panel.querySelectorAll('a')).filter(a => {
|
|
61
|
+
const r = a.getBoundingClientRect();
|
|
62
|
+
const pr = panel.getBoundingClientRect();
|
|
63
|
+
return r.y < pr.y + 30 && a.offsetWidth > 0 && a.offsetWidth < 30;
|
|
64
|
+
});
|
|
65
|
+
if (topLinks.length > 0) {
|
|
66
|
+
topLinks[0].click();
|
|
67
|
+
return 'clicked (first top link)';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return 'no new session button found';
|
|
72
|
+
} catch (e) {
|
|
73
|
+
return 'error: ' + e.message;
|
|
74
|
+
}
|
|
75
|
+
})()
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity v1 — read_chat (v4 — 스크롤 아래만, 가시 영역만 수집)
|
|
3
|
+
*
|
|
4
|
+
* 원칙:
|
|
5
|
+
* - 스크롤은 아래로만 (위로 절대 안 감)
|
|
6
|
+
* - 가상화로 사라진 과거 사용자 메시지는 무시
|
|
7
|
+
* - 현재 보이는 메시지만 수집 (최신 턴 중심)
|
|
8
|
+
*
|
|
9
|
+
* DOM 구조:
|
|
10
|
+
* 사용자: bg-gray-500/15 + select-text + p-2, 내부 whitespace-pre-wrap
|
|
11
|
+
* 어시스턴트: .leading-relaxed.select-text
|
|
12
|
+
*/
|
|
13
|
+
(() => {
|
|
14
|
+
try {
|
|
15
|
+
const conv = document.querySelector('#conversation') || document.querySelector('.antigravity-agent-side-panel') || document.body;
|
|
16
|
+
const scroll = conv.querySelector('.overflow-y-auto') || conv;
|
|
17
|
+
|
|
18
|
+
// 1. 상태 감지 — 사이드바 하단 Send/Stop 버튼 기반 (오탐 방지)
|
|
19
|
+
let status = 'idle';
|
|
20
|
+
|
|
21
|
+
// 시그널 A (1순위): 사이드바 하단 빨간 Stop 사각형 (.bg-red-500) — generating 중일 때 표시됨
|
|
22
|
+
const stopSquare = conv.querySelector('[class*="bg-red-500"]') || conv.querySelector('button[class*="rounded"] [class*="bg-red"]');
|
|
23
|
+
if (stopSquare && stopSquare.offsetWidth > 0) {
|
|
24
|
+
status = 'generating';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 시그널 B: conv 내부의 animate-markdown (생성 중 마크다운 렌더링)
|
|
28
|
+
if (status === 'idle') {
|
|
29
|
+
const animMarkdown = scroll.querySelector('.leading-relaxed [class*="animate-markdown"]');
|
|
30
|
+
if (animMarkdown && animMarkdown.offsetWidth > 0) status = 'generating';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const title = document.title.split(' \u2014 ')[0].trim() || 'Active Session';
|
|
34
|
+
|
|
35
|
+
// ─── HTML → Markdown 변환기 (대시보드가 ReactMarkdown+remarkGfm 사용) ───
|
|
36
|
+
function htmlToMd(node) {
|
|
37
|
+
if (node.nodeType === 3) return node.textContent || '';
|
|
38
|
+
if (node.nodeType !== 1) return '';
|
|
39
|
+
const tag = node.tagName;
|
|
40
|
+
|
|
41
|
+
// 스타일/스크립트 제거
|
|
42
|
+
if (tag === 'STYLE' || tag === 'SCRIPT' || tag === 'SVG') return '';
|
|
43
|
+
|
|
44
|
+
// 테이블 → GFM
|
|
45
|
+
if (tag === 'TABLE') {
|
|
46
|
+
const rows = Array.from(node.querySelectorAll('tr'));
|
|
47
|
+
if (rows.length === 0) return '';
|
|
48
|
+
const table = rows.map(tr =>
|
|
49
|
+
Array.from(tr.querySelectorAll('th, td')).map(cell => (cell.textContent || '').trim().replace(/\|/g, '\\|'))
|
|
50
|
+
);
|
|
51
|
+
if (table.length === 0) return '';
|
|
52
|
+
const colCount = Math.max(...table.map(r => r.length));
|
|
53
|
+
const header = table[0];
|
|
54
|
+
const sep = Array(colCount).fill('---');
|
|
55
|
+
const body = table.slice(1);
|
|
56
|
+
let md = '| ' + header.join(' | ') + ' |\n';
|
|
57
|
+
md += '| ' + sep.join(' | ') + ' |\n';
|
|
58
|
+
for (const row of body) {
|
|
59
|
+
while (row.length < colCount) row.push('');
|
|
60
|
+
md += '| ' + row.join(' | ') + ' |\n';
|
|
61
|
+
}
|
|
62
|
+
return '\n' + md + '\n';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (tag === 'UL') return '\n' + Array.from(node.children).map(li => '- ' + childrenToMd(li).trim()).join('\n') + '\n';
|
|
66
|
+
if (tag === 'OL') return '\n' + Array.from(node.children).map((li, i) => (i + 1) + '. ' + childrenToMd(li).trim()).join('\n') + '\n';
|
|
67
|
+
if (tag === 'LI') return childrenToMd(node);
|
|
68
|
+
|
|
69
|
+
if (tag === 'H1') return '\n# ' + childrenToMd(node).trim() + '\n';
|
|
70
|
+
if (tag === 'H2') return '\n## ' + childrenToMd(node).trim() + '\n';
|
|
71
|
+
if (tag === 'H3') return '\n### ' + childrenToMd(node).trim() + '\n';
|
|
72
|
+
if (tag === 'H4') return '\n#### ' + childrenToMd(node).trim() + '\n';
|
|
73
|
+
|
|
74
|
+
if (tag === 'STRONG' || tag === 'B') return '**' + childrenToMd(node).trim() + '**';
|
|
75
|
+
if (tag === 'EM' || tag === 'I') return '*' + childrenToMd(node).trim() + '*';
|
|
76
|
+
|
|
77
|
+
if (tag === 'PRE') {
|
|
78
|
+
const codeEl = node.querySelector('code');
|
|
79
|
+
const lang = codeEl ? (codeEl.className.match(/language-(\w+)/)?.[1] || '') : '';
|
|
80
|
+
const code = (codeEl || node).textContent || '';
|
|
81
|
+
return '\n```' + lang + '\n' + code.trim() + '\n```\n';
|
|
82
|
+
}
|
|
83
|
+
if (tag === 'CODE') {
|
|
84
|
+
if (node.parentElement && node.parentElement.tagName === 'PRE') return node.textContent || '';
|
|
85
|
+
return '`' + (node.textContent || '').trim() + '`';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (tag === 'BLOCKQUOTE') return '\n> ' + childrenToMd(node).trim().replace(/\n/g, '\n> ') + '\n';
|
|
89
|
+
if (tag === 'A') return '[' + childrenToMd(node).trim() + '](' + (node.getAttribute('href') || '') + ')';
|
|
90
|
+
if (tag === 'BR') return '\n';
|
|
91
|
+
if (tag === 'P') return '\n' + childrenToMd(node).trim() + '\n';
|
|
92
|
+
|
|
93
|
+
return childrenToMd(node);
|
|
94
|
+
}
|
|
95
|
+
function childrenToMd(node) {
|
|
96
|
+
return Array.from(node.childNodes).map(htmlToMd).join('');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getCleanMd(el) {
|
|
100
|
+
const clone = el.cloneNode(true);
|
|
101
|
+
// 노이즈 제거
|
|
102
|
+
clone.querySelectorAll('button, [role="button"], style, script, svg, .codicon, [class*="feedback"], [aria-label*="Good"], [aria-label*="Bad"]').forEach(n => n.remove());
|
|
103
|
+
// 상태 텍스트 제거 (leaf만, 60자 이하만)
|
|
104
|
+
clone.querySelectorAll('*').forEach(child => {
|
|
105
|
+
if (!child.parentNode) return;
|
|
106
|
+
const t = (child.textContent || '').trim();
|
|
107
|
+
if (t.length > 60) return;
|
|
108
|
+
const low = t.toLowerCase();
|
|
109
|
+
if (/^(analyzed\s+\d|edited\s+\d|ran\s+\S|terminal\s|reading|searching)/i.test(low)) child.remove();
|
|
110
|
+
if (/^(mcp|customizationmcp|serversexport)/i.test(low)) child.remove();
|
|
111
|
+
});
|
|
112
|
+
let md = htmlToMd(clone);
|
|
113
|
+
// "Thought for X seconds" 제거
|
|
114
|
+
md = md.replace(/^Thought for\s+[\d.]+\s*(seconds?|s)\s*/i, '');
|
|
115
|
+
md = md.replace(/\n{3,}/g, '\n\n').trim();
|
|
116
|
+
return md;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 2. 메시지 수집 (스크롤 조작 없음 — 현재 DOM에 있는 것만)
|
|
120
|
+
const collected = [];
|
|
121
|
+
const seenHashes = new Set();
|
|
122
|
+
|
|
123
|
+
// 사용자 메시지 (bg-gray-500/15 + select-text + p-2)
|
|
124
|
+
const allDivs = scroll.querySelectorAll('div');
|
|
125
|
+
for (const el of allDivs) {
|
|
126
|
+
const cls = (el.className || '');
|
|
127
|
+
if (typeof cls !== 'string') continue;
|
|
128
|
+
if (cls.includes('bg-gray-500/15') && cls.includes('select-text') && cls.includes('p-2')) {
|
|
129
|
+
const textEl = el.querySelector('[class*="whitespace-pre-wrap"]') || el;
|
|
130
|
+
const text = (textEl.innerText || '').trim();
|
|
131
|
+
if (!text || text.length < 1) continue;
|
|
132
|
+
const hash = 'user:' + text.slice(0, 200);
|
|
133
|
+
if (seenHashes.has(hash)) continue;
|
|
134
|
+
seenHashes.add(hash);
|
|
135
|
+
collected.push({ role: 'user', text, el });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 어시스턴트 메시지 (leading-relaxed.select-text) — HTML→Markdown 변환
|
|
140
|
+
const assistantBlocks = scroll.querySelectorAll('.leading-relaxed.select-text');
|
|
141
|
+
for (const ab of assistantBlocks) {
|
|
142
|
+
if (ab.offsetHeight < 10) continue;
|
|
143
|
+
if (ab.closest('[class*="max-h-"][class*="overflow-y-auto"]')) continue;
|
|
144
|
+
|
|
145
|
+
let text = getCleanMd(ab);
|
|
146
|
+
if (!text || text.length < 2) continue;
|
|
147
|
+
if (/^(Running command|Checked command|collectStatus)/i.test(text)) continue;
|
|
148
|
+
|
|
149
|
+
const hash = 'assistant:' + text.slice(0, 200);
|
|
150
|
+
if (seenHashes.has(hash)) continue;
|
|
151
|
+
seenHashes.add(hash);
|
|
152
|
+
collected.push({ role: 'assistant', text, el: ab });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 3. DOM 순서 정렬
|
|
156
|
+
collected.sort((a, b) => {
|
|
157
|
+
const pos = a.el.compareDocumentPosition(b.el);
|
|
158
|
+
if (pos & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
|
|
159
|
+
if (pos & Node.DOCUMENT_POSITION_PRECEDING) return 1;
|
|
160
|
+
return 0;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// 최신 30개만 유지 (대화 첫 로드 시 수백 개 수집 방지)
|
|
164
|
+
const trimmed = collected.length > 30 ? collected.slice(-30) : collected;
|
|
165
|
+
|
|
166
|
+
const final = trimmed.map((m, i) => ({
|
|
167
|
+
id: 'msg_' + i,
|
|
168
|
+
role: m.role,
|
|
169
|
+
content: m.text.length > 6000 ? m.text.slice(0, 6000) + '\n[... truncated]' : m.text,
|
|
170
|
+
index: i,
|
|
171
|
+
kind: 'standard',
|
|
172
|
+
vsc_history: true
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
// 4. 입력창
|
|
176
|
+
const editor = conv.querySelector('[contenteditable="true"][role="textbox"]') ||
|
|
177
|
+
conv.querySelector('[data-lexical-editor="true"]') ||
|
|
178
|
+
conv.querySelector('textarea');
|
|
179
|
+
const inputContent = editor ? (editor.innerText || editor.value || '').trim() : '';
|
|
180
|
+
|
|
181
|
+
// 5. 모달/승인 감지 — Run⌥⏎/Reject 인라인 + Deny/Allow 브라우저 승인
|
|
182
|
+
let activeModal = null;
|
|
183
|
+
try {
|
|
184
|
+
const isApprovalLike = (el) => {
|
|
185
|
+
const t = (el.textContent || '').trim().toLowerCase();
|
|
186
|
+
// 드롭다운 옵션 제외
|
|
187
|
+
if (t === 'ask every time') return false;
|
|
188
|
+
return /^(run|reject|skip|approve|allow|deny|cancel|accept|yes|no)\b/i.test(t)
|
|
189
|
+
|| t === 'always allow' || t === 'always deny'
|
|
190
|
+
|| t.includes('run ') || t.includes('approve') || t.includes('reject')
|
|
191
|
+
|| t.includes('skip');
|
|
192
|
+
};
|
|
193
|
+
// A: 전통적 모달 다이얼로그
|
|
194
|
+
const dialog = document.querySelector('.monaco-dialog-box, [role="dialog"], .monaco-modal-block');
|
|
195
|
+
if (dialog && dialog.offsetWidth > 80 && dialog.offsetHeight > 40) {
|
|
196
|
+
const msg = (dialog.querySelector('.dialog-message, .dialog-message-text') || dialog).innerText?.trim() || '';
|
|
197
|
+
const buttons = Array.from(dialog.querySelectorAll('.monaco-button, button'))
|
|
198
|
+
.map(b => (b.innerText || '').trim())
|
|
199
|
+
.filter(t => t.length > 0 && t.length < 30);
|
|
200
|
+
if (msg || buttons.length > 0) {
|
|
201
|
+
activeModal = { message: msg.slice(0, 300), buttons, width: dialog.offsetWidth, height: dialog.offsetHeight };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// B: 인라인 approval 버튼 (Run⌥⏎, Reject, Deny, Allow 등)
|
|
205
|
+
// ⚠ 사이드바(conv) 내부의 버튼만 검사 — 에디터의 Accept/Reject Changes 제외
|
|
206
|
+
if (!activeModal) {
|
|
207
|
+
const panelBtns = Array.from(conv.querySelectorAll('button')).filter(b => b.offsetWidth > 0 && b.offsetHeight > 0);
|
|
208
|
+
const approvalBtns = panelBtns.filter(isApprovalLike);
|
|
209
|
+
if (approvalBtns.length > 0) {
|
|
210
|
+
const hasActionBtn = approvalBtns.some(b => {
|
|
211
|
+
const t = (b.textContent || '').trim().toLowerCase();
|
|
212
|
+
return t.indexOf('run') === 0 || t === 'reject' || t.indexOf('reject') === 0
|
|
213
|
+
|| t === 'skip' || t.indexOf('skip') === 0
|
|
214
|
+
|| t === 'deny' || t === 'allow' || t === 'always allow' || t === 'always deny'
|
|
215
|
+
|| t === 'accept' || t === 'approve';
|
|
216
|
+
});
|
|
217
|
+
if (hasActionBtn) {
|
|
218
|
+
const btnTexts = [...new Set(
|
|
219
|
+
approvalBtns.map(b => (b.textContent || '').trim())
|
|
220
|
+
.filter(t => t.length > 0 && t.length < 40)
|
|
221
|
+
)];
|
|
222
|
+
const firstApproval = approvalBtns[0];
|
|
223
|
+
let wrapper = firstApproval.parentElement;
|
|
224
|
+
for (let up = 0; up < 5 && wrapper; up++, wrapper = wrapper.parentElement) {
|
|
225
|
+
if (wrapper.offsetHeight > 40) break;
|
|
226
|
+
}
|
|
227
|
+
const msg = wrapper ? (wrapper.textContent || '').trim().slice(0, 300) : '';
|
|
228
|
+
activeModal = { message: msg, buttons: btnTexts, width: 400, height: 100 };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// C: footer 기반 사용량/quota 다이얼로그 (Dismiss / See Plans / Enable Overages 등)
|
|
233
|
+
// <footer> 요소가 conv 안에 존재하고 2개 이상 버튼이 있으면 사용자 액션이 필요한 카드로 판단
|
|
234
|
+
if (!activeModal) {
|
|
235
|
+
const footers = Array.from(conv.querySelectorAll('footer')).filter(f => f.offsetWidth > 0 && f.offsetHeight > 0);
|
|
236
|
+
for (const footer of footers) {
|
|
237
|
+
const footerBtns = Array.from(footer.querySelectorAll('button, a')).filter(b => b.offsetWidth > 0);
|
|
238
|
+
if (footerBtns.length >= 2) {
|
|
239
|
+
// 카드 컨테이너: footer 상위에서 충분한 높이를 가진 첫 번째 요소
|
|
240
|
+
let card = footer.parentElement;
|
|
241
|
+
for (let up = 0; up < 4 && card; up++) {
|
|
242
|
+
if (card.offsetHeight > 60) break;
|
|
243
|
+
card = card.parentElement;
|
|
244
|
+
}
|
|
245
|
+
const msg = card ? (card.innerText || '').trim().slice(0, 300) : '';
|
|
246
|
+
const btnTexts = footerBtns.map(b => (b.innerText || '').trim()).filter(t => t.length > 0 && t.length < 40);
|
|
247
|
+
if (btnTexts.length >= 2) {
|
|
248
|
+
activeModal = { message: msg, buttons: btnTexts, width: card ? card.offsetWidth : 300, height: card ? card.offsetHeight : 100 };
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// 모달이 감지되면 status를 waiting_approval로 변경
|
|
255
|
+
if (activeModal) status = 'waiting_approval';
|
|
256
|
+
} catch (e) { activeModal = null; }
|
|
257
|
+
|
|
258
|
+
return { id: 'active_session', status, title, messages: final, inputContent, activeModal };
|
|
259
|
+
} catch (e) {
|
|
260
|
+
return { id: 'error', status: 'error', error: e.message, messages: [] };
|
|
261
|
+
}
|
|
262
|
+
})()
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity v1 — resolve_action
|
|
3
|
+
*
|
|
4
|
+
* 버튼 찾기 + 좌표 반환 (CDP Input.dispatchMouseEvent로 클릭)
|
|
5
|
+
* 파라미터: ${BUTTON_TEXT}
|
|
6
|
+
*
|
|
7
|
+
* 핵심: viewport 안에 보이는 버튼 중 마지막(최신) 매칭 우선
|
|
8
|
+
*/
|
|
9
|
+
(() => {
|
|
10
|
+
const want = ${ BUTTON_TEXT };
|
|
11
|
+
const wantNorm = (want || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
12
|
+
|
|
13
|
+
function norm(t) { return (t || '').replace(/\s+/g, ' ').trim().toLowerCase(); }
|
|
14
|
+
|
|
15
|
+
function matches(el) {
|
|
16
|
+
const raw = (el.textContent || '').trim();
|
|
17
|
+
const t = norm(raw);
|
|
18
|
+
if (!t || t.length > 80) return false;
|
|
19
|
+
if (t === wantNorm) return true;
|
|
20
|
+
if (t.indexOf(wantNorm) === 0) return true;
|
|
21
|
+
if (wantNorm.indexOf(t) >= 0 && t.length > 2) return true;
|
|
22
|
+
if (t.indexOf(wantNorm) >= 0) return true;
|
|
23
|
+
if (/^(run|approve|allow|accept|always|yes)\b/.test(wantNorm)) {
|
|
24
|
+
if (/^run\b/.test(t)) return true;
|
|
25
|
+
if (/^allow\b/.test(t)) return true;
|
|
26
|
+
if (/^accept\b/.test(t) && !t.includes('changes')) return true;
|
|
27
|
+
if (/^always\b/.test(t)) return true;
|
|
28
|
+
}
|
|
29
|
+
if (/^(reject|deny|no|abort)\b/.test(wantNorm)) {
|
|
30
|
+
if (/^reject\b/.test(t) && !t.includes('changes')) return true;
|
|
31
|
+
if (/^deny\b/.test(t)) return true;
|
|
32
|
+
}
|
|
33
|
+
if (/^(skip|cancel)\b/.test(wantNorm)) {
|
|
34
|
+
if (/^skip\b/.test(t) || /^cancel\b/.test(t)) return true;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const sel = 'button, [role="button"]';
|
|
40
|
+
const allBtns = [...document.querySelectorAll(sel)].filter(b => {
|
|
41
|
+
if (!b.offsetWidth || !b.getBoundingClientRect().height) return false;
|
|
42
|
+
const rect = b.getBoundingClientRect();
|
|
43
|
+
// viewport 안에 보이는 것만 (y > 0, y < window.innerHeight)
|
|
44
|
+
return rect.y > 0 && rect.y < window.innerHeight;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// 마지막(최신) 매칭 우선 — 역순 검색
|
|
48
|
+
let found = null;
|
|
49
|
+
for (let i = allBtns.length - 1; i >= 0; i--) {
|
|
50
|
+
if (matches(allBtns[i])) {
|
|
51
|
+
found = allBtns[i];
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (found) {
|
|
57
|
+
const rect = found.getBoundingClientRect();
|
|
58
|
+
return JSON.stringify({
|
|
59
|
+
found: true,
|
|
60
|
+
text: found.textContent?.trim()?.substring(0, 40),
|
|
61
|
+
x: Math.round(rect.x + rect.width / 2),
|
|
62
|
+
y: Math.round(rect.y + rect.height / 2),
|
|
63
|
+
w: Math.round(rect.width),
|
|
64
|
+
h: Math.round(rect.height)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return JSON.stringify({ found: false, want: wantNorm });
|
|
68
|
+
})()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity CDP Scripts
|
|
3
|
+
*
|
|
4
|
+
* Version routing is handled by ProviderLoader:
|
|
5
|
+
* - provider.json "versions" field declares script directory overrides
|
|
6
|
+
* - VersionArchive detects installed Antigravity version at daemon startup
|
|
7
|
+
* - resolve() picks scripts/legacy/ for < 1.107.0, scripts/ for >= 1.107.0
|
|
8
|
+
*
|
|
9
|
+
* To add support for a new breaking version:
|
|
10
|
+
* 1. Create scripts/v<next>/ with updated scripts
|
|
11
|
+
* 2. Add entry to provider.json "versions" field:
|
|
12
|
+
* "< X.Y.Z": { "__dir": "scripts/v<next>" }
|
|
13
|
+
* 3. Update "testedVersions" in provider.json
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const DIR = __dirname; // scripts/1.107/
|
|
21
|
+
|
|
22
|
+
function load(name) {
|
|
23
|
+
try { return fs.readFileSync(path.join(DIR, name), 'utf-8'); }
|
|
24
|
+
catch { return null; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports.readChat = () => load('read_chat.js');
|
|
28
|
+
module.exports.focusEditor = () => load('focus_editor.js');
|
|
29
|
+
module.exports.listSessions = () => load('list_chats.js');
|
|
30
|
+
module.exports.newSession = () => load('new_session.js');
|
|
31
|
+
module.exports.listModels = () => load('list_models.js');
|
|
32
|
+
module.exports.listModes = () => load('list_modes.js');
|
|
33
|
+
|
|
34
|
+
module.exports.sendMessage = (text) => {
|
|
35
|
+
const script = load('send_message.js');
|
|
36
|
+
if (!script) return null;
|
|
37
|
+
return script.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text));
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
module.exports.switchSession = (sessionId) => {
|
|
41
|
+
const script = load('switch_session.js');
|
|
42
|
+
if (!script) return null;
|
|
43
|
+
return script.replace(/\$\{\s*SESSION_ID\s*\}/g, JSON.stringify(sessionId));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
module.exports.resolveAction = (params) => {
|
|
47
|
+
const action = typeof params === 'string' ? params : params?.action || 'approve';
|
|
48
|
+
const buttonText = params?.button || params?.buttonText
|
|
49
|
+
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : action);
|
|
50
|
+
const script = load('resolve_action.js');
|
|
51
|
+
if (!script) return null;
|
|
52
|
+
return script.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
module.exports.setModel = (params) => {
|
|
56
|
+
const model = typeof params === 'string' ? params : params?.model;
|
|
57
|
+
const script = load('set_model.js');
|
|
58
|
+
if (!script) return null;
|
|
59
|
+
return script.replace(/\$\{\s*MODEL\s*\}/g, JSON.stringify(model));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
module.exports.setMode = (params) => {
|
|
63
|
+
const mode = typeof params === 'string' ? params : params?.mode;
|
|
64
|
+
const script = load('set_mode.js');
|
|
65
|
+
if (!script) return null;
|
|
66
|
+
return script.replace(/\$\{\s*MODE\s*\}/g, JSON.stringify(mode));
|
|
67
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity v1 — send_message
|
|
3
|
+
*
|
|
4
|
+
* Antigravity는 contenteditable div[role="textbox"]를 사용.
|
|
5
|
+
* 여러 개의 contenteditable이 있을 수 있으므로 y좌표가 가장 큰 (메인 채팅) 것을 선택.
|
|
6
|
+
*
|
|
7
|
+
* ⚠️ Enter 이벤트에 composed: true + which 필수 (Shadow DOM 경계 통과 + React 호환)
|
|
8
|
+
* ⚠️ keydown + keypress + keyup 전체 시퀀스 필요
|
|
9
|
+
*
|
|
10
|
+
* 파라미터: ${ MESSAGE }
|
|
11
|
+
* 최종 확인: 2026-03-10
|
|
12
|
+
*/
|
|
13
|
+
(async () => {
|
|
14
|
+
try {
|
|
15
|
+
const msg = ${ MESSAGE };
|
|
16
|
+
|
|
17
|
+
// ─── 1. 메인 채팅 입력 필드 찾기 ───
|
|
18
|
+
const editors = document.querySelectorAll('[contenteditable="true"][role="textbox"]');
|
|
19
|
+
if (!editors.length) return 'error: no contenteditable textbox found';
|
|
20
|
+
|
|
21
|
+
// y좌표가 가장 큰 (화면 아래쪽 = 메인 채팅) 에디터 선택
|
|
22
|
+
const editor = [...editors].reduce((a, b) =>
|
|
23
|
+
b.getBoundingClientRect().y > a.getBoundingClientRect().y ? b : a
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// ─── 2. 텍스트 삽입 ───
|
|
27
|
+
editor.focus();
|
|
28
|
+
|
|
29
|
+
// 기존 내용 삭제
|
|
30
|
+
document.execCommand('selectAll', false, null);
|
|
31
|
+
document.execCommand('delete', false, null);
|
|
32
|
+
|
|
33
|
+
// 텍스트 삽입
|
|
34
|
+
document.execCommand('insertText', false, msg);
|
|
35
|
+
|
|
36
|
+
// React에 변경 알림
|
|
37
|
+
editor.dispatchEvent(new Event('input', { bubbles: true }));
|
|
38
|
+
|
|
39
|
+
await new Promise(r => setTimeout(r, 300));
|
|
40
|
+
|
|
41
|
+
// ─── 3. Enter 키 전송 (full sequence) ───
|
|
42
|
+
const enterOpts = {
|
|
43
|
+
key: 'Enter', code: 'Enter',
|
|
44
|
+
keyCode: 13, which: 13,
|
|
45
|
+
bubbles: true, cancelable: true, composed: true,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
editor.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
|
|
49
|
+
editor.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
|
|
50
|
+
editor.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
|
|
51
|
+
|
|
52
|
+
return 'sent';
|
|
53
|
+
} catch (e) {
|
|
54
|
+
return 'error: ' + e.message;
|
|
55
|
+
}
|
|
56
|
+
})()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity — set_mode [>= 1.107.0]
|
|
3
|
+
* mode trigger: BUTTON with py-1 pl-1 pr-2 opacity-70
|
|
4
|
+
* ${MODE} → JSON.stringify(modeName)
|
|
5
|
+
* → { success: boolean, mode?: string }
|
|
6
|
+
*/
|
|
7
|
+
(async () => {
|
|
8
|
+
try {
|
|
9
|
+
const target = ${MODE};
|
|
10
|
+
|
|
11
|
+
// ── Helper: find mode button ──────────────────────────────────────────
|
|
12
|
+
function findModeBtn() {
|
|
13
|
+
return [...document.querySelectorAll('button')].find(b => {
|
|
14
|
+
const cls = b.className || '';
|
|
15
|
+
return cls.includes('py-1') &&
|
|
16
|
+
cls.includes('pl-1') &&
|
|
17
|
+
cls.includes('pr-2') &&
|
|
18
|
+
cls.includes('opacity-70') &&
|
|
19
|
+
b.offsetWidth > 0;
|
|
20
|
+
}) || null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ── Helper: click item in open "Conversation mode" panel ─────────────
|
|
24
|
+
function clickModeItem(targetName) {
|
|
25
|
+
const headers = document.querySelectorAll('.text-xs.px-2.pb-1.opacity-80');
|
|
26
|
+
for (const header of headers) {
|
|
27
|
+
if (header.textContent?.trim() === 'Conversation mode') {
|
|
28
|
+
const parent = header.parentElement;
|
|
29
|
+
if (!parent) continue;
|
|
30
|
+
for (const item of parent.querySelectorAll('.font-medium')) {
|
|
31
|
+
const text = item.textContent?.trim();
|
|
32
|
+
if (text && text.toLowerCase() === targetName.toLowerCase()) {
|
|
33
|
+
item.click();
|
|
34
|
+
return text;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── Step 1: Panel already open — direct click ─────────────────────────
|
|
44
|
+
const direct = clickModeItem(target);
|
|
45
|
+
if (direct) {
|
|
46
|
+
await new Promise(r => setTimeout(r, 300));
|
|
47
|
+
return JSON.stringify({ success: true, mode: direct });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Step 2: Open panel via mode button, then click ───────────────────
|
|
51
|
+
const modeBtn = findModeBtn();
|
|
52
|
+
if (modeBtn) {
|
|
53
|
+
modeBtn.click();
|
|
54
|
+
await new Promise(r => setTimeout(r, 400));
|
|
55
|
+
|
|
56
|
+
const hit = clickModeItem(target);
|
|
57
|
+
if (hit) {
|
|
58
|
+
return JSON.stringify({ success: true, mode: hit });
|
|
59
|
+
}
|
|
60
|
+
modeBtn.click(); // Close if not found
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return JSON.stringify({ success: false, error: 'mode not found: ' + target });
|
|
64
|
+
} catch (e) {
|
|
65
|
+
return JSON.stringify({ success: false, error: e.message });
|
|
66
|
+
}
|
|
67
|
+
})()
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity — set_model
|
|
3
|
+
* 모델 드롭다운에서 모델 선택
|
|
4
|
+
*
|
|
5
|
+
* Version compatibility:
|
|
6
|
+
* v0 (old): .flex.min-w-0.max-w-full.cursor-pointer.items-center trigger
|
|
7
|
+
* v1 (new): Tailwind arbitrary values → partial matching required
|
|
8
|
+
* Both: dropdown items .px-2.py-1.flex.items-center.justify-between.cursor-pointer
|
|
9
|
+
*
|
|
10
|
+
* ${MODEL} → JSON.stringify(modelName)
|
|
11
|
+
* → { success: boolean, model?: string }
|
|
12
|
+
*/
|
|
13
|
+
(async () => {
|
|
14
|
+
try {
|
|
15
|
+
const target = ${MODEL};
|
|
16
|
+
|
|
17
|
+
// ── Helper: find model trigger button (v0 + v1 compat) ───────────────
|
|
18
|
+
function findModelTrigger() {
|
|
19
|
+
// v0: exact selector
|
|
20
|
+
const v0 = document.querySelector('.flex.min-w-0.max-w-full.cursor-pointer.items-center');
|
|
21
|
+
if (v0 && v0.offsetWidth > 0) return v0;
|
|
22
|
+
// v1: partial matching
|
|
23
|
+
return [...document.querySelectorAll('div, button')].find(e => {
|
|
24
|
+
const cls = e.className || '';
|
|
25
|
+
return cls.includes('min-w-0') &&
|
|
26
|
+
cls.includes('max-w-full') &&
|
|
27
|
+
cls.includes('cursor-pointer') &&
|
|
28
|
+
cls.includes('items-center') &&
|
|
29
|
+
e.offsetWidth > 0;
|
|
30
|
+
}) || null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Helper: find and click a model from open dropdown ────────────────
|
|
34
|
+
function clickModelItem(targetName) {
|
|
35
|
+
const items = document.querySelectorAll('.px-2.py-1.flex.items-center.justify-between.cursor-pointer');
|
|
36
|
+
for (const item of items) {
|
|
37
|
+
const label = item.querySelector('.text-xs.font-medium');
|
|
38
|
+
const text = (label || item).textContent?.trim();
|
|
39
|
+
if (text && (text === targetName || text.toLowerCase().includes(targetName.toLowerCase()))) {
|
|
40
|
+
item.click();
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Step 1: Dropdown already open — try direct click ─────────────────
|
|
48
|
+
const directHit = clickModelItem(target);
|
|
49
|
+
if (directHit) {
|
|
50
|
+
await new Promise(r => setTimeout(r, 200));
|
|
51
|
+
return JSON.stringify({ success: true, model: directHit });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ── Step 2: Dropdown closed — open trigger, then select ──────────────
|
|
55
|
+
const trigger = findModelTrigger();
|
|
56
|
+
if (trigger) {
|
|
57
|
+
trigger.click();
|
|
58
|
+
await new Promise(r => setTimeout(r, 400));
|
|
59
|
+
|
|
60
|
+
const hit = clickModelItem(target);
|
|
61
|
+
if (hit) {
|
|
62
|
+
return JSON.stringify({ success: true, model: hit });
|
|
63
|
+
}
|
|
64
|
+
// Close dropdown if target not found
|
|
65
|
+
trigger.click();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return JSON.stringify({ success: false, error: 'model not found: ' + target });
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return JSON.stringify({ success: false, error: e.message });
|
|
71
|
+
}
|
|
72
|
+
})()
|