lazy-gravity 0.0.2 → 0.0.4

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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +224 -0
  3. package/dist/bin/cli.js +79 -0
  4. package/dist/bin/commands/doctor.js +156 -0
  5. package/dist/bin/commands/open.js +145 -0
  6. package/dist/bin/commands/setup.js +366 -0
  7. package/dist/bin/commands/start.js +15 -0
  8. package/dist/bot/index.js +914 -0
  9. package/dist/commands/chatCommandHandler.js +145 -0
  10. package/dist/commands/cleanupCommandHandler.js +396 -0
  11. package/dist/commands/messageParser.js +28 -0
  12. package/dist/commands/registerSlashCommands.js +149 -0
  13. package/dist/commands/slashCommandHandler.js +104 -0
  14. package/dist/commands/workspaceCommandHandler.js +230 -0
  15. package/dist/database/chatSessionRepository.js +88 -0
  16. package/dist/database/scheduleRepository.js +119 -0
  17. package/dist/database/templateRepository.js +103 -0
  18. package/dist/database/workspaceBindingRepository.js +109 -0
  19. package/dist/events/interactionCreateHandler.js +286 -0
  20. package/dist/events/messageCreateHandler.js +154 -0
  21. package/dist/index.js +10 -0
  22. package/dist/middleware/auth.js +10 -0
  23. package/dist/middleware/sanitize.js +20 -0
  24. package/dist/services/antigravityLauncher.js +89 -0
  25. package/dist/services/approvalDetector.js +384 -0
  26. package/dist/services/autoAcceptService.js +80 -0
  27. package/dist/services/cdpBridgeManager.js +204 -0
  28. package/dist/services/cdpConnectionPool.js +157 -0
  29. package/dist/services/cdpService.js +1311 -0
  30. package/dist/services/channelManager.js +118 -0
  31. package/dist/services/chatSessionService.js +516 -0
  32. package/dist/services/modeService.js +73 -0
  33. package/dist/services/modelService.js +63 -0
  34. package/dist/services/processManager.js +61 -0
  35. package/dist/services/progressSender.js +61 -0
  36. package/dist/services/promptDispatcher.js +17 -0
  37. package/dist/services/quotaService.js +185 -0
  38. package/dist/services/responseMonitor.js +645 -0
  39. package/dist/services/scheduleService.js +134 -0
  40. package/dist/services/screenshotService.js +85 -0
  41. package/dist/services/titleGeneratorService.js +113 -0
  42. package/dist/services/workspaceService.js +64 -0
  43. package/dist/ui/autoAcceptUi.js +34 -0
  44. package/dist/ui/modeUi.js +34 -0
  45. package/dist/ui/modelsUi.js +97 -0
  46. package/dist/ui/screenshotUi.js +51 -0
  47. package/dist/ui/templateUi.js +67 -0
  48. package/dist/utils/cdpPorts.js +5 -0
  49. package/dist/utils/config.js +20 -0
  50. package/dist/utils/configLoader.js +160 -0
  51. package/dist/utils/discordFormatter.js +167 -0
  52. package/dist/utils/i18n.js +77 -0
  53. package/dist/utils/imageHandler.js +154 -0
  54. package/dist/utils/lockfile.js +113 -0
  55. package/dist/utils/logger.js +32 -0
  56. package/dist/utils/logo.js +13 -0
  57. package/dist/utils/metadataExtractor.js +15 -0
  58. package/dist/utils/processLogBuffer.js +98 -0
  59. package/dist/utils/streamMessageFormatter.js +90 -0
  60. package/package.json +73 -5
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LazyGravity Project
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,224 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/tokyoweb3/LazyGravity/main/docs/assets/LazyGravityBanner.png" alt="LazyGravity Banner" width="100%" />
3
+ </p>
4
+
5
+ <p align="center">
6
+ <img src="https://img.shields.io/badge/version-0.0.1-blue?style=flat-square" alt="Version" />
7
+ <img src="https://img.shields.io/badge/node-18.x+-brightgreen?style=flat-square&logo=node.js" alt="Node.js" />
8
+ <img src="https://img.shields.io/badge/discord.js-14.x-5865F2?style=flat-square&logo=discord&logoColor=white" alt="discord.js" />
9
+ <img src="https://img.shields.io/badge/protocol-CDP%20%2F%20WebSocket-orange?style=flat-square" alt="CDP/WebSocket" />
10
+ <img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="License" />
11
+ </p>
12
+
13
+ # LazyGravity
14
+
15
+ **LazyGravity** is a local, secure Discord Bot that lets you remotely operate [Antigravity](https://antigravity.dev) on your home PC — from your smartphone's Discord app, anywhere.
16
+
17
+ Send natural language instructions like "fix that bug" or "start designing the new feature" from your phone. Antigravity executes them locally on your home PC using its full resources, and reports results back to Discord.
18
+
19
+ <p align="center">
20
+ <video src="https://github.com/user-attachments/assets/84eca973-59e8-4ffa-93e9-fba78ba72f74" width="100%" controls autoplay muted loop>
21
+ Your browser does not support the video tag.
22
+ </video>
23
+ </p>
24
+
25
+
26
+ ## Quick Setup
27
+
28
+ Runtime: **Node >= 18**.
29
+
30
+ ```bash
31
+ npm install -g lazy-gravity
32
+ lazy-gravity setup
33
+ ```
34
+
35
+ The interactive wizard walks you through Discord bot creation, token setup, and workspace configuration. When done:
36
+
37
+ ```bash
38
+ lazy-gravity open # Launch Antigravity with CDP enabled
39
+ lazy-gravity start # Start the Discord bot
40
+ ```
41
+
42
+ Or run directly without installing:
43
+
44
+ ```bash
45
+ npx lazy-gravity
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Features
51
+
52
+ 1. **Fully Local & Secure**
53
+ - **No external server or port exposure** — runs as a local process on your PC, communicating directly with Discord.
54
+ - **Whitelist access control**: only authorized Discord user IDs (`allowedUserIds`) can interact with the bot.
55
+ - **Secure credential management**: Bot tokens and API keys are stored locally (never in source code).
56
+ - **Path traversal prevention & resource protection**: sandboxed directory access and concurrent task limits prevent abuse.
57
+
58
+ 2. **Project Management (Channel-Directory Binding)**
59
+ - Use `/project` to bind a Discord channel to a local project directory via an interactive select menu with buttons.
60
+ - Messages sent in a bound channel are automatically forwarded to Antigravity with the correct project context.
61
+
62
+ 3. **Context-Aware Embed Replies**
63
+ - Results are delivered as rich Discord Embeds. Use Discord's Reply feature on any result to continue the conversation — the bot preserves full context (directory, task history) across reply chains.
64
+
65
+ 4. **Real-Time Progress Monitoring**
66
+ - Long-running Antigravity tasks report progress as a series of messages (delivery confirmed / planning / analysis / execution / implementation / final summary).
67
+
68
+ 5. **File Attachments & Context Parsing**
69
+ - Send images (screenshots, mockups) or text files via Discord — they are automatically forwarded to Antigravity as context.
70
+
71
+ ## Usage & Commands
72
+
73
+ ### Natural Language Messages
74
+ Just type in any bound channel:
75
+ > `refactor the components under src/components. Make the layout look like yesterday's screenshot` (with image attached)
76
+
77
+ ### Slash Commands
78
+
79
+ - `📂 /project list` — Browse projects via select menu; selecting one auto-creates a category and session channel
80
+ - `📂 /project create <name>` — Create a new project directory + Discord category/channel
81
+ - `💬 /new` — Start a new Antigravity chat session in the current project
82
+ - `💬 /chat` — Show current session info and list all sessions in the project
83
+ - `⚙️ /model [name]` — Switch the LLM model (e.g. `gpt-4o`, `claude-3-opus`, `gemini-1.5-pro`)
84
+ - `⚙️ /mode` — Switch execution mode via dropdown (`code`, `architect`, `ask`, etc.)
85
+ - `📝 /template list` — Display registered templates with execute buttons
86
+ - `📝 /template add <name> <prompt>` — Register a new prompt template
87
+ - `📝 /template delete <name>` — Delete a template
88
+ - `🛑 /stop` — Force-stop a running Antigravity task
89
+ - `📸 /screenshot` — Capture and send Antigravity's current screen
90
+ - `🔧 /status` — Show bot connection status, current mode, and active project
91
+ - `✅ /autoaccept [on|off|status]` — Toggle auto-approval of file edit dialogs
92
+ - `🧹 /cleanup [days]` — Scan and clean up inactive session channels (default: 7 days)
93
+ - `❓ /help` — Display list of available commands
94
+
95
+ ### CLI Commands
96
+
97
+ ```bash
98
+ lazy-gravity # Auto: runs setup if unconfigured, otherwise starts the bot
99
+ lazy-gravity setup # Interactive setup wizard
100
+ lazy-gravity open # Open Antigravity with CDP (auto-selects available port)
101
+ lazy-gravity start # Start the Discord bot
102
+ lazy-gravity doctor # Check environment and dependencies
103
+ lazy-gravity --version # Show version
104
+ lazy-gravity --help # Show help
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Setup (Detailed)
110
+
111
+ ### Option A: npm (Recommended)
112
+
113
+ ```bash
114
+ npm install -g lazy-gravity
115
+ lazy-gravity setup
116
+ ```
117
+
118
+ The wizard guides you through 4 steps:
119
+
120
+ 1. **Discord Bot Token** — create a bot at the [Discord Developer Portal](https://discord.com/developers/applications), enable Privileged Gateway Intents (PRESENCE, SERVER MEMBERS, MESSAGE CONTENT), and copy the token. Client ID is extracted from the token automatically.
121
+ 2. **Guild (Server) ID** — for instant slash command registration (optional; press Enter to skip).
122
+ 3. **Allowed User IDs** — Discord users authorized to interact with the bot.
123
+ 4. **Workspace Directory** — parent directory where your coding projects live.
124
+
125
+ Config is saved to `~/.lazy-gravity/config.json`.
126
+
127
+ ### Option B: From source
128
+
129
+ ```bash
130
+ git clone https://github.com/tokyoweb3/LazyGravity.git
131
+ cd LazyGravity
132
+ npm install
133
+ ```
134
+
135
+ Set up your `.env` file:
136
+
137
+ ```bash
138
+ cp .env.example .env
139
+ ```
140
+
141
+ Edit `.env` and fill in the required values:
142
+
143
+ ```env
144
+ DISCORD_BOT_TOKEN=your_bot_token_here
145
+ GUILD_ID=your_guild_id_here
146
+ ALLOWED_USER_IDS=123456789,987654321
147
+ WORKSPACE_BASE_DIR=~/Code
148
+ ```
149
+
150
+ Then start the bot:
151
+
152
+ ```bash
153
+ npm run start
154
+ ```
155
+
156
+ Alternatively, you can build and use the CLI:
157
+
158
+ ```bash
159
+ npm run build
160
+ node dist/bin/cli.js setup # or: node dist/bin/cli.js start
161
+ ```
162
+
163
+ ### Launch Antigravity with CDP
164
+
165
+ LazyGravity connects to Antigravity via Chrome DevTools Protocol (CDP).
166
+ You need to launch Antigravity with a remote debugging port enabled.
167
+
168
+ ```bash
169
+ # Easiest way (auto-selects an available port):
170
+ lazy-gravity open
171
+ ```
172
+
173
+ If you cloned from source, you can also use the bundled launcher scripts (they auto-detect an available port from 9222–9666):
174
+
175
+ #### macOS
176
+ Double-click **`start_antigravity_mac.command`** in the repo root.
177
+
178
+ - **First run**: if you get a permission error, run `chmod +x start_antigravity_mac.command` once in the terminal.
179
+
180
+ #### Windows
181
+ Double-click **`start_antigravity_win.bat`** in the repo root.
182
+
183
+ - **If it doesn't launch**: the executable may not be in your PATH. Right-click the file, edit it, and replace `"Antigravity.exe"` with the full install path (e.g. `"%LOCALAPPDATA%\Programs\Antigravity\Antigravity.exe"`).
184
+
185
+ > **Tip**: CDP ports are auto-scanned from candidates (9222, 9223, 9333, 9444, 9555, 9666).
186
+ > Launch Antigravity first, then start the bot — it connects automatically.
187
+
188
+ ---
189
+
190
+ ## Troubleshooting
191
+
192
+ If the bot is unresponsive or you've updated the code, restart it:
193
+
194
+ 1. **Stop the bot** — press `Ctrl + C` in the terminal, or:
195
+ ```bash
196
+ pkill -f "lazy-gravity"
197
+ ```
198
+ 2. **Restart**
199
+ ```bash
200
+ lazy-gravity start
201
+ # or, from source: npm run start
202
+ ```
203
+
204
+ If Antigravity is restarted, the bot automatically attempts CDP reconnection. Sending a message triggers automatic project reconnection.
205
+
206
+ Run `lazy-gravity doctor` to diagnose configuration and connectivity issues.
207
+
208
+ ---
209
+
210
+ ## How CDP Connection Works
211
+
212
+ <p align="center">
213
+ <img src="https://raw.githubusercontent.com/tokyoweb3/LazyGravity/main/docs/images/architecture.svg" alt="LazyGravity Architecture" width="100%" />
214
+ </p>
215
+
216
+ 1. The bot scans debug ports (default: 9222) and auto-detects the Antigravity target
217
+ 2. Connects via WebSocket to CDP (`Runtime.evaluate` for DOM operations)
218
+ 3. Injects messages into the chat input, monitors Antigravity responses, and captures screenshots
219
+
220
+ **On disconnect**: automatically retries up to 3 times (`maxReconnectAttempts`). If all retries fail, an error notification is sent to Discord.
221
+
222
+ ## License
223
+
224
+ [MIT](LICENSE)
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const commander_1 = require("commander");
40
+ const package_json_1 = require("../../package.json");
41
+ const start_1 = require("./commands/start");
42
+ const doctor_1 = require("./commands/doctor");
43
+ const setup_1 = require("./commands/setup");
44
+ const open_1 = require("./commands/open");
45
+ const configLoader_1 = require("../utils/configLoader");
46
+ let commandRan = false;
47
+ const markRan = (fn) => ((...args) => { commandRan = true; return fn(...args); });
48
+ const program = new commander_1.Command()
49
+ .name('lazy-gravity')
50
+ .description('Control your AI coding assistant from Discord')
51
+ .version(package_json_1.version);
52
+ program
53
+ .command('start')
54
+ .description('Start the Discord bot')
55
+ .action(markRan(start_1.startAction));
56
+ program
57
+ .command('doctor')
58
+ .description('Check environment and dependencies')
59
+ .action(markRan(doctor_1.doctorAction));
60
+ program
61
+ .command('setup')
62
+ .description('Interactive setup wizard')
63
+ .action(markRan(setup_1.setupAction));
64
+ program
65
+ .command('open')
66
+ .description('Open Antigravity with CDP enabled (auto-selects available port)')
67
+ .action(markRan(open_1.openAction));
68
+ program.parse();
69
+ // Default behavior: if no subcommand was matched, decide what to run
70
+ if (!commandRan) {
71
+ const hasConfig = configLoader_1.ConfigLoader.configExists();
72
+ const hasEnv = fs.existsSync(path.resolve(process.cwd(), '.env'));
73
+ if (!hasConfig && !hasEnv) {
74
+ (0, setup_1.setupAction)();
75
+ }
76
+ else {
77
+ (0, start_1.startAction)();
78
+ }
79
+ }
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.doctorAction = doctorAction;
37
+ const http = __importStar(require("http"));
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const cdpPorts_1 = require("../../utils/cdpPorts");
41
+ const configLoader_1 = require("../../utils/configLoader");
42
+ function checkPort(port) {
43
+ return new Promise((resolve) => {
44
+ const req = http.get(`http://127.0.0.1:${port}/json/list`, (res) => {
45
+ let data = '';
46
+ res.on('data', (chunk) => (data += chunk));
47
+ res.on('end', () => {
48
+ try {
49
+ const parsed = JSON.parse(data);
50
+ resolve(Array.isArray(parsed));
51
+ }
52
+ catch {
53
+ resolve(false);
54
+ }
55
+ });
56
+ });
57
+ req.on('error', () => resolve(false));
58
+ req.setTimeout(2000, () => {
59
+ req.destroy();
60
+ resolve(false);
61
+ });
62
+ });
63
+ }
64
+ function checkEnvFile() {
65
+ const envPath = path.resolve(process.cwd(), '.env');
66
+ return { exists: fs.existsSync(envPath), path: envPath };
67
+ }
68
+ function checkRequiredEnvVars() {
69
+ const required = ['DISCORD_BOT_TOKEN', 'CLIENT_ID', 'ALLOWED_USER_IDS'];
70
+ return required.map((name) => ({
71
+ name,
72
+ set: Boolean(process.env[name]),
73
+ }));
74
+ }
75
+ async function doctorAction() {
76
+ console.log('lazy-gravity doctor\n');
77
+ let allOk = true;
78
+ // 1. Config directory check
79
+ const configDir = configLoader_1.ConfigLoader.getConfigDir();
80
+ if (fs.existsSync(configDir)) {
81
+ console.log(` [OK] Config directory exists: ${configDir}`);
82
+ }
83
+ else {
84
+ console.log(` [--] Config directory not found: ${configDir}`);
85
+ console.log(' Run: lazy-gravity setup (optional if using .env)');
86
+ }
87
+ // 2. Config file check
88
+ const configFilePath = configLoader_1.ConfigLoader.getConfigFilePath();
89
+ if (configLoader_1.ConfigLoader.configExists()) {
90
+ console.log(` [OK] Config file found: ${configFilePath}`);
91
+ }
92
+ else {
93
+ console.log(` [--] Config file not found: ${configFilePath} (optional — .env fallback used)`);
94
+ }
95
+ // 3. .env file check
96
+ const env = checkEnvFile();
97
+ if (env.exists) {
98
+ // Load .env so subsequent checks can see the variables
99
+ require('dotenv').config({ path: env.path });
100
+ console.log(` [OK] .env file found: ${env.path}`);
101
+ }
102
+ else {
103
+ if (!configLoader_1.ConfigLoader.configExists()) {
104
+ console.log(` [!!] .env file not found: ${env.path}`);
105
+ allOk = false;
106
+ }
107
+ else {
108
+ console.log(` [--] .env file not found: ${env.path} (not needed — config.json used)`);
109
+ }
110
+ }
111
+ // 4. Required environment variables (check both env and config.json sources)
112
+ const vars = checkRequiredEnvVars();
113
+ for (const v of vars) {
114
+ if (v.set) {
115
+ console.log(` [OK] ${v.name} is set`);
116
+ }
117
+ else {
118
+ console.log(` [!!] ${v.name} is NOT set`);
119
+ allOk = false;
120
+ }
121
+ }
122
+ // 5. CDP port check
123
+ console.log('\n Checking CDP ports...');
124
+ let cdpOk = false;
125
+ for (const port of cdpPorts_1.CDP_PORTS) {
126
+ const alive = await checkPort(port);
127
+ if (alive) {
128
+ console.log(` [OK] CDP port ${port} is responding`);
129
+ cdpOk = true;
130
+ }
131
+ }
132
+ if (!cdpOk) {
133
+ console.log(' [!!] No CDP ports responding');
134
+ console.log(' Run: open -a Antigravity --args --remote-debugging-port=9222');
135
+ allOk = false;
136
+ }
137
+ // 6. Node.js version check
138
+ const nodeVersion = process.versions.node;
139
+ const major = parseInt(nodeVersion.split('.')[0], 10);
140
+ if (major >= 18) {
141
+ console.log(`\n [OK] Node.js ${nodeVersion}`);
142
+ }
143
+ else {
144
+ console.log(`\n [!!] Node.js ${nodeVersion} (>= 18.0.0 required)`);
145
+ allOk = false;
146
+ }
147
+ // Summary
148
+ console.log('');
149
+ if (allOk) {
150
+ console.log(' All checks passed!');
151
+ }
152
+ else {
153
+ console.log(' Some checks failed. Please fix the issues above.');
154
+ process.exitCode = 1;
155
+ }
156
+ }
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.openAction = openAction;
37
+ const net = __importStar(require("net"));
38
+ const os = __importStar(require("os"));
39
+ const child_process_1 = require("child_process");
40
+ const cdpPorts_1 = require("../../utils/cdpPorts");
41
+ const APP_NAME = 'Antigravity';
42
+ const C = {
43
+ reset: '\x1b[0m',
44
+ bold: '\x1b[1m',
45
+ dim: '\x1b[2m',
46
+ cyan: '\x1b[36m',
47
+ green: '\x1b[32m',
48
+ red: '\x1b[31m',
49
+ };
50
+ /**
51
+ * Check whether a TCP port is available (not in use) by attempting to listen on it.
52
+ */
53
+ function isPortAvailable(port) {
54
+ return new Promise((resolve) => {
55
+ const server = net.createServer();
56
+ server.once('error', () => resolve(false));
57
+ server.once('listening', () => {
58
+ server.close(() => resolve(true));
59
+ });
60
+ server.listen(port, '127.0.0.1');
61
+ });
62
+ }
63
+ async function findAvailablePort() {
64
+ for (const port of cdpPorts_1.CDP_PORTS) {
65
+ if (await isPortAvailable(port)) {
66
+ return port;
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+ function openMacOS(port) {
72
+ return new Promise((resolve, reject) => {
73
+ (0, child_process_1.execFile)('open', ['-a', APP_NAME, '--args', `--remote-debugging-port=${port}`], (err) => {
74
+ if (err) {
75
+ reject(new Error(`Failed to open ${APP_NAME}: ${err.message}`));
76
+ return;
77
+ }
78
+ resolve();
79
+ });
80
+ });
81
+ }
82
+ function openWindows(port) {
83
+ return new Promise((resolve, reject) => {
84
+ (0, child_process_1.execFile)(`${APP_NAME}.exe`, [`--remote-debugging-port=${port}`], { shell: true }, (err) => {
85
+ if (err) {
86
+ reject(new Error(`Failed to open ${APP_NAME}: ${err.message}`));
87
+ return;
88
+ }
89
+ resolve();
90
+ });
91
+ });
92
+ }
93
+ function openLinux(port) {
94
+ return new Promise((resolve, reject) => {
95
+ try {
96
+ const child = (0, child_process_1.spawn)(APP_NAME.toLowerCase(), [`--remote-debugging-port=${port}`], {
97
+ detached: true,
98
+ stdio: 'ignore',
99
+ });
100
+ child.unref();
101
+ child.on('error', (err) => {
102
+ reject(new Error(`Failed to open ${APP_NAME}: ${err.message}`));
103
+ });
104
+ // Give it a moment to detect spawn errors
105
+ setTimeout(() => resolve(), 500);
106
+ }
107
+ catch (err) {
108
+ const msg = err instanceof Error ? err.message : String(err);
109
+ reject(new Error(`Failed to open ${APP_NAME}: ${msg}`));
110
+ }
111
+ });
112
+ }
113
+ async function openAction() {
114
+ const platform = os.platform();
115
+ console.log(`\n ${C.cyan}Searching for an available CDP port...${C.reset}`);
116
+ const port = await findAvailablePort();
117
+ if (port === null) {
118
+ console.log(` ${C.red}No available CDP ports found.${C.reset}`);
119
+ console.log(` ${C.dim}All candidate ports are in use: ${cdpPorts_1.CDP_PORTS.join(', ')}${C.reset}`);
120
+ console.log(` ${C.dim}Close an application using one of these ports and try again.${C.reset}\n`);
121
+ process.exitCode = 1;
122
+ return;
123
+ }
124
+ console.log(` ${C.green}Found available port: ${port}${C.reset}`);
125
+ console.log(` ${C.dim}Opening ${APP_NAME} with --remote-debugging-port=${port}...${C.reset}\n`);
126
+ try {
127
+ if (platform === 'darwin') {
128
+ await openMacOS(port);
129
+ }
130
+ else if (platform === 'win32') {
131
+ await openWindows(port);
132
+ }
133
+ else {
134
+ await openLinux(port);
135
+ }
136
+ console.log(` ${C.green}${APP_NAME} opened on CDP port ${port}${C.reset}`);
137
+ console.log(` ${C.dim}Run ${C.reset}${C.cyan}lazy-gravity start${C.reset}${C.dim} to connect the bot.${C.reset}\n`);
138
+ }
139
+ catch (error) {
140
+ const msg = error instanceof Error ? error.message : String(error);
141
+ console.log(` ${C.red}${msg}${C.reset}`);
142
+ console.log(` ${C.dim}Make sure ${APP_NAME} is installed on your system.${C.reset}\n`);
143
+ process.exitCode = 1;
144
+ }
145
+ }