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,138 @@
1
+ /**
2
+ * Chat history scraper.
3
+ * Scrolls the Antigravity conversation view to de-virtualize all content,
4
+ * then walks the DOM to extract user/agent messages in order.
5
+ */
6
+
7
+ import type { ProxyContext, ChatHistory } from '../types';
8
+
9
+ export async function getChatHistory(ctx: ProxyContext): Promise<ChatHistory> {
10
+ if (!ctx.workbenchPage) {
11
+ return { isRunning: false, turnCount: 0, turns: [] };
12
+ }
13
+
14
+ return await ctx.workbenchPage.evaluate(async () => {
15
+ const panel = document.querySelector('.antigravity-agent-side-panel');
16
+ if (!panel) return { isRunning: false, turnCount: 0, turns: [] };
17
+
18
+ const conversation =
19
+ panel.querySelector('#conversation') ||
20
+ document.querySelector('#conversation');
21
+ const scrollArea = conversation?.querySelector('.overflow-y-auto');
22
+ if (!scrollArea)
23
+ return { isRunning: false, turnCount: 0, turns: [] };
24
+
25
+ // Step 1: Scroll to top to force older content to render
26
+ scrollArea.scrollTop = 0;
27
+ await new Promise((r) => setTimeout(r, 300));
28
+
29
+ // Step 2: Incrementally scroll down to de-virtualize all content
30
+ const scrollHeight = scrollArea.scrollHeight;
31
+ const viewportHeight = scrollArea.clientHeight;
32
+ const scrollStep = viewportHeight * 0.8;
33
+ let pos = 0;
34
+ while (pos < scrollHeight) {
35
+ scrollArea.scrollTop = pos;
36
+ await new Promise((r) => setTimeout(r, 100));
37
+ pos += scrollStep;
38
+ }
39
+ scrollArea.scrollTop = scrollArea.scrollHeight;
40
+ await new Promise((r) => setTimeout(r, 200));
41
+
42
+ // Step 3: Walk the DOM to find messages in document order
43
+ const turns: { role: 'user' | 'agent'; content: string }[] = [];
44
+ const seen = new Set<string>();
45
+
46
+ const msgList =
47
+ (scrollArea as Element).querySelector('.mx-auto') || scrollArea;
48
+
49
+ const candidates: {
50
+ el: Element;
51
+ role: 'user' | 'agent';
52
+ content: string;
53
+ }[] = [];
54
+
55
+ // Find user messages
56
+ const allWhitespace = msgList.querySelectorAll('.whitespace-pre-wrap');
57
+ for (const el of allWhitespace) {
58
+ const text = el.textContent?.trim();
59
+ if (!text) continue;
60
+
61
+ let isInsideAgentResponse = false;
62
+ let parent = el.parentElement;
63
+ while (parent && parent !== msgList) {
64
+ const cls = parent.getAttribute('class') || '';
65
+ if (
66
+ cls.includes('leading-relaxed') &&
67
+ cls.includes('select-text')
68
+ ) {
69
+ isInsideAgentResponse = true;
70
+ break;
71
+ }
72
+ parent = parent.parentElement;
73
+ }
74
+ if (isInsideAgentResponse) continue;
75
+
76
+ if (el.closest('[data-lexical-editor]')) continue;
77
+ if (el.closest('#antigravity\\.agentSidePanelInputBox')) continue;
78
+
79
+ const key = 'user:' + text.substring(0, 200);
80
+ if (seen.has(key)) continue;
81
+ seen.add(key);
82
+
83
+ candidates.push({ el, role: 'user', content: text });
84
+ }
85
+
86
+ // Find agent response blocks
87
+ const allResponses = msgList.querySelectorAll(
88
+ '.leading-relaxed.select-text'
89
+ );
90
+ for (const el of allResponses) {
91
+ let hidden = false;
92
+ let ancestor = el.parentElement;
93
+ let depth = 0;
94
+ while (ancestor && ancestor !== msgList && depth < 15) {
95
+ const cls = ancestor.getAttribute('class') || '';
96
+ if (cls.includes('max-h-0') || cls.includes('hidden')) {
97
+ hidden = true;
98
+ break;
99
+ }
100
+ ancestor = ancestor.parentElement;
101
+ depth++;
102
+ }
103
+ if (hidden) continue;
104
+
105
+ const clone = el.cloneNode(true) as Element;
106
+ clone.querySelectorAll('style, script').forEach((n) => n.remove());
107
+ const html = (clone as HTMLElement).innerHTML?.trim();
108
+ if (!html) continue;
109
+
110
+ const key = 'agent:' + el.textContent?.trim().substring(0, 200);
111
+ if (seen.has(key)) continue;
112
+ seen.add(key);
113
+
114
+ candidates.push({ el, role: 'agent', content: html });
115
+ }
116
+
117
+ // Sort by document position
118
+ candidates.sort((a, b) => {
119
+ const pos = a.el.compareDocumentPosition(b.el);
120
+ if (pos & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
121
+ if (pos & Node.DOCUMENT_POSITION_PRECEDING) return 1;
122
+ return 0;
123
+ });
124
+
125
+ for (const c of candidates) {
126
+ turns.push({ role: c.role, content: c.content });
127
+ }
128
+
129
+ // Scroll back to bottom
130
+ scrollArea.scrollTop = scrollArea.scrollHeight;
131
+
132
+ return {
133
+ isRunning: false,
134
+ turnCount: turns.length,
135
+ turns,
136
+ };
137
+ });
138
+ }
@@ -0,0 +1,124 @@
1
+ import type { ProxyContext, Conversation } from '../types';
2
+ import { logger } from '../logger';
3
+
4
+ export interface IdeConversation {
5
+ title: string;
6
+ active: boolean;
7
+ index: number;
8
+ }
9
+
10
+ /**
11
+ * Gets the list of available conversations directly from the IDE's UI.
12
+ * This ensures we only see conversations for the current window context.
13
+ */
14
+ export async function getIdeConversations(ctx: ProxyContext): Promise<IdeConversation[]> {
15
+ try {
16
+ logger.info('[Scraper] Fetching conversations strictly from IDE UI...');
17
+
18
+ if (!ctx.workbenchPage) {
19
+ logger.info('[Scraper] No active workbench page.');
20
+ return [];
21
+ }
22
+
23
+ const result = await ctx.workbenchPage.evaluate(async () => {
24
+ try {
25
+ // 1. Get the CURRENT active conversation title from the chat panel header
26
+ const activeHeaderEl = document.querySelector('span.font-semibold.text-ide-text-color');
27
+ const activeTitle = activeHeaderEl && activeHeaderEl.textContent ? activeHeaderEl.textContent.trim() : null;
28
+
29
+ // 2. Find the history button
30
+ const historyBtn = document.querySelector('a[data-past-conversations-toggle="true"]');
31
+
32
+ if (!historyBtn) {
33
+ return { error: 'History button not found (a[data-past-conversations-toggle="true"])' };
34
+ }
35
+
36
+ let isAlreadyOpen = !!document.querySelector('.text-quickinput-foreground.opacity-50');
37
+
38
+ if (!isAlreadyOpen) {
39
+ historyBtn.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window }));
40
+ historyBtn.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true, view: window }));
41
+ historyBtn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
42
+
43
+ for (let i = 0; i < 20; i++) {
44
+ await new Promise(r => setTimeout(r, 100));
45
+ if (document.querySelector('.text-quickinput-foreground.opacity-50')) {
46
+ isAlreadyOpen = true;
47
+ break;
48
+ }
49
+ }
50
+ }
51
+
52
+ if (!isAlreadyOpen) {
53
+ return { error: 'History dropdown did not appear after clicking' };
54
+ }
55
+
56
+ // 3. Extract the conversations
57
+ const rowSelector = '.cursor-pointer.flex.items-center.justify-between.rounded-md.text-quickinput-foreground';
58
+ const rowElements = Array.from(document.querySelectorAll(rowSelector));
59
+
60
+ const rows = rowElements.map((row, index) => {
61
+ const titleEl = row.querySelector('.truncate span');
62
+ const title = titleEl ? titleEl.textContent?.trim() || '' : row.textContent?.trim() || '';
63
+
64
+ let isActive = row.className.includes('bg-gray-500/10') || !!row.querySelector('svg.lucide-circle');
65
+ if (activeTitle && title === activeTitle) {
66
+ isActive = true;
67
+ }
68
+
69
+ return { title, active: isActive, index };
70
+ });
71
+
72
+ // 4. Close the dropdown
73
+ historyBtn.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window }));
74
+ historyBtn.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true, view: window }));
75
+ historyBtn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
76
+
77
+ return { rows, activeTitle };
78
+ } catch (e: any) {
79
+ return { error: e.message };
80
+ }
81
+ });
82
+
83
+ if (result?.error) {
84
+ throw new Error(`Failed to scrape IDE conversations: ${result.error}`);
85
+ }
86
+
87
+ if (!result?.rows) {
88
+ logger.info('[Scraper] No rows returned from conversation scraper snippet.');
89
+ return [];
90
+ }
91
+
92
+ logger.info(`[Scraper] Successfully scraped ${result.rows.length} conversations. Active title: ${result.activeTitle}`);
93
+
94
+ let foundActive = false;
95
+ const conversations: IdeConversation[] = result.rows.map((r: any) => {
96
+ let active = r.active;
97
+
98
+ if (result.activeTitle && r.title === result.activeTitle) {
99
+ active = true;
100
+ }
101
+
102
+ if (active) foundActive = true;
103
+
104
+ return {
105
+ title: r.title,
106
+ active,
107
+ index: r.index
108
+ };
109
+ });
110
+
111
+ if (!foundActive && result.activeTitle) {
112
+ conversations.unshift({
113
+ title: result.activeTitle,
114
+ active: true,
115
+ index: -1
116
+ });
117
+ }
118
+
119
+ return conversations;
120
+ } catch (err: any) {
121
+ logger.error(`[Scraper] Error fetching IDE conversations: ${err.message}`);
122
+ throw err;
123
+ }
124
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * State diffing for SSE stream.
3
+ * Compares two agent states and returns typed events for any changes.
4
+ */
5
+
6
+ import type { AgentState, SSEStep } from '@/lib/types';
7
+ import { logger } from '@/lib/logger';
8
+
9
+ export function diffStates(prev: AgentState, curr: AgentState): SSEStep[] {
10
+ const events: SSEStep[] = [];
11
+
12
+ // New thinking blocks
13
+ if (curr.thinking.length > prev.thinking.length) {
14
+ for (let i = prev.thinking.length; i < curr.thinking.length; i++) {
15
+ events.push({ type: 'thinking', data: curr.thinking[i] as any });
16
+ }
17
+ }
18
+
19
+ // New or updated tool calls
20
+ if (curr.toolCalls.length > prev.toolCalls.length) {
21
+ for (let i = prev.toolCalls.length; i < curr.toolCalls.length; i++) {
22
+ logger.debug(
23
+ `[Diff] Detected tool_call change at index ${i}: id=${curr.toolCalls[i]?.id}, status=${curr.toolCalls[i]?.status}`
24
+ );
25
+ events.push({
26
+ type: 'tool_call',
27
+ data: { ...curr.toolCalls[i], index: i, isNew: true } as any,
28
+ });
29
+ }
30
+ }
31
+
32
+ // Updated existing tool calls
33
+ const sharedLen = Math.min(prev.toolCalls.length, curr.toolCalls.length);
34
+ for (let i = 0; i < sharedLen; i++) {
35
+ const p = prev.toolCalls[i];
36
+ const c = curr.toolCalls[i];
37
+ const footerChanged =
38
+ JSON.stringify(p.footerButtons) !== JSON.stringify(c.footerButtons);
39
+ if (
40
+ p.status !== c.status ||
41
+ p.exitCode !== c.exitCode ||
42
+ p.hasCancelBtn !== c.hasCancelBtn ||
43
+ footerChanged
44
+ ) {
45
+ console.log(
46
+ `[diffStates] UPDATED tool_call at index ${i}: status ${p.status}->${c.status}, exitCode ${p.exitCode}->${c.exitCode}, footerChanged=${footerChanged}`
47
+ );
48
+ events.push({
49
+ type: 'tool_call',
50
+ data: { ...c, index: i, isNew: false } as any,
51
+ });
52
+ }
53
+ }
54
+
55
+ // HITL state changes
56
+ const prevHITL = prev.toolCalls.some((t) => t.hasCancelBtn);
57
+ const currHITL = curr.toolCalls.some((t) => t.hasCancelBtn);
58
+ if (currHITL && !prevHITL) {
59
+ const hitlTool = curr.toolCalls.find((t) => t.hasCancelBtn);
60
+ events.push({
61
+ type: 'hitl',
62
+ data: { action: 'approval_required', tool: hitlTool } as any,
63
+ });
64
+ } else if (!currHITL && prevHITL) {
65
+ events.push({
66
+ type: 'hitl',
67
+ data: { action: 'resolved' } as any,
68
+ });
69
+ }
70
+
71
+ // New response blocks
72
+ if (curr.responses.length > prev.responses.length) {
73
+ for (let i = prev.responses.length; i < curr.responses.length; i++) {
74
+ events.push({
75
+ type: 'response',
76
+ data: {
77
+ content: curr.responses[i],
78
+ index: i,
79
+ partial: curr.isRunning,
80
+ } as any,
81
+ });
82
+ }
83
+ }
84
+ // Updated last response
85
+ if (
86
+ curr.responses.length > 0 &&
87
+ prev.responses.length > 0 &&
88
+ curr.responses.length === prev.responses.length
89
+ ) {
90
+ const lastIdx = curr.responses.length - 1;
91
+ if (curr.responses[lastIdx] !== prev.responses[lastIdx]) {
92
+ events.push({
93
+ type: 'response',
94
+ data: {
95
+ content: curr.responses[lastIdx],
96
+ index: lastIdx,
97
+ partial: curr.isRunning,
98
+ } as any,
99
+ });
100
+ }
101
+ }
102
+
103
+ // Notification blocks
104
+ if (curr.notifications.length > prev.notifications.length) {
105
+ for (let i = prev.notifications.length; i < curr.notifications.length; i++) {
106
+ events.push({
107
+ type: 'notification',
108
+ data: { content: curr.notifications[i], index: i } as any,
109
+ });
110
+ }
111
+ }
112
+
113
+ // File changes
114
+ if (
115
+ curr.fileChanges &&
116
+ prev.fileChanges &&
117
+ curr.fileChanges.length > prev.fileChanges.length
118
+ ) {
119
+ for (let i = prev.fileChanges.length; i < curr.fileChanges.length; i++) {
120
+ events.push({ type: 'file_change', data: curr.fileChanges[i] as any });
121
+ }
122
+ }
123
+
124
+ // Status change
125
+ if (prev.isRunning !== curr.isRunning) {
126
+ events.push({
127
+ type: 'status',
128
+ data: { isRunning: curr.isRunning } as any,
129
+ });
130
+ }
131
+
132
+ // Error
133
+ if (curr.error && !prev.error) {
134
+ events.push({
135
+ type: 'error',
136
+ data: { message: curr.error } as any,
137
+ });
138
+ }
139
+
140
+ return events;
141
+ }
package/lib/types.ts ADDED
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Shared TypeScript types for the Antigravity Chat Proxy.
3
+ */
4
+
5
+ import type { Page, Browser } from 'puppeteer-core';
6
+
7
+ // ── Context (shared server state) ──
8
+
9
+ export interface ProxyContext {
10
+ workbenchPage: Page | null;
11
+ browser: Browser | null;
12
+ allWorkbenches: WorkbenchInfo[];
13
+ activeWindowIdx: number;
14
+ activeConversationId: string | null;
15
+ activeTitle?: string | null;
16
+ lastActionTimestamp: number;
17
+ }
18
+
19
+ export interface WorkbenchInfo {
20
+ page: Page;
21
+ title: string;
22
+ url: string;
23
+ }
24
+
25
+ // ── Agent State (from scraper) ──
26
+
27
+ export interface ToolCall {
28
+ id: string;
29
+ status: string;
30
+ type: string;
31
+ path: string;
32
+ command: string | null;
33
+ exitCode: string | null;
34
+ hasCancelBtn: boolean;
35
+ footerButtons: string[];
36
+ hasTerminal: boolean;
37
+ terminalOutput: string | null;
38
+ additions?: string | null;
39
+ deletions?: string | null;
40
+ lineRange?: string | null;
41
+ mcpToolName?: string | null;
42
+ mcpArgs?: string | null;
43
+ mcpOutput?: string | null;
44
+ }
45
+
46
+ export interface ThinkingBlock {
47
+ time: string;
48
+ }
49
+
50
+ export interface FileChange {
51
+ fileName: string;
52
+ type: string;
53
+ }
54
+
55
+ export interface AgentState {
56
+ isRunning: boolean;
57
+ turnCount: number;
58
+ stepGroupCount: number;
59
+ thinking: ThinkingBlock[];
60
+ toolCalls: ToolCall[];
61
+ responses: string[];
62
+ notifications: string[];
63
+ error: string | null;
64
+ fileChanges: FileChange[];
65
+ lastTurnResponseHTML: string;
66
+ }
67
+
68
+ export interface ChatTurn {
69
+ role: 'user' | 'agent';
70
+ content: string;
71
+ }
72
+
73
+ export interface ChatHistory {
74
+ isRunning: boolean;
75
+ turnCount: number;
76
+ turns: ChatTurn[];
77
+ }
78
+
79
+ // ── SSE Events ──
80
+
81
+ export type SSEEventType =
82
+ | 'thinking'
83
+ | 'tool_call'
84
+ | 'hitl'
85
+ | 'response'
86
+ | 'notification'
87
+ | 'file_change'
88
+ | 'status'
89
+ | 'done'
90
+ | 'error';
91
+
92
+ export interface SSEEvent {
93
+ type: SSEEventType;
94
+ data: Record<string, unknown>;
95
+ }
96
+
97
+ // ── Conversation & Artifacts ──
98
+
99
+ export interface ConversationFile {
100
+ name: string;
101
+ size: number;
102
+ mtime: string;
103
+ }
104
+
105
+ export interface Conversation {
106
+ id: string;
107
+ title: string | null;
108
+ files: ConversationFile[];
109
+ mtime: string;
110
+ active: boolean;
111
+ }
112
+
113
+ // ── Frontend Types ──
114
+
115
+ export interface WindowInfo {
116
+ index: number;
117
+ title: string;
118
+ url: string;
119
+ active: boolean;
120
+ }
121
+
122
+ export interface ConversationInfo {
123
+ id: string; // The backend brain UUID, or "-1" for unknown
124
+ title: string;
125
+ active: boolean;
126
+ index: number;
127
+ mtime?: string;
128
+ files?: ArtifactFile[];
129
+ }
130
+
131
+ export interface ArtifactFile {
132
+ name: string;
133
+ size: number;
134
+ mtime: string;
135
+ }
136
+
137
+ export interface ChatMessage {
138
+ role: 'user' | 'agent';
139
+ content: string;
140
+ steps?: SSEStep[];
141
+ }
142
+
143
+ export interface SSEStep {
144
+ type: string;
145
+ data: Record<string, any>;
146
+ }
package/lib/utils.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Shared utility functions.
3
+ */
4
+
5
+ export function sleep(ms: number): Promise<void> {
6
+ return new Promise((r) => setTimeout(r, ms));
7
+ }
package/next.config.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* config options here */
5
+ };
6
+
7
+ export default nextConfig;
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "antigravity-chat-proxy",
3
+ "version": "0.1.0",
4
+ "description": "A web chat proxy for Antigravity IDE with ngrok OAuth tunnel support",
5
+ "keywords": [
6
+ "antigravity",
7
+ "ide",
8
+ "chat",
9
+ "proxy",
10
+ "ngrok"
11
+ ],
12
+ "license": "MIT",
13
+ "bin": {
14
+ "antigravity-chat-proxy": "bin/cli.js"
15
+ },
16
+ "files": [
17
+ "bin/",
18
+ "app/",
19
+ "components/",
20
+ "hooks/",
21
+ "lib/",
22
+ "public/",
23
+ "next.config.ts",
24
+ "tsconfig.json",
25
+ "package.json"
26
+ ],
27
+ "scripts": {
28
+ "dev": "next dev -p 5555",
29
+ "build": "next build",
30
+ "start": "next start -p 5555",
31
+ "tunnel": "node bin/cli.js",
32
+ "lint": "next lint",
33
+ "type-check": "tsc --noEmit"
34
+ },
35
+ "dependencies": {
36
+ "@ngrok/ngrok": "^1.7.0",
37
+ "next": "16.1.6",
38
+ "puppeteer-core": "^24.39.0",
39
+ "react": "19.2.3",
40
+ "react-dom": "19.2.3"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20",
44
+ "@types/react": "^19",
45
+ "@types/react-dom": "^19",
46
+ "eslint": "^9.39.4",
47
+ "eslint-config-next": "^16.1.6",
48
+ "typescript": "^5"
49
+ }
50
+ }
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
package/tsconfig.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ },
25
+ "include": [
26
+ "next-env.d.ts",
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ ".next/types/**/*.ts",
30
+ ".next/dev/types/**/*.ts",
31
+ "**/*.mts"
32
+ ],
33
+ "exclude": ["node_modules"]
34
+ }