lazy-gravity 0.0.2 → 0.0.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.
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
@@ -0,0 +1,366 @@
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.setupAction = setupAction;
37
+ const readline = __importStar(require("readline"));
38
+ const https = __importStar(require("https"));
39
+ const fs = __importStar(require("fs"));
40
+ const os = __importStar(require("os"));
41
+ const path = __importStar(require("path"));
42
+ const configLoader_1 = require("../../utils/configLoader");
43
+ const cdpPorts_1 = require("../../utils/cdpPorts");
44
+ // ---------------------------------------------------------------------------
45
+ // ANSI colors
46
+ // ---------------------------------------------------------------------------
47
+ const C = {
48
+ reset: '\x1b[0m',
49
+ bold: '\x1b[1m',
50
+ dim: '\x1b[2m',
51
+ cyan: '\x1b[36m',
52
+ green: '\x1b[32m',
53
+ yellow: '\x1b[33m',
54
+ red: '\x1b[31m',
55
+ magenta: '\x1b[35m',
56
+ };
57
+ const SETUP_LOGO = `
58
+ ${C.cyan} . * .${C.reset}
59
+ ${C.cyan} /\\___/\\ z Z${C.reset}
60
+ ${C.cyan} * ( - . - )____________z${C.reset} *
61
+ ${C.cyan} \\_ __)${C.reset}
62
+ ${C.cyan} \\_ \\________/ /${C.reset} .
63
+ ${C.cyan} \\__) \\__)${C.reset}
64
+
65
+ ${C.bold}~ LazyGravity Setup ~${C.reset}
66
+ `;
67
+ // ---------------------------------------------------------------------------
68
+ // Pure validators
69
+ // ---------------------------------------------------------------------------
70
+ function isNonEmpty(value) {
71
+ return value.trim().length > 0;
72
+ }
73
+ function isNumericString(value) {
74
+ return /^\d+$/.test(value.trim());
75
+ }
76
+ function parseAllowedUserIds(raw) {
77
+ return raw
78
+ .split(',')
79
+ .map((id) => id.trim())
80
+ .filter((id) => id.length > 0);
81
+ }
82
+ function validateAllowedUserIds(raw) {
83
+ const ids = parseAllowedUserIds(raw);
84
+ if (ids.length === 0) {
85
+ return 'Please enter at least one user ID.';
86
+ }
87
+ const invalid = ids.find((id) => !isNumericString(id));
88
+ if (invalid) {
89
+ return `Invalid user ID: "${invalid}" — must be a numeric string.`;
90
+ }
91
+ return null;
92
+ }
93
+ function expandTilde(raw) {
94
+ if (raw === '~')
95
+ return os.homedir();
96
+ if (raw.startsWith('~/'))
97
+ return path.join(os.homedir(), raw.slice(2));
98
+ return raw;
99
+ }
100
+ /**
101
+ * Extract Bot ID from a Discord token.
102
+ * Token format: base64(bot_id).timestamp.hmac
103
+ */
104
+ function extractBotIdFromToken(token) {
105
+ const parts = token.split('.');
106
+ if (parts.length !== 3)
107
+ return null;
108
+ try {
109
+ const decoded = Buffer.from(parts[0], 'base64').toString('utf-8');
110
+ return isNumericString(decoded) ? decoded : null;
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ }
116
+ /**
117
+ * Verify a Discord bot token via GET /users/@me and return bot info.
118
+ */
119
+ function verifyToken(token) {
120
+ return new Promise((resolve) => {
121
+ const req = https.get('https://discord.com/api/v10/users/@me', {
122
+ headers: { Authorization: `Bot ${token}` },
123
+ }, (res) => {
124
+ let data = '';
125
+ res.on('data', (chunk) => (data += chunk));
126
+ res.on('end', () => {
127
+ if (res.statusCode !== 200) {
128
+ resolve(null);
129
+ return;
130
+ }
131
+ try {
132
+ const json = JSON.parse(data);
133
+ resolve({ id: json.id, username: json.username });
134
+ }
135
+ catch {
136
+ resolve(null);
137
+ }
138
+ });
139
+ });
140
+ req.on('error', () => resolve(null));
141
+ req.setTimeout(10000, () => {
142
+ req.destroy();
143
+ resolve(null);
144
+ });
145
+ });
146
+ }
147
+ // ---------------------------------------------------------------------------
148
+ // Readline helpers
149
+ // ---------------------------------------------------------------------------
150
+ function createInterface() {
151
+ return readline.createInterface({
152
+ input: process.stdin,
153
+ output: process.stdout,
154
+ });
155
+ }
156
+ function ask(rl, prompt) {
157
+ return new Promise((resolve) => {
158
+ rl.question(prompt, (answer) => {
159
+ resolve(answer);
160
+ });
161
+ });
162
+ }
163
+ /**
164
+ * Read a secret value without echoing to the terminal.
165
+ * Falls back to normal readline if raw mode is unavailable (e.g. piped stdin).
166
+ */
167
+ function askSecret(rl, prompt) {
168
+ return new Promise((resolve) => {
169
+ if (!process.stdin.isTTY) {
170
+ rl.question(prompt, resolve);
171
+ return;
172
+ }
173
+ process.stdout.write(prompt);
174
+ rl.pause();
175
+ const stdin = process.stdin;
176
+ stdin.setRawMode(true);
177
+ stdin.resume();
178
+ stdin.setEncoding('utf8');
179
+ let input = '';
180
+ const onData = (char) => {
181
+ const code = char.charCodeAt(0);
182
+ if (char === '\r' || char === '\n') {
183
+ stdin.setRawMode(false);
184
+ stdin.removeListener('data', onData);
185
+ process.stdout.write('\n');
186
+ rl.resume();
187
+ resolve(input);
188
+ }
189
+ else if (code === 127 || code === 8) {
190
+ if (input.length > 0) {
191
+ input = input.slice(0, -1);
192
+ process.stdout.write('\b \b');
193
+ }
194
+ }
195
+ else if (code === 3) {
196
+ stdin.setRawMode(false);
197
+ process.stdout.write('\n');
198
+ process.exit(0);
199
+ }
200
+ else if (code >= 32) {
201
+ input += char;
202
+ process.stdout.write('*');
203
+ }
204
+ };
205
+ stdin.on('data', onData);
206
+ });
207
+ }
208
+ // ---------------------------------------------------------------------------
209
+ // Output helpers
210
+ // ---------------------------------------------------------------------------
211
+ function stepHeader(step, total, title) {
212
+ console.log(` ${C.cyan}[Step ${step}/${total}]${C.reset} ${C.bold}${title}${C.reset}`);
213
+ }
214
+ function hint(text) {
215
+ console.log(` ${C.dim}${text}${C.reset}`);
216
+ }
217
+ function hintBlank() {
218
+ console.log('');
219
+ }
220
+ function errMsg(text) {
221
+ console.log(` ${C.red}${text}${C.reset}\n`);
222
+ }
223
+ function buildInviteUrl(clientId) {
224
+ const permissions = '2147485696';
225
+ return `https://discord.com/api/oauth2/authorize?client_id=${clientId}&permissions=${permissions}&scope=bot%20applications.commands`;
226
+ }
227
+ // ---------------------------------------------------------------------------
228
+ // Setup steps
229
+ // ---------------------------------------------------------------------------
230
+ const TOTAL_STEPS = 4;
231
+ async function promptToken(rl) {
232
+ while (true) {
233
+ const token = await askSecret(rl, ` ${C.yellow}>${C.reset} `);
234
+ if (!isNonEmpty(token)) {
235
+ errMsg('Token cannot be empty. Please try again.');
236
+ continue;
237
+ }
238
+ const trimmed = token.trim();
239
+ // Extract Client ID from token
240
+ const clientId = extractBotIdFromToken(trimmed);
241
+ if (!clientId) {
242
+ errMsg('Invalid token format. A Discord bot token has 3 dot-separated segments.');
243
+ continue;
244
+ }
245
+ // Verify token against Discord API
246
+ process.stdout.write(` ${C.dim}Verifying token...${C.reset}`);
247
+ const botInfo = await verifyToken(trimmed);
248
+ if (botInfo) {
249
+ process.stdout.write(`\r ${C.green}Verified!${C.reset} Bot: ${C.bold}${botInfo.username}${C.reset} (${botInfo.id})\n`);
250
+ return { token: trimmed, clientId: botInfo.id, botName: botInfo.username };
251
+ }
252
+ // API failed but token format is valid — use extracted ID
253
+ process.stdout.write(`\r ${C.yellow}Could not verify online${C.reset} — using extracted ID: ${clientId}\n`);
254
+ return { token: trimmed, clientId, botName: null };
255
+ }
256
+ }
257
+ async function promptGuildId(rl) {
258
+ const raw = await ask(rl, ` ${C.yellow}>${C.reset} `);
259
+ const trimmed = raw.trim();
260
+ if (!trimmed)
261
+ return undefined;
262
+ if (isNumericString(trimmed))
263
+ return trimmed;
264
+ errMsg('Guild ID must be a numeric string. Skipping.');
265
+ return undefined;
266
+ }
267
+ async function promptAllowedUserIds(rl) {
268
+ while (true) {
269
+ const raw = await ask(rl, ` ${C.yellow}>${C.reset} `);
270
+ const error = validateAllowedUserIds(raw);
271
+ if (error === null) {
272
+ return parseAllowedUserIds(raw);
273
+ }
274
+ errMsg(`${error}`);
275
+ }
276
+ }
277
+ async function promptWorkspaceDir(rl) {
278
+ const defaultDir = path.join(os.homedir(), 'Code');
279
+ while (true) {
280
+ const raw = await ask(rl, ` ${C.yellow}>${C.reset} [${C.dim}${defaultDir}${C.reset}] `);
281
+ const dir = expandTilde(raw.trim().length > 0 ? raw.trim() : defaultDir);
282
+ const resolved = path.resolve(dir);
283
+ if (fs.existsSync(resolved)) {
284
+ return resolved;
285
+ }
286
+ const answer = await ask(rl, ` ${C.yellow}"${resolved}" does not exist. Create it? (y/n):${C.reset} `);
287
+ if (answer.trim().toLowerCase() === 'y') {
288
+ fs.mkdirSync(resolved, { recursive: true });
289
+ return resolved;
290
+ }
291
+ errMsg('Please enter an existing directory.');
292
+ }
293
+ }
294
+ async function runSetupWizard() {
295
+ const rl = createInterface();
296
+ try {
297
+ console.log(SETUP_LOGO);
298
+ console.log(` ${C.bold}Interactive setup — ${TOTAL_STEPS} steps${C.reset}\n`);
299
+ stepHeader(1, TOTAL_STEPS, 'Discord Bot Token');
300
+ hint('1. Go to https://discord.com/developers/applications and log in');
301
+ hint('2. Click "New Application" (top-right), enter a name (e.g. LazyGravity), and create it');
302
+ hint('3. Go to the "Bot" tab on the left sidebar');
303
+ hint('4. Click "Reset Token" to generate and copy the token');
304
+ hint(`5. Scroll down to ${C.bold}"Privileged Gateway Intents"${C.dim} and enable ALL of:`);
305
+ hint(` ${C.cyan}PRESENCE INTENT${C.dim}`);
306
+ hint(` ${C.cyan}SERVER MEMBERS INTENT${C.dim}`);
307
+ hint(` ${C.cyan}MESSAGE CONTENT INTENT${C.dim} ${C.yellow}(required — bot cannot read messages without this)${C.dim}`);
308
+ hint(`6. Click ${C.bold}"Save Changes"${C.dim} at the bottom (Warning banner)`);
309
+ hintBlank();
310
+ const { token: discordToken, clientId } = await promptToken(rl);
311
+ console.log('');
312
+ stepHeader(2, TOTAL_STEPS, 'Guild (Server) ID');
313
+ hint('This registers slash commands instantly to your server.');
314
+ hint('1. Open Discord Settings > Advanced > enable "Developer Mode"');
315
+ hint('2. Right-click your server icon > "Copy Server ID"');
316
+ hint(`${C.yellow}Press Enter to skip${C.dim} (commands will register globally, may take ~1 hour)`);
317
+ hintBlank();
318
+ const guildId = await promptGuildId(rl);
319
+ console.log('');
320
+ stepHeader(3, TOTAL_STEPS, 'Allowed Discord User IDs');
321
+ hint('Only these users can send commands to the bot.');
322
+ hint('1. In Discord, right-click your own profile icon');
323
+ hint('2. Click "Copy User ID" (requires Developer Mode from step 2)');
324
+ hint('Multiple IDs: separate with commas (e.g. 123456,789012)');
325
+ hintBlank();
326
+ const allowedUserIds = await promptAllowedUserIds(rl);
327
+ console.log('');
328
+ stepHeader(4, TOTAL_STEPS, 'Workspace Base Directory');
329
+ hint('The parent directory where your coding projects live.');
330
+ hint('LazyGravity will scan subdirectories as workspaces.');
331
+ hintBlank();
332
+ const workspaceBaseDir = await promptWorkspaceDir(rl);
333
+ console.log('');
334
+ return { discordToken, clientId, guildId, allowedUserIds, workspaceBaseDir };
335
+ }
336
+ finally {
337
+ rl.close();
338
+ }
339
+ }
340
+ // ---------------------------------------------------------------------------
341
+ // Public action
342
+ // ---------------------------------------------------------------------------
343
+ async function setupAction() {
344
+ const result = await runSetupWizard();
345
+ configLoader_1.ConfigLoader.save({
346
+ discordToken: result.discordToken,
347
+ clientId: result.clientId,
348
+ guildId: result.guildId,
349
+ allowedUserIds: result.allowedUserIds,
350
+ workspaceBaseDir: result.workspaceBaseDir,
351
+ });
352
+ const configPath = configLoader_1.ConfigLoader.getConfigFilePath();
353
+ const inviteUrl = buildInviteUrl(result.clientId);
354
+ console.log(` ${C.green}Setup complete!${C.reset}\n`);
355
+ console.log(` ${C.dim}Saved to${C.reset} ${configPath}\n`);
356
+ console.log(` ${C.cyan}Next steps:${C.reset}`);
357
+ console.log(` ${C.bold}1.${C.reset} ${C.yellow}Verify Privileged Gateway Intents are enabled${C.reset} in the Bot tab:`);
358
+ console.log(` ${C.dim}Required: PRESENCE INTENT, SERVER MEMBERS INTENT, MESSAGE CONTENT INTENT${C.reset}`);
359
+ console.log(` https://discord.com/developers/applications/${result.clientId}/bot\n`);
360
+ console.log(` ${C.bold}2.${C.reset} Add the bot to your server:`);
361
+ console.log(` ${inviteUrl}\n`);
362
+ console.log(` ${C.bold}3.${C.reset} Open Antigravity with CDP enabled:`);
363
+ console.log(` ${C.green}lazy-gravity open${C.reset}`);
364
+ console.log(` ${C.dim}(auto-selects an available port from: ${cdpPorts_1.CDP_PORTS.join(', ')})${C.reset}\n`);
365
+ console.log(` ${C.bold}4.${C.reset} Run: ${C.green}lazy-gravity start${C.reset}\n`);
366
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startAction = startAction;
4
+ const logo_1 = require("../../utils/logo");
5
+ const lockfile_1 = require("../../utils/lockfile");
6
+ const bot_1 = require("../../bot");
7
+ const logger_1 = require("../../utils/logger");
8
+ async function startAction() {
9
+ console.log(logo_1.LOGO);
10
+ (0, lockfile_1.acquireLock)();
11
+ await (0, bot_1.startBot)().catch((err) => {
12
+ logger_1.logger.error('Failed to start bot:', err);
13
+ process.exit(1);
14
+ });
15
+ }