antigravity-chat-proxy 0.1.0

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 (76) hide show
  1. package/README.md +362 -0
  2. package/app/api/v1/artifacts/[convId]/[filename]/route.ts +75 -0
  3. package/app/api/v1/artifacts/[convId]/route.ts +47 -0
  4. package/app/api/v1/artifacts/active/[filename]/route.ts +50 -0
  5. package/app/api/v1/artifacts/active/route.ts +89 -0
  6. package/app/api/v1/artifacts/route.ts +43 -0
  7. package/app/api/v1/chat/action/route.ts +30 -0
  8. package/app/api/v1/chat/approve/route.ts +21 -0
  9. package/app/api/v1/chat/history/route.ts +23 -0
  10. package/app/api/v1/chat/mode/route.ts +59 -0
  11. package/app/api/v1/chat/new/route.ts +21 -0
  12. package/app/api/v1/chat/reject/route.ts +21 -0
  13. package/app/api/v1/chat/route.ts +105 -0
  14. package/app/api/v1/chat/state/route.ts +23 -0
  15. package/app/api/v1/chat/stream/route.ts +258 -0
  16. package/app/api/v1/conversations/active/route.ts +117 -0
  17. package/app/api/v1/conversations/route.ts +189 -0
  18. package/app/api/v1/conversations/select/route.ts +114 -0
  19. package/app/api/v1/debug/dom/route.ts +30 -0
  20. package/app/api/v1/debug/scrape/route.ts +56 -0
  21. package/app/api/v1/health/route.ts +13 -0
  22. package/app/api/v1/windows/cdp-start/route.ts +32 -0
  23. package/app/api/v1/windows/cdp-status/route.ts +32 -0
  24. package/app/api/v1/windows/close/route.ts +67 -0
  25. package/app/api/v1/windows/open/route.ts +49 -0
  26. package/app/api/v1/windows/recent/route.ts +25 -0
  27. package/app/api/v1/windows/route.ts +27 -0
  28. package/app/api/v1/windows/select/route.ts +35 -0
  29. package/app/debug/page.tsx +228 -0
  30. package/app/favicon.ico +0 -0
  31. package/app/globals.css +1234 -0
  32. package/app/layout.tsx +42 -0
  33. package/app/page.tsx +10 -0
  34. package/bin/cli.js +601 -0
  35. package/components/agent-message.tsx +63 -0
  36. package/components/artifact-panel.tsx +133 -0
  37. package/components/chat-container.tsx +82 -0
  38. package/components/chat-input.tsx +92 -0
  39. package/components/conversation-selector.tsx +97 -0
  40. package/components/header.tsx +302 -0
  41. package/components/hitl-dialog.tsx +23 -0
  42. package/components/message-list.tsx +41 -0
  43. package/components/thinking-block.tsx +14 -0
  44. package/components/tool-call-card.tsx +75 -0
  45. package/components/typing-indicator.tsx +11 -0
  46. package/components/user-message.tsx +13 -0
  47. package/components/welcome-screen.tsx +38 -0
  48. package/hooks/use-artifacts.ts +85 -0
  49. package/hooks/use-chat.ts +278 -0
  50. package/hooks/use-conversations.ts +190 -0
  51. package/lib/actions/hitl.ts +113 -0
  52. package/lib/actions/new-chat.ts +116 -0
  53. package/lib/actions/send-message.ts +31 -0
  54. package/lib/actions/switch-conversation.ts +92 -0
  55. package/lib/cdp/connection.ts +95 -0
  56. package/lib/cdp/process-manager.ts +327 -0
  57. package/lib/cdp/recent-projects.ts +137 -0
  58. package/lib/cdp/selectors.ts +11 -0
  59. package/lib/context.ts +38 -0
  60. package/lib/init.ts +48 -0
  61. package/lib/logger.ts +32 -0
  62. package/lib/scraper/agent-mode.ts +122 -0
  63. package/lib/scraper/agent-state.ts +756 -0
  64. package/lib/scraper/chat-history.ts +138 -0
  65. package/lib/scraper/ide-conversations.ts +124 -0
  66. package/lib/sse/diff-states.ts +141 -0
  67. package/lib/types.ts +146 -0
  68. package/lib/utils.ts +7 -0
  69. package/next.config.ts +7 -0
  70. package/package.json +50 -0
  71. package/public/file.svg +1 -0
  72. package/public/globe.svg +1 -0
  73. package/public/next.svg +1 -0
  74. package/public/vercel.svg +1 -0
  75. package/public/window.svg +1 -0
  76. package/tsconfig.json +34 -0
@@ -0,0 +1,228 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback } from 'react';
4
+
5
+ interface WindowInfo {
6
+ index: number;
7
+ title: string;
8
+ url: string;
9
+ active: boolean;
10
+ }
11
+
12
+ interface ScrapeResult {
13
+ raw: string;
14
+ parsed: {
15
+ isRunning: boolean;
16
+ turnCount: number;
17
+ stepGroupCount: number;
18
+ thinking: { time: string }[];
19
+ toolCalls: any[];
20
+ responses: string[];
21
+ notifications: string[];
22
+ error: string | null;
23
+ fileChanges: { fileName: string; type: string }[];
24
+ lastTurnResponseHTML: string;
25
+ };
26
+ meta: {
27
+ timestamp: string;
28
+ activeWindowIdx: number;
29
+ turnCount: number;
30
+ toolCallCount: number;
31
+ responseCount: number;
32
+ };
33
+ }
34
+
35
+ export default function DebugPage() {
36
+ const [windows, setWindows] = useState<WindowInfo[]>([]);
37
+ const [selectedIdx, setSelectedIdx] = useState<number | null>(null);
38
+ const [scrapeResult, setScrapeResult] = useState<ScrapeResult | null>(null);
39
+ const [loading, setLoading] = useState(false);
40
+ const [error, setError] = useState('');
41
+ const [activeTab, setActiveTab] = useState<'raw' | 'parsed' | 'response'>('parsed');
42
+
43
+ const loadWindows = useCallback(async () => {
44
+ try {
45
+ const res = await fetch('/api/v1/windows');
46
+ const data = await res.json();
47
+ setWindows(data.windows || []);
48
+ const active = data.windows?.find((w: WindowInfo) => w.active);
49
+ if (active) setSelectedIdx(active.index);
50
+ } catch (e: any) {
51
+ setError('Failed to load windows: ' + e.message);
52
+ }
53
+ }, []);
54
+
55
+ useEffect(() => { loadWindows(); }, [loadWindows]);
56
+
57
+ const selectWindow = async (idx: number) => {
58
+ setSelectedIdx(idx);
59
+ setError('');
60
+ try {
61
+ await fetch('/api/v1/windows/select', {
62
+ method: 'POST',
63
+ headers: { 'Content-Type': 'application/json' },
64
+ body: JSON.stringify({ index: idx }),
65
+ });
66
+ await loadWindows();
67
+ } catch (e: any) {
68
+ setError('Failed to select window: ' + e.message);
69
+ }
70
+ };
71
+
72
+ const scrape = async () => {
73
+ setLoading(true);
74
+ setError('');
75
+ setScrapeResult(null);
76
+ try {
77
+ const res = await fetch('/api/v1/debug/scrape');
78
+ if (!res.ok) {
79
+ const data = await res.json();
80
+ throw new Error(data.error || `HTTP ${res.status}`);
81
+ }
82
+ const data = await res.json();
83
+ setScrapeResult(data);
84
+ } catch (e: any) {
85
+ setError(e.message);
86
+ } finally {
87
+ setLoading(false);
88
+ }
89
+ };
90
+
91
+ return (
92
+ <div style={styles.page}>
93
+ <header style={styles.header}>
94
+ <div style={styles.headerLeft}>
95
+ <a href="/" style={styles.backLink}>← Chat</a>
96
+ <h1 style={styles.title}>🔬 Scraper Debug</h1>
97
+ </div>
98
+ <span style={styles.badge}>Debug Tool</span>
99
+ </header>
100
+
101
+ {/* Window Selector */}
102
+ <section style={styles.section}>
103
+ <h2 style={styles.sectionTitle}>1. Select Window</h2>
104
+ <div style={styles.windowGrid}>
105
+ {windows.map((w) => (
106
+ <button
107
+ key={w.index}
108
+ style={{
109
+ ...styles.windowCard,
110
+ ...(selectedIdx === w.index ? styles.windowCardActive : {}),
111
+ }}
112
+ onClick={() => selectWindow(w.index)}
113
+ >
114
+ <div style={windowDotStyle(selectedIdx === w.index)} />
115
+ <div style={styles.windowInfo}>
116
+ <span style={styles.windowTitle}>{w.title || `Window ${w.index}`}</span>
117
+ <span style={styles.windowUrl}>{w.url?.substring(0, 60)}</span>
118
+ </div>
119
+ {selectedIdx === w.index && <span style={styles.activeBadge}>Active</span>}
120
+ </button>
121
+ ))}
122
+ {windows.length === 0 && (
123
+ <div style={styles.emptyState}>No Antigravity windows found. Is the IDE running?</div>
124
+ )}
125
+ </div>
126
+ </section>
127
+
128
+ {/* Scrape Button */}
129
+ <section style={styles.section}>
130
+ <h2 style={styles.sectionTitle}>2. Scrape Last Agent Response</h2>
131
+ <button style={styles.scrapeBtn} onClick={scrape} disabled={loading || selectedIdx === null}>
132
+ {loading ? '⏳ Scraping...' : '🔍 Scrape Now'}
133
+ </button>
134
+ {error && <div style={styles.errorBox}>⚠️ {error}</div>}
135
+ </section>
136
+
137
+ {/* Results */}
138
+ {scrapeResult && (
139
+ <section style={{ ...styles.section, flex: 1, display: 'flex', flexDirection: 'column' as const, overflow: 'hidden' }}>
140
+ {/* Meta Stats */}
141
+ <div style={styles.metaBar}>
142
+ <span>⏱ {scrapeResult.meta.timestamp}</span>
143
+ <span>🔄 Turns: {scrapeResult.meta.turnCount}</span>
144
+ <span>🔧 Tools: {scrapeResult.meta.toolCallCount}</span>
145
+ <span>💬 Responses: {scrapeResult.meta.responseCount}</span>
146
+ <span>{scrapeResult.parsed.isRunning ? '🟢 Running' : '⚪ Idle'}</span>
147
+ </div>
148
+
149
+ {/* Tabs */}
150
+ <div style={styles.tabs}>
151
+ <button style={{ ...styles.tab, ...(activeTab === 'parsed' ? styles.tabActive : {}) }} onClick={() => setActiveTab('parsed')}>
152
+ Parsed State
153
+ </button>
154
+ <button style={{ ...styles.tab, ...(activeTab === 'response' ? styles.tabActive : {}) }} onClick={() => setActiveTab('response')}>
155
+ Rendered Response
156
+ </button>
157
+ <button style={{ ...styles.tab, ...(activeTab === 'raw' ? styles.tabActive : {}) }} onClick={() => setActiveTab('raw')}>
158
+ Raw HTML
159
+ </button>
160
+ </div>
161
+
162
+ {/* Tab Content */}
163
+ <div style={styles.tabContent}>
164
+ {activeTab === 'parsed' && (
165
+ <div style={styles.jsonView}>
166
+ <pre style={styles.pre}>{JSON.stringify(scrapeResult.parsed, null, 2)}</pre>
167
+ </div>
168
+ )}
169
+ {activeTab === 'response' && (
170
+ <div style={styles.renderedView}>
171
+ {scrapeResult.parsed.responses.length > 0 ? (
172
+ scrapeResult.parsed.responses.map((html, i) => (
173
+ <div key={i} style={styles.responseBlock}>
174
+ <div style={styles.responseLabel}>Response #{i}</div>
175
+ <div className="agent-response" dangerouslySetInnerHTML={{ __html: html }} />
176
+ </div>
177
+ ))
178
+ ) : (
179
+ <div style={styles.emptyState}>No responses scraped from the agent panel.</div>
180
+ )}
181
+ </div>
182
+ )}
183
+ {activeTab === 'raw' && (
184
+ <div style={styles.rawView}>
185
+ <pre style={styles.pre}>{scrapeResult.raw}</pre>
186
+ </div>
187
+ )}
188
+ </div>
189
+ </section>
190
+ )}
191
+ </div>
192
+ );
193
+ }
194
+
195
+ /* ── Inline Styles ── */
196
+ const windowDotStyle = (active: boolean): React.CSSProperties => ({ width: '8px', height: '8px', borderRadius: '50%', background: active ? '#34d399' : '#686888', boxShadow: active ? '0 0 8px rgba(52, 211, 153, 0.4)' : 'none', flexShrink: 0 });
197
+
198
+ const styles: Record<string, React.CSSProperties> = {
199
+ page: { display: 'flex', flexDirection: 'column', height: '100vh', maxWidth: '1100px', margin: '0 auto', padding: '0 16px', overflow: 'hidden' },
200
+ header: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px 0', borderBottom: '1px solid rgba(255,255,255,0.06)' },
201
+ headerLeft: { display: 'flex', alignItems: 'center', gap: '16px' },
202
+ backLink: { color: '#6366f1', textDecoration: 'none', fontSize: '13px', fontWeight: 500 },
203
+ title: { fontSize: '20px', fontWeight: 700 },
204
+ badge: { fontSize: '10px', textTransform: 'uppercase' as const, letterSpacing: '0.1em', color: '#686888', border: '1px solid rgba(255,255,255,0.06)', padding: '4px 10px', borderRadius: '12px' },
205
+ section: { padding: '16px 0' },
206
+ sectionTitle: { fontSize: '14px', fontWeight: 600, color: '#9898b0', marginBottom: '12px', textTransform: 'uppercase' as const, letterSpacing: '0.05em' },
207
+ windowGrid: { display: 'flex', flexDirection: 'column' as const, gap: '8px' },
208
+ windowCard: { display: 'flex', alignItems: 'center', gap: '12px', width: '100%', padding: '12px 16px', background: '#12121a', border: '1px solid rgba(255,255,255,0.06)', borderRadius: '10px', cursor: 'pointer', transition: 'all 0.15s ease', textAlign: 'left' as const, color: '#9898b0', fontFamily: 'inherit', fontSize: '13px' },
209
+ windowCardActive: { borderColor: 'rgba(99, 102, 241, 0.4)', background: 'rgba(99, 102, 241, 0.06)', color: '#e8e8f0' },
210
+ windowInfo: { flex: 1, display: 'flex', flexDirection: 'column' as const, gap: '2px', overflow: 'hidden' },
211
+ windowTitle: { fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' as const },
212
+ windowUrl: { fontSize: '10px', color: '#686888', fontFamily: "'JetBrains Mono', monospace", overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' as const },
213
+ activeBadge: { fontSize: '10px', color: '#34d399', background: 'rgba(52, 211, 153, 0.1)', padding: '2px 8px', borderRadius: '8px', fontWeight: 600, flexShrink: 0 },
214
+ scrapeBtn: { padding: '10px 24px', borderRadius: '10px', border: 'none', background: 'linear-gradient(135deg, #6366f1, #a855f7)', color: 'white', fontSize: '14px', fontWeight: 600, cursor: 'pointer', transition: 'all 0.2s ease', fontFamily: 'inherit' },
215
+ errorBox: { marginTop: '12px', padding: '10px 14px', background: 'rgba(239, 68, 68, 0.06)', border: '1px solid rgba(239, 68, 68, 0.4)', borderRadius: '8px', color: '#ef4444', fontSize: '13px' },
216
+ metaBar: { display: 'flex', gap: '16px', flexWrap: 'wrap' as const, padding: '10px 14px', background: '#12121a', borderRadius: '8px', fontSize: '11px', color: '#9898b0', fontFamily: "'JetBrains Mono', monospace", marginBottom: '12px' },
217
+ tabs: { display: 'flex', gap: '4px', marginBottom: '12px' },
218
+ tab: { padding: '8px 16px', borderRadius: '8px', border: '1px solid rgba(255,255,255,0.06)', background: 'transparent', color: '#9898b0', fontSize: '12px', fontWeight: 500, cursor: 'pointer', fontFamily: 'inherit', transition: 'all 0.15s ease' },
219
+ tabActive: { background: 'rgba(99, 102, 241, 0.1)', borderColor: 'rgba(99, 102, 241, 0.4)', color: '#e8e8f0' },
220
+ tabContent: { flex: 1, overflow: 'auto', borderRadius: '10px', border: '1px solid rgba(255,255,255,0.06)', background: '#0d0d14' },
221
+ jsonView: { padding: '16px' },
222
+ rawView: { padding: '16px' },
223
+ renderedView: { padding: '16px' },
224
+ pre: { margin: 0, fontSize: '12px', lineHeight: 1.6, color: '#9898b0', fontFamily: "'JetBrains Mono', monospace", whiteSpace: 'pre-wrap' as const, wordBreak: 'break-all' as const },
225
+ responseBlock: { padding: '16px', background: 'rgba(255,255,255,0.02)', borderRadius: '8px', marginBottom: '12px', border: '1px solid rgba(255,255,255,0.04)' },
226
+ responseLabel: { fontSize: '10px', color: '#686888', textTransform: 'uppercase' as const, letterSpacing: '0.08em', marginBottom: '8px', fontWeight: 600 },
227
+ emptyState: { textAlign: 'center' as const, padding: '32px', color: '#686888', fontSize: '13px' },
228
+ };
Binary file