codebakers 1.0.45 → 2.0.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 (81) hide show
  1. package/README.md +275 -60
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +3999 -0
  4. package/install.bat +9 -0
  5. package/package.json +71 -115
  6. package/src/channels/discord.ts +5 -0
  7. package/src/channels/slack.ts +5 -0
  8. package/src/channels/sms.ts +4 -0
  9. package/src/channels/telegram.ts +5 -0
  10. package/src/channels/whatsapp.ts +7 -0
  11. package/src/commands/check.ts +365 -0
  12. package/src/commands/code.ts +684 -0
  13. package/src/commands/connect.ts +12 -0
  14. package/src/commands/deploy.ts +414 -0
  15. package/src/commands/fix.ts +20 -0
  16. package/src/commands/gateway.ts +604 -0
  17. package/src/commands/generate.ts +178 -0
  18. package/src/commands/init.ts +574 -0
  19. package/src/commands/learn.ts +36 -0
  20. package/src/commands/security.ts +102 -0
  21. package/src/commands/setup.ts +448 -0
  22. package/src/commands/status.ts +56 -0
  23. package/src/index.ts +268 -0
  24. package/src/patterns/loader.ts +337 -0
  25. package/src/services/github.ts +61 -0
  26. package/src/services/supabase.ts +147 -0
  27. package/src/services/vercel.ts +61 -0
  28. package/src/utils/claude-md.ts +287 -0
  29. package/src/utils/config.ts +282 -0
  30. package/src/utils/updates.ts +27 -0
  31. package/tsconfig.json +17 -10
  32. package/.vscodeignore +0 -18
  33. package/LICENSE +0 -21
  34. package/codebakers-1.0.0.vsix +0 -0
  35. package/codebakers-1.0.10.vsix +0 -0
  36. package/codebakers-1.0.11.vsix +0 -0
  37. package/codebakers-1.0.12.vsix +0 -0
  38. package/codebakers-1.0.13.vsix +0 -0
  39. package/codebakers-1.0.14.vsix +0 -0
  40. package/codebakers-1.0.15.vsix +0 -0
  41. package/codebakers-1.0.16.vsix +0 -0
  42. package/codebakers-1.0.17.vsix +0 -0
  43. package/codebakers-1.0.18.vsix +0 -0
  44. package/codebakers-1.0.19.vsix +0 -0
  45. package/codebakers-1.0.20.vsix +0 -0
  46. package/codebakers-1.0.21.vsix +0 -0
  47. package/codebakers-1.0.22.vsix +0 -0
  48. package/codebakers-1.0.23.vsix +0 -0
  49. package/codebakers-1.0.24.vsix +0 -0
  50. package/codebakers-1.0.25.vsix +0 -0
  51. package/codebakers-1.0.26.vsix +0 -0
  52. package/codebakers-1.0.27.vsix +0 -0
  53. package/codebakers-1.0.28.vsix +0 -0
  54. package/codebakers-1.0.29.vsix +0 -0
  55. package/codebakers-1.0.30.vsix +0 -0
  56. package/codebakers-1.0.31.vsix +0 -0
  57. package/codebakers-1.0.32.vsix +0 -0
  58. package/codebakers-1.0.35.vsix +0 -0
  59. package/codebakers-1.0.36.vsix +0 -0
  60. package/codebakers-1.0.37.vsix +0 -0
  61. package/codebakers-1.0.38.vsix +0 -0
  62. package/codebakers-1.0.39.vsix +0 -0
  63. package/codebakers-1.0.40.vsix +0 -0
  64. package/codebakers-1.0.41.vsix +0 -0
  65. package/codebakers-1.0.42.vsix +0 -0
  66. package/codebakers-1.0.43.vsix +0 -0
  67. package/codebakers-1.0.44.vsix +0 -0
  68. package/codebakers-1.0.45.vsix +0 -0
  69. package/dist/extension.js +0 -1394
  70. package/esbuild.js +0 -63
  71. package/media/icon.png +0 -0
  72. package/media/icon.svg +0 -7
  73. package/nul +0 -1
  74. package/preview.html +0 -547
  75. package/src/ChatPanelProvider.ts +0 -1815
  76. package/src/ChatViewProvider.ts +0 -749
  77. package/src/CodeBakersClient.ts +0 -1146
  78. package/src/CodeValidator.ts +0 -645
  79. package/src/FileOperations.ts +0 -410
  80. package/src/ProjectContext.ts +0 -526
  81. package/src/extension.ts +0 -332
@@ -0,0 +1,604 @@
1
+ import * as p from '@clack/prompts';
2
+ import chalk from 'chalk';
3
+ import { Config } from '../utils/config.js';
4
+ import { WhatsAppChannel } from '../channels/whatsapp.js';
5
+ import { TelegramChannel } from '../channels/telegram.js';
6
+ import { DiscordChannel } from '../channels/discord.js';
7
+ import { SlackChannel } from '../channels/slack.js';
8
+ import { SMSChannel } from '../channels/sms.js';
9
+
10
+ interface GatewayOptions {
11
+ start?: boolean;
12
+ stop?: boolean;
13
+ status?: boolean;
14
+ }
15
+
16
+ interface ChannelInfo {
17
+ id: string;
18
+ name: string;
19
+ description: string;
20
+ icon: string;
21
+ connected: boolean;
22
+ status: 'running' | 'stopped' | 'error' | 'connecting';
23
+ }
24
+
25
+ export async function gatewayCommand(options: GatewayOptions = {}): Promise<void> {
26
+ const config = new Config();
27
+
28
+ p.intro(chalk.bgCyan.black(' Channel Gateway '));
29
+
30
+ // Quick actions
31
+ if (options.start) {
32
+ await startAllChannels(config);
33
+ return;
34
+ }
35
+
36
+ if (options.stop) {
37
+ await stopAllChannels(config);
38
+ return;
39
+ }
40
+
41
+ if (options.status) {
42
+ await showGatewayStatus(config);
43
+ return;
44
+ }
45
+
46
+ // Interactive menu
47
+ const action = await p.select({
48
+ message: 'What do you want to do?',
49
+ options: [
50
+ { value: 'status', label: '📊 View status', hint: 'see all channels' },
51
+ { value: 'connect', label: '🔗 Connect channel', hint: 'add WhatsApp, Telegram, etc.' },
52
+ { value: 'start', label: '▶️ Start gateway', hint: 'start receiving messages' },
53
+ { value: 'stop', label: '⏹️ Stop gateway', hint: 'pause all channels' },
54
+ { value: 'deploy', label: '🚀 Deploy to cloud', hint: 'always-on hosting' },
55
+ { value: 'back', label: '← Back' },
56
+ ],
57
+ });
58
+
59
+ if (p.isCancel(action) || action === 'back') {
60
+ return;
61
+ }
62
+
63
+ switch (action) {
64
+ case 'status':
65
+ await showGatewayStatus(config);
66
+ break;
67
+ case 'connect':
68
+ await connectChannelWizard(config);
69
+ break;
70
+ case 'start':
71
+ await startAllChannels(config);
72
+ break;
73
+ case 'stop':
74
+ await stopAllChannels(config);
75
+ break;
76
+ case 'deploy':
77
+ await deployGatewayWizard(config);
78
+ break;
79
+ }
80
+ }
81
+
82
+ async function showGatewayStatus(config: Config): Promise<void> {
83
+ const channels = await getChannelStatuses(config);
84
+
85
+ console.log(chalk.bold('\n📱 Channel Gateway Status\n'));
86
+
87
+ for (const channel of channels) {
88
+ const statusIcon = {
89
+ running: chalk.green('●'),
90
+ stopped: chalk.gray('○'),
91
+ error: chalk.red('✗'),
92
+ connecting: chalk.yellow('◐'),
93
+ }[channel.status];
94
+
95
+ const statusText = {
96
+ running: chalk.green('Running'),
97
+ stopped: chalk.gray('Stopped'),
98
+ error: chalk.red('Error'),
99
+ connecting: chalk.yellow('Connecting...'),
100
+ }[channel.status];
101
+
102
+ console.log(` ${channel.icon} ${channel.name.padEnd(15)} ${statusIcon} ${statusText}`);
103
+ }
104
+
105
+ console.log('');
106
+ }
107
+
108
+ async function getChannelStatuses(config: Config): Promise<ChannelInfo[]> {
109
+ const channels: ChannelInfo[] = [
110
+ {
111
+ id: 'whatsapp',
112
+ name: 'WhatsApp',
113
+ description: 'WhatsApp via Baileys',
114
+ icon: '💬',
115
+ connected: !!config.getChannelConfig('whatsapp')?.enabled,
116
+ status: config.getChannelConfig('whatsapp')?.enabled ? 'stopped' : 'stopped',
117
+ },
118
+ {
119
+ id: 'telegram',
120
+ name: 'Telegram',
121
+ description: 'Telegram Bot API',
122
+ icon: '✈️',
123
+ connected: !!config.getChannelConfig('telegram')?.enabled,
124
+ status: config.getChannelConfig('telegram')?.enabled ? 'stopped' : 'stopped',
125
+ },
126
+ {
127
+ id: 'discord',
128
+ name: 'Discord',
129
+ description: 'Discord.js bot',
130
+ icon: '🎮',
131
+ connected: !!config.getChannelConfig('discord')?.enabled,
132
+ status: config.getChannelConfig('discord')?.enabled ? 'stopped' : 'stopped',
133
+ },
134
+ {
135
+ id: 'slack',
136
+ name: 'Slack',
137
+ description: 'Slack Bot API',
138
+ icon: '💼',
139
+ connected: !!config.getChannelConfig('slack')?.enabled,
140
+ status: config.getChannelConfig('slack')?.enabled ? 'stopped' : 'stopped',
141
+ },
142
+ {
143
+ id: 'sms',
144
+ name: 'SMS',
145
+ description: 'Twilio SMS',
146
+ icon: '📱',
147
+ connected: !!config.getCredentials('twilio')?.accountSid,
148
+ status: config.getCredentials('twilio')?.accountSid ? 'stopped' : 'stopped',
149
+ },
150
+ ];
151
+
152
+ return channels;
153
+ }
154
+
155
+ async function connectChannelWizard(config: Config): Promise<void> {
156
+ const channel = await p.select({
157
+ message: 'Which channel do you want to connect?',
158
+ options: [
159
+ { value: 'whatsapp', label: '💬 WhatsApp', hint: 'Personal or Business' },
160
+ { value: 'telegram', label: '✈️ Telegram', hint: 'Create a bot' },
161
+ { value: 'discord', label: '🎮 Discord', hint: 'Server bot' },
162
+ { value: 'slack', label: '💼 Slack', hint: 'Workspace app' },
163
+ { value: 'sms', label: '📱 SMS', hint: 'via Twilio' },
164
+ { value: 'imessage', label: '🍎 iMessage', hint: 'Mac only' },
165
+ ],
166
+ });
167
+
168
+ if (p.isCancel(channel)) return;
169
+
170
+ switch (channel) {
171
+ case 'whatsapp':
172
+ await connectWhatsApp(config);
173
+ break;
174
+ case 'telegram':
175
+ await connectTelegram(config);
176
+ break;
177
+ case 'discord':
178
+ await connectDiscord(config);
179
+ break;
180
+ case 'slack':
181
+ await connectSlack(config);
182
+ break;
183
+ case 'sms':
184
+ await connectSMS(config);
185
+ break;
186
+ case 'imessage':
187
+ await connectiMessage(config);
188
+ break;
189
+ }
190
+ }
191
+
192
+ async function connectWhatsApp(config: Config): Promise<void> {
193
+ p.log.info(chalk.bold('WhatsApp Setup'));
194
+ p.log.info(chalk.dim(`
195
+ WhatsApp uses QR code authentication via Baileys library.
196
+ Your phone needs to scan a QR code to connect.
197
+
198
+ Note: This is for personal accounts. For WhatsApp Business API,
199
+ you'll need a Meta Business account.
200
+ `));
201
+
202
+ const proceed = await p.confirm({
203
+ message: 'Ready to scan QR code?',
204
+ initialValue: true,
205
+ });
206
+
207
+ if (!proceed || p.isCancel(proceed)) return;
208
+
209
+ const spinner = p.spinner();
210
+ spinner.start('Generating QR code...');
211
+
212
+ try {
213
+ // In real implementation, this would:
214
+ // 1. Initialize Baileys
215
+ // 2. Generate QR code
216
+ // 3. Wait for scan
217
+ // 4. Save session
218
+
219
+ // Simulated QR code display
220
+ spinner.stop('');
221
+
222
+ console.log(chalk.cyan(`
223
+ ╔════════════════════════════════════╗
224
+ ║ ║
225
+ ║ ▄▄▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄ ║
226
+ ║ █ ▄▄▄ █ ▀█▀▄█ █ ▄▄▄ █ ║
227
+ ║ █ ███ █ ▄▀ ▀▄ █ ███ █ ║
228
+ ║ █▄▄▄▄▄█ █ ▄▀█ █▄▄▄▄▄█ ║
229
+ ║ ▄▄▄▄▄ ▄▄▄██▀▀▄ ▄ ▄ ▄ ║
230
+ ║ ██▀▀▀▄▄▀▄▀▄▄█▀██▀▄█▄▄ ║
231
+ ║ ▄▄▄▄▄▄▄ █▄▀▄▀ ▄██▄█▄ ║
232
+ ║ █ ▄▄▄ █ █▀▀▄▄▄▄▀▄ ║
233
+ ║ █ ███ █ ▄███▄█▀█▄▄▄█ ║
234
+ ║ █▄▄▄▄▄█ ██▀▄▀▀▄█▄▀█ ║
235
+ ║ ║
236
+ ╚════════════════════════════════════╝
237
+
238
+ Scan this QR code with WhatsApp:
239
+ 1. Open WhatsApp on your phone
240
+ 2. Go to Settings > Linked Devices
241
+ 3. Tap "Link a Device"
242
+ 4. Scan the QR code above
243
+ `));
244
+
245
+ const connected = await p.confirm({
246
+ message: 'Did you scan the QR code successfully?',
247
+ initialValue: false,
248
+ });
249
+
250
+ if (connected && !p.isCancel(connected)) {
251
+ config.setChannelConfig('whatsapp', { enabled: true });
252
+ p.log.success('WhatsApp connected!');
253
+ }
254
+ } catch (error) {
255
+ spinner.stop('Error connecting WhatsApp');
256
+ p.log.error(error instanceof Error ? error.message : 'Unknown error');
257
+ }
258
+ }
259
+
260
+ async function connectTelegram(config: Config): Promise<void> {
261
+ p.log.info(chalk.bold('Telegram Bot Setup'));
262
+ p.log.info(chalk.dim(`
263
+ To create a Telegram bot:
264
+ 1. Open Telegram and search for @BotFather
265
+ 2. Send /newbot
266
+ 3. Follow the prompts to name your bot
267
+ 4. Copy the API token
268
+ `));
269
+
270
+ const openBotFather = await p.confirm({
271
+ message: 'Open BotFather in browser?',
272
+ initialValue: true,
273
+ });
274
+
275
+ if (openBotFather && !p.isCancel(openBotFather)) {
276
+ const open = (await import('open')).default;
277
+ await open('https://t.me/botfather');
278
+ }
279
+
280
+ const token = await p.text({
281
+ message: 'Paste your bot token:',
282
+ placeholder: '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11',
283
+ validate: (value) => {
284
+ if (!value) return 'Token is required';
285
+ if (!value.includes(':')) return 'Invalid token format';
286
+ return undefined;
287
+ },
288
+ });
289
+
290
+ if (p.isCancel(token)) return;
291
+
292
+ const spinner = p.spinner();
293
+ spinner.start('Verifying bot token...');
294
+
295
+ try {
296
+ // Verify token by calling getMe
297
+ const response = await fetch(`https://api.telegram.org/bot${token}/getMe`);
298
+ const data = await response.json();
299
+
300
+ if (!data.ok) {
301
+ throw new Error(data.description || 'Invalid token');
302
+ }
303
+
304
+ spinner.stop('Bot verified!');
305
+
306
+ config.setChannelConfig('telegram', {
307
+ enabled: true,
308
+ botToken: token,
309
+ botUsername: data.result.username,
310
+ });
311
+
312
+ p.log.success(`Connected to @${data.result.username}`);
313
+ } catch (error) {
314
+ spinner.stop('Verification failed');
315
+ p.log.error(error instanceof Error ? error.message : 'Invalid token');
316
+ }
317
+ }
318
+
319
+ async function connectDiscord(config: Config): Promise<void> {
320
+ p.log.info(chalk.bold('Discord Bot Setup'));
321
+ p.log.info(chalk.dim(`
322
+ To create a Discord bot:
323
+ 1. Go to Discord Developer Portal
324
+ 2. Create a new application
325
+ 3. Go to Bot section
326
+ 4. Create a bot and copy the token
327
+ 5. Enable necessary intents (Message Content)
328
+ `));
329
+
330
+ const openPortal = await p.confirm({
331
+ message: 'Open Discord Developer Portal?',
332
+ initialValue: true,
333
+ });
334
+
335
+ if (openPortal && !p.isCancel(openPortal)) {
336
+ const open = (await import('open')).default;
337
+ await open('https://discord.com/developers/applications');
338
+ }
339
+
340
+ const token = await p.text({
341
+ message: 'Paste your bot token:',
342
+ placeholder: 'MTIzNDU2Nzg5MDEyMzQ1Njc4.Gg1234.abc...',
343
+ });
344
+
345
+ if (p.isCancel(token) || !token) return;
346
+
347
+ const clientId = await p.text({
348
+ message: 'Paste your application (client) ID:',
349
+ placeholder: '123456789012345678',
350
+ });
351
+
352
+ if (p.isCancel(clientId) || !clientId) return;
353
+
354
+ config.setChannelConfig('discord', {
355
+ enabled: true,
356
+ botToken: token,
357
+ clientId: clientId,
358
+ });
359
+
360
+ // Generate invite URL
361
+ const inviteUrl = `https://discord.com/api/oauth2/authorize?client_id=${clientId}&permissions=2048&scope=bot`;
362
+
363
+ p.log.success('Discord bot configured!');
364
+ p.log.info(`Add bot to server: ${inviteUrl}`);
365
+ }
366
+
367
+ async function connectSlack(config: Config): Promise<void> {
368
+ p.log.info(chalk.bold('Slack App Setup'));
369
+ p.log.info(chalk.dim(`
370
+ To create a Slack app:
371
+ 1. Go to Slack API portal
372
+ 2. Create a new app
373
+ 3. Add bot scopes (chat:write, app_mentions:read, etc.)
374
+ 4. Install to workspace
375
+ 5. Copy the Bot Token
376
+ `));
377
+
378
+ const openSlack = await p.confirm({
379
+ message: 'Open Slack API portal?',
380
+ initialValue: true,
381
+ });
382
+
383
+ if (openSlack && !p.isCancel(openSlack)) {
384
+ const open = (await import('open')).default;
385
+ await open('https://api.slack.com/apps');
386
+ }
387
+
388
+ const token = await p.text({
389
+ message: 'Paste your Bot User OAuth Token:',
390
+ placeholder: 'xoxb-...',
391
+ validate: (value) => {
392
+ if (!value) return 'Token is required';
393
+ if (!value.startsWith('xoxb-')) return 'Should start with xoxb-';
394
+ return undefined;
395
+ },
396
+ });
397
+
398
+ if (p.isCancel(token)) return;
399
+
400
+ config.setChannelConfig('slack', {
401
+ enabled: true,
402
+ botToken: token,
403
+ });
404
+
405
+ p.log.success('Slack app configured!');
406
+ }
407
+
408
+ async function connectSMS(config: Config): Promise<void> {
409
+ p.log.info(chalk.bold('SMS via Twilio'));
410
+
411
+ const twilioConfig = config.getCredentials('twilio');
412
+
413
+ if (twilioConfig?.accountSid) {
414
+ p.log.info('Twilio is already configured.');
415
+
416
+ const reconfigure = await p.confirm({
417
+ message: 'Reconfigure Twilio?',
418
+ initialValue: false,
419
+ });
420
+
421
+ if (!reconfigure || p.isCancel(reconfigure)) {
422
+ config.setChannelConfig('sms', { enabled: true });
423
+ p.log.success('SMS channel enabled!');
424
+ return;
425
+ }
426
+ }
427
+
428
+ const openTwilio = await p.confirm({
429
+ message: 'Open Twilio Console?',
430
+ initialValue: true,
431
+ });
432
+
433
+ if (openTwilio && !p.isCancel(openTwilio)) {
434
+ const open = (await import('open')).default;
435
+ await open('https://console.twilio.com/');
436
+ }
437
+
438
+ const accountSid = await p.text({
439
+ message: 'Account SID:',
440
+ placeholder: 'AC...',
441
+ });
442
+
443
+ if (p.isCancel(accountSid)) return;
444
+
445
+ const authToken = await p.text({
446
+ message: 'Auth Token:',
447
+ placeholder: '...',
448
+ });
449
+
450
+ if (p.isCancel(authToken)) return;
451
+
452
+ const phoneNumber = await p.text({
453
+ message: 'Twilio phone number:',
454
+ placeholder: '+1234567890',
455
+ });
456
+
457
+ if (p.isCancel(phoneNumber)) return;
458
+
459
+ config.setCredentials('twilio', {
460
+ accountSid: accountSid as string,
461
+ authToken: authToken as string,
462
+ phoneNumber: phoneNumber as string,
463
+ });
464
+
465
+ config.setChannelConfig('sms', { enabled: true });
466
+ p.log.success('SMS channel configured!');
467
+ }
468
+
469
+ async function connectiMessage(config: Config): Promise<void> {
470
+ p.log.warn('iMessage support requires macOS and additional setup.');
471
+ p.log.info(chalk.dim(`
472
+ iMessage integration uses AppleScript on macOS.
473
+ This requires:
474
+ 1. macOS with Messages app
475
+ 2. iCloud signed in
476
+ 3. Terminal with accessibility permissions
477
+ `));
478
+
479
+ const isMac = process.platform === 'darwin';
480
+
481
+ if (!isMac) {
482
+ p.log.error('iMessage is only available on macOS.');
483
+ return;
484
+ }
485
+
486
+ // Would check for accessibility permissions and configure
487
+ p.log.info('iMessage support coming soon.');
488
+ }
489
+
490
+ async function startAllChannels(config: Config): Promise<void> {
491
+ const spinner = p.spinner();
492
+ spinner.start('Starting channel gateway...');
493
+
494
+ // In real implementation, this would start all enabled channels
495
+ await new Promise(resolve => setTimeout(resolve, 1000));
496
+
497
+ spinner.stop('Gateway started');
498
+
499
+ console.log(chalk.green(`
500
+ ╔═══════════════════════════════════════╗
501
+ ║ Gateway is running! ║
502
+ ║ ║
503
+ ║ You can now receive messages from: ║
504
+ ║ • WhatsApp • Telegram • Discord ║
505
+ ║ • Slack • SMS ║
506
+ ║ ║
507
+ ║ Press Ctrl+C to stop ║
508
+ ╚═══════════════════════════════════════╝
509
+ `));
510
+ }
511
+
512
+ async function stopAllChannels(config: Config): Promise<void> {
513
+ const spinner = p.spinner();
514
+ spinner.start('Stopping gateway...');
515
+
516
+ await new Promise(resolve => setTimeout(resolve, 500));
517
+
518
+ spinner.stop('Gateway stopped');
519
+ }
520
+
521
+ async function deployGatewayWizard(config: Config): Promise<void> {
522
+ p.log.info(chalk.bold('Deploy Gateway to Cloud'));
523
+
524
+ const platform = await p.select({
525
+ message: 'Where do you want to deploy?',
526
+ options: [
527
+ { value: 'codebakers', label: '☁️ CodeBakers Cloud', hint: '$29/mo - We handle everything' },
528
+ { value: 'vps', label: '🖥️ VPS', hint: '$5/mo - DigitalOcean, etc.' },
529
+ { value: 'local', label: '🏠 Home server', hint: 'Raspberry Pi, etc.' },
530
+ ],
531
+ });
532
+
533
+ if (p.isCancel(platform)) return;
534
+
535
+ switch (platform) {
536
+ case 'codebakers':
537
+ p.log.info('CodeBakers Cloud coming soon!');
538
+ p.log.info(chalk.dim('Sign up at https://codebakers.dev/cloud'));
539
+ break;
540
+
541
+ case 'vps':
542
+ await deployToVPS(config);
543
+ break;
544
+
545
+ case 'local':
546
+ p.log.info('Local deployment guide:');
547
+ p.log.info(chalk.dim(`
548
+ 1. Install Node.js on your server
549
+ 2. Clone your project
550
+ 3. Run: npm install
551
+ 4. Run: pm2 start codebakers -- gateway --start
552
+ 5. Configure firewall to allow outbound connections
553
+ `));
554
+ break;
555
+ }
556
+ }
557
+
558
+ async function deployToVPS(config: Config): Promise<void> {
559
+ const provider = await p.select({
560
+ message: 'Which VPS provider?',
561
+ options: [
562
+ { value: 'digitalocean', label: 'DigitalOcean' },
563
+ { value: 'vultr', label: 'Vultr' },
564
+ { value: 'linode', label: 'Linode' },
565
+ { value: 'hetzner', label: 'Hetzner' },
566
+ { value: 'other', label: 'Other' },
567
+ ],
568
+ });
569
+
570
+ if (p.isCancel(provider)) return;
571
+
572
+ if (provider === 'digitalocean') {
573
+ p.log.info('DigitalOcean auto-deploy:');
574
+ p.log.info(chalk.dim(`
575
+ 1. Opening DigitalOcean login...
576
+ 2. We'll create a $6/mo droplet
577
+ 3. Install CodeBakers gateway
578
+ 4. Connect all your channels
579
+ `));
580
+
581
+ const proceed = await p.confirm({
582
+ message: 'Open DigitalOcean?',
583
+ initialValue: true,
584
+ });
585
+
586
+ if (proceed && !p.isCancel(proceed)) {
587
+ const open = (await import('open')).default;
588
+ await open('https://cloud.digitalocean.com/');
589
+ }
590
+ } else {
591
+ p.log.info(chalk.dim(`
592
+ Manual VPS setup:
593
+
594
+ 1. Create a VPS (Ubuntu 22.04 recommended)
595
+ 2. SSH into your server
596
+ 3. Run these commands:
597
+
598
+ curl -fsSL https://codebakers.dev/install.sh | bash
599
+ codebakers gateway --start
600
+
601
+ 4. Your gateway will start automatically on boot
602
+ `));
603
+ }
604
+ }