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/agents/agentWebSocket.js +170 -0
- package/dist/agents/gektoPersistent.js +3 -1
- package/dist/agents/gektoTools.js +14 -0
- package/dist/detectClaude.js +14 -0
- package/dist/inspectRepo.js +185 -0
- package/dist/onboarding.js +339 -0
- package/dist/portUtils.js +40 -0
- package/dist/posthog.js +53 -0
- package/dist/proxy.js +96 -132
- package/dist/terminal.js +5 -0
- package/dist/widget/gekto-widget.iife.js +279 -271
- package/package.json +2 -1
package/dist/proxy.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
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
|
|
44
|
+
let HAS_WEB_UI = true;
|
|
45
|
+
let STACK = null;
|
|
44
46
|
let DEV_MODE = false;
|
|
45
47
|
let WIDGET_PORT = 5174;
|
|
46
|
-
//
|
|
47
|
-
async function
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
369
|
-
if (
|
|
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
|
-
|
|
491
|
-
|
|
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}
|
|
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,
|