gekto 0.0.13 → 0.0.15

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.
package/dist/proxy.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import * as p from '@clack/prompts';
2
+ import { getPostHog, getDistinctId } from './posthog.js';
3
+ import { runOnboarding } from './onboarding.js';
3
4
  // Colors for TUI
4
5
  const c = {
5
6
  reset: '\x1b[0m',
@@ -40,135 +41,25 @@ function printBox(lines, color = '\x1b[32m') {
40
41
  // Configuration - will be set after prompts
41
42
  let PROXY_PORT = 3200;
42
43
  let TARGET_PORT = 5173;
43
- let PROJECT_TYPE = 'frontend';
44
+ let HAS_WEB_UI = true;
45
+ let STACK = null;
44
46
  let DEV_MODE = false;
45
47
  let WIDGET_PORT = 5174;
46
- // Save lead to SheetDB
47
- async function saveLeadToSheetDB(data) {
48
- try {
49
- await fetch('https://sheetdb.io/api/v1/hxn1hd5nzjxhd?sheet=npx%20data', {
50
- method: 'POST',
51
- headers: { 'Content-Type': 'application/json' },
52
- body: JSON.stringify({ data }),
53
- });
54
- }
55
- catch {
56
- // Silently fail - don't block onboarding
57
- }
58
- }
59
- // Load settings from store (imported dynamically later)
60
- let loadSettings;
61
- let saveSettings;
62
- // Onboarding prompts - runs FIRST before anything else
63
- async function runOnboarding() {
64
- // Skip onboarding in dev mode (bun run dev)
48
+ // Main function - runs after onboarding completes
49
+ async function main() {
50
+ // === STEP 1: Run onboarding FIRST (nothing else runs yet) ===
65
51
  if (process.env.GEKTO_DEV === '1') {
66
52
  DEV_MODE = true;
67
53
  TARGET_PORT = 5173;
68
- return;
69
- }
70
- // Dynamic import to avoid loading before onboarding
71
- const fs = await import('fs');
72
- const path = await import('path');
73
- const STORE_PATH = path.join(process.cwd(), 'gekto-store.json');
74
- loadSettings = () => {
75
- try {
76
- if (fs.existsSync(STORE_PATH)) {
77
- const store = JSON.parse(fs.readFileSync(STORE_PATH, 'utf8'));
78
- return store.data?.settings;
79
- }
80
- }
81
- catch { }
82
- return undefined;
83
- };
84
- saveSettings = (settings) => {
85
- let store = { version: 1, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), data: {} };
86
- try {
87
- if (fs.existsSync(STORE_PATH)) {
88
- store = JSON.parse(fs.readFileSync(STORE_PATH, 'utf8'));
89
- }
90
- }
91
- catch { }
92
- store.data.settings = settings;
93
- store.updatedAt = new Date().toISOString();
94
- fs.writeFileSync(STORE_PATH, JSON.stringify(store, null, 2));
95
- };
96
- // Check for existing settings
97
- const existingSettings = loadSettings();
98
- if (existingSettings?.onboardingCompleted) {
99
- // Use saved settings
100
- PROJECT_TYPE = existingSettings.projectType;
101
- TARGET_PORT = existingSettings.targetPort;
102
- PROXY_PORT = existingSettings.proxyPort;
103
- console.log(`${c.dim}Loaded settings from gekto-store.json${c.reset}`);
104
- return;
54
+ HAS_WEB_UI = true;
105
55
  }
106
- console.clear();
107
- p.intro(`${c.green}${c.bold}create-gekto${c.reset}`);
108
- // Ask project type
109
- const projectType = await p.select({
110
- message: 'Select project type',
111
- options: [
112
- { label: 'Frontend (React, Vue, Next.js, etc.)', value: 'frontend' },
113
- { label: 'Backend (Node.js, Express, FastAPI, etc.)', value: 'backend' },
114
- { label: 'CLI (Command-line tools)', value: 'cli' },
115
- { label: 'Fullstack (Frontend + Backend)', value: 'fullstack' },
116
- { label: 'Mobile (React Native, Flutter, etc.)', value: 'mobile' },
117
- { label: 'Other', value: 'other' },
118
- ],
119
- });
120
- if (p.isCancel(projectType)) {
121
- p.cancel('Setup cancelled.');
122
- process.exit(0);
56
+ else {
57
+ const result = await runOnboarding();
58
+ STACK = result.stack;
59
+ HAS_WEB_UI = result.stack.hasWebUI;
60
+ TARGET_PORT = result.stack.port ?? TARGET_PORT;
61
+ PROXY_PORT = result.proxyPort;
123
62
  }
124
- PROJECT_TYPE = projectType;
125
- // For frontend apps, ask for the port
126
- if (PROJECT_TYPE === 'frontend') {
127
- const portAnswer = await p.text({
128
- message: 'What port is your app running on?',
129
- placeholder: '3000',
130
- defaultValue: '3000',
131
- });
132
- if (p.isCancel(portAnswer)) {
133
- p.cancel('Setup cancelled.');
134
- process.exit(0);
135
- }
136
- TARGET_PORT = parseInt(portAnswer, 10);
137
- }
138
- // Ask for email
139
- const emailAnswer = await p.text({
140
- message: 'Enter your email for updates (optional)',
141
- placeholder: 'you@example.com',
142
- });
143
- if (p.isCancel(emailAnswer)) {
144
- p.cancel('Setup cancelled.');
145
- process.exit(0);
146
- }
147
- const email = emailAnswer || '';
148
- // Show spinner while saving
149
- const spinner = p.spinner();
150
- spinner.start('Preparing Gekto...');
151
- // Save lead to SheetDB (always send, even if email is empty)
152
- await saveLeadToSheetDB({
153
- project_type: PROJECT_TYPE,
154
- port: String(TARGET_PORT),
155
- email,
156
- });
157
- // Save settings for next time
158
- saveSettings({
159
- projectType: PROJECT_TYPE,
160
- targetPort: TARGET_PORT,
161
- proxyPort: PROXY_PORT,
162
- onboardingCompleted: true,
163
- email,
164
- });
165
- spinner.stop('Ready!');
166
- p.outro(`${c.green}Starting Gekto...${c.reset}`);
167
- }
168
- // Main function - runs after onboarding completes
169
- async function main() {
170
- // === STEP 1: Run onboarding FIRST (nothing else runs yet) ===
171
- await runOnboarding();
172
63
  // === STEP 2: Now load all the heavy modules ===
173
64
  const http = await import('http');
174
65
  const https = await import('https');
@@ -182,9 +73,12 @@ async function main() {
182
73
  // Prevent EPIPE and other uncaught errors from crashing the server
183
74
  process.on('uncaughtException', (err) => {
184
75
  console.error('[proxy] Uncaught exception (server stays running):', err.message);
76
+ getPostHog().captureException(err, getDistinctId());
185
77
  });
186
78
  process.on('unhandledRejection', (err) => {
187
79
  console.error('[proxy] Unhandled rejection (server stays running):', err);
80
+ if (err instanceof Error)
81
+ getPostHog().captureException(err, getDistinctId());
188
82
  });
189
83
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
190
84
  // Parse CLI arguments (for overrides)
@@ -365,8 +259,8 @@ async function main() {
365
259
  }
366
260
  }
367
261
  }
368
- // For non-frontend projects, serve standalone page
369
- if (PROJECT_TYPE !== 'frontend' && (url === '/' || url === '/index.html')) {
262
+ // For projects without a web UI, serve a standalone widget page
263
+ if (!HAS_WEB_UI && (url === '/' || url === '/index.html')) {
370
264
  const injection = getInjectionScript();
371
265
  res.writeHead(200, { 'Content-Type': 'text/html' });
372
266
  res.end(`
@@ -487,8 +381,18 @@ async function main() {
487
381
  // === STEP 4: Show logo and start listening ===
488
382
  console.clear();
489
383
  printLogo();
490
- server.listen(PROXY_PORT, () => {
491
- if (PROJECT_TYPE === 'frontend') {
384
+ // Graceful shutdown — flush PostHog before exit
385
+ const shutdown = async () => {
386
+ teardownKeyboardControls();
387
+ await getPostHog().shutdown();
388
+ process.exit(0);
389
+ };
390
+ process.on('SIGINT', shutdown);
391
+ process.on('SIGTERM', shutdown);
392
+ const printReadyScreen = () => {
393
+ console.clear();
394
+ printLogo();
395
+ if (HAS_WEB_UI) {
492
396
  printBox([
493
397
  `${c.bold}Gekto is ready!${c.reset}`,
494
398
  ``,
@@ -497,7 +401,6 @@ async function main() {
497
401
  `${c.dim}Mode:${c.reset} ${c.yellow}${DEV_MODE ? 'development' : 'production'}${c.reset}`,
498
402
  ``,
499
403
  `${c.dim}Open ${c.white}http://localhost:${PROXY_PORT}${c.dim} in your browser${c.reset}`,
500
- `${c.dim}Press ${c.white}Ctrl+C${c.dim} to stop${c.reset}`,
501
404
  ], c.green);
502
405
  }
503
406
  else {
@@ -506,14 +409,75 @@ async function main() {
506
409
  ``,
507
410
  `${c.dim}Open:${c.reset} ${c.magenta}http://localhost:${PROXY_PORT}${c.reset}`,
508
411
  `${c.dim}Mode:${c.reset} ${c.yellow}${DEV_MODE ? 'development' : 'production'}${c.reset}`,
509
- ``,
510
- `${c.dim}Press ${c.white}Ctrl+C${c.dim} to stop${c.reset}`,
511
412
  ], c.green);
512
413
  }
513
- // Footer
514
414
  console.log();
515
- console.log(` ${c.dim}Enjoying Gekto? Star us on GitHub: ${c.white}https://github.com/Badaboom1995/gekto${c.reset}`);
415
+ console.log(` ${c.dim}[R] refresh setup • [Q] quit${c.reset}`);
416
+ console.log();
417
+ console.log(` ${c.dim}Enjoying Gekto? ⭐ Star us on GitHub: ${c.white}https://github.com/gekto-dev/gekto${c.reset}`);
516
418
  console.log();
419
+ };
420
+ let keyHandler = null;
421
+ const teardownKeyboardControls = () => {
422
+ if (!process.stdin.isTTY)
423
+ return;
424
+ if (keyHandler)
425
+ process.stdin.off('data', keyHandler);
426
+ keyHandler = null;
427
+ try {
428
+ process.stdin.setRawMode(false);
429
+ }
430
+ catch { }
431
+ process.stdin.pause();
432
+ };
433
+ const setupKeyboardControls = () => {
434
+ if (!process.stdin.isTTY)
435
+ return;
436
+ process.stdin.setRawMode(true);
437
+ process.stdin.resume();
438
+ process.stdin.setEncoding('utf8');
439
+ keyHandler = (key) => {
440
+ if (key === '' || key === 'q' || key === 'Q') {
441
+ void shutdown();
442
+ return;
443
+ }
444
+ if (key === 'r' || key === 'R')
445
+ void refreshSetup();
446
+ };
447
+ process.stdin.on('data', keyHandler);
448
+ };
449
+ const refreshSetup = async () => {
450
+ teardownKeyboardControls();
451
+ console.log();
452
+ try {
453
+ const result = await runOnboarding({ force: true, dev: DEV_MODE });
454
+ STACK = result.stack;
455
+ HAS_WEB_UI = result.stack.hasWebUI;
456
+ TARGET_PORT = result.stack.port ?? TARGET_PORT;
457
+ }
458
+ catch (err) {
459
+ console.error('[refresh] onboarding failed:', err);
460
+ }
461
+ printReadyScreen();
462
+ setupKeyboardControls();
463
+ };
464
+ server.listen(PROXY_PORT, () => {
465
+ getPostHog().capture({
466
+ distinctId: getDistinctId(),
467
+ event: 'proxy started',
468
+ properties: {
469
+ language: STACK?.language,
470
+ framework: STACK?.framework,
471
+ bundler: STACK?.bundler,
472
+ runtime: STACK?.runtime,
473
+ has_web_ui: HAS_WEB_UI,
474
+ proxy_port: PROXY_PORT,
475
+ target_port: TARGET_PORT,
476
+ dev_mode: DEV_MODE,
477
+ },
478
+ });
479
+ printReadyScreen();
480
+ setupKeyboardControls();
517
481
  });
518
482
  }
519
483
  // Run
package/dist/terminal.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { WebSocket, WebSocketServer } from 'ws';
2
2
  import * as pty from 'node-pty';
3
+ import { getPostHog, getDistinctId } from './posthog.js';
3
4
  const sessions = new Map();
4
5
  export function setupTerminalWebSocket(server, path = '/__gekto/terminal') {
5
6
  const wss = new WebSocketServer({ noServer: true });
@@ -14,6 +15,10 @@ export function setupTerminalWebSocket(server, path = '/__gekto/terminal') {
14
15
  // Other upgrade requests (like Vite HMR) are handled elsewhere
15
16
  });
16
17
  wss.on('connection', (ws) => {
18
+ getPostHog().capture({
19
+ distinctId: getDistinctId(),
20
+ event: 'terminal session started',
21
+ });
17
22
  // Create session but don't spawn PTY yet - wait for resize
18
23
  const session = {
19
24
  pty: null,