gekto 0.0.13 → 0.0.14
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 +114 -0
- package/dist/posthog.js +53 -0
- package/dist/proxy.js +33 -0
- package/dist/terminal.js +5 -0
- package/package.json +2 -1
|
@@ -6,6 +6,7 @@ import { getState, mutate, mutateBatch, addClient, removeClient, sendSnapshot, g
|
|
|
6
6
|
import { persistEntity } from '../entityStore.js';
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import nodePath from 'path';
|
|
9
|
+
import { getPostHog, getDistinctId } from '../posthog.js';
|
|
9
10
|
let gektoInitialized = false;
|
|
10
11
|
function broadcastGektoState(state) {
|
|
11
12
|
const message = JSON.stringify({ type: 'gekto_state', state });
|
|
@@ -160,6 +161,11 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
160
161
|
type: 'kill_all_result',
|
|
161
162
|
killed: killedCount,
|
|
162
163
|
}));
|
|
164
|
+
getPostHog().capture({
|
|
165
|
+
distinctId: getDistinctId(),
|
|
166
|
+
event: 'all agents killed',
|
|
167
|
+
properties: { killed_count: killedCount },
|
|
168
|
+
});
|
|
163
169
|
// Notify about state changes
|
|
164
170
|
for (const session of getActiveSessions()) {
|
|
165
171
|
ws.send(JSON.stringify({ type: 'state', lizardId: session.lizardId, state: 'ready' }));
|
|
@@ -193,6 +199,11 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
193
199
|
mutate('activePlanId', null);
|
|
194
200
|
broadcastActivePlans();
|
|
195
201
|
broadcastActivePlanId();
|
|
202
|
+
getPostHog().capture({
|
|
203
|
+
distinctId: getDistinctId(),
|
|
204
|
+
event: 'all agents cleared',
|
|
205
|
+
properties: { agent_count: agentIds.length },
|
|
206
|
+
});
|
|
196
207
|
return;
|
|
197
208
|
}
|
|
198
209
|
case 'set_active_plan': {
|
|
@@ -299,6 +310,14 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
299
310
|
planId: msg.planId,
|
|
300
311
|
prompt: msg.prompt,
|
|
301
312
|
}));
|
|
313
|
+
getPostHog().capture({
|
|
314
|
+
distinctId: getDistinctId(),
|
|
315
|
+
event: 'gekto message sent',
|
|
316
|
+
properties: {
|
|
317
|
+
plan_id: msg.planId,
|
|
318
|
+
has_images: Boolean(planImagePaths?.length),
|
|
319
|
+
},
|
|
320
|
+
});
|
|
302
321
|
const planResult = await processWithTools(msg.prompt, msg.planId, getWorkingDir(), getActiveSessions(), planCallbacks, msg.existingPlan, planImagePaths);
|
|
303
322
|
// Replace streamed JSON with clean message (always send to overwrite raw JSON)
|
|
304
323
|
// For delegate, the system message handles display — clear the streaming text
|
|
@@ -348,6 +367,15 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
348
367
|
planId: msg.planId,
|
|
349
368
|
plan: planResult.plan,
|
|
350
369
|
}));
|
|
370
|
+
getPostHog().capture({
|
|
371
|
+
distinctId: getDistinctId(),
|
|
372
|
+
event: 'plan created',
|
|
373
|
+
properties: {
|
|
374
|
+
plan_id: planResult.plan.id,
|
|
375
|
+
plan_title: planResult.plan.title,
|
|
376
|
+
action: planExistedBefore ? 'update_plan' : 'create_plan',
|
|
377
|
+
},
|
|
378
|
+
});
|
|
351
379
|
}
|
|
352
380
|
else if (planResult.type === 'remove' && planResult.removedAgents) {
|
|
353
381
|
// Remove agents from server state
|
|
@@ -441,6 +469,7 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
441
469
|
}
|
|
442
470
|
catch (err) {
|
|
443
471
|
console.error('[Agent] Gekto processing failed:', err);
|
|
472
|
+
getPostHog().captureException(err, getDistinctId(), { plan_id: msg.planId });
|
|
444
473
|
ws.send(JSON.stringify({
|
|
445
474
|
type: 'gekto_chat',
|
|
446
475
|
planId: msg.planId,
|
|
@@ -531,6 +560,14 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
531
560
|
planId: msg.planId,
|
|
532
561
|
taskCount: tasks.length,
|
|
533
562
|
}));
|
|
563
|
+
getPostHog().capture({
|
|
564
|
+
distinctId: getDistinctId(),
|
|
565
|
+
event: 'plan tasks generated',
|
|
566
|
+
properties: {
|
|
567
|
+
plan_id: msg.planId,
|
|
568
|
+
task_count: tasks.length,
|
|
569
|
+
},
|
|
570
|
+
});
|
|
534
571
|
},
|
|
535
572
|
onError: (error) => {
|
|
536
573
|
ws.send(JSON.stringify({
|
|
@@ -547,6 +584,7 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
547
584
|
}
|
|
548
585
|
catch (err) {
|
|
549
586
|
console.error('[Agent] Task generation failed:', err);
|
|
587
|
+
getPostHog().captureException(err, getDistinctId(), { plan_id: msg.planId });
|
|
550
588
|
ws.send(JSON.stringify({
|
|
551
589
|
type: 'gekto_chat',
|
|
552
590
|
planId: msg.planId,
|
|
@@ -570,9 +608,22 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
570
608
|
mutate(`activePlans.${msg.planId}.status`, 'executing');
|
|
571
609
|
broadcastSinglePlan(msg.planId);
|
|
572
610
|
}
|
|
611
|
+
getPostHog().capture({
|
|
612
|
+
distinctId: getDistinctId(),
|
|
613
|
+
event: 'plan executed',
|
|
614
|
+
properties: {
|
|
615
|
+
plan_id: msg.planId,
|
|
616
|
+
task_count: currentState.activePlans[msg.planId]?.taskIds?.length ?? 0,
|
|
617
|
+
},
|
|
618
|
+
});
|
|
573
619
|
return;
|
|
574
620
|
}
|
|
575
621
|
case 'cancel_plan': {
|
|
622
|
+
getPostHog().capture({
|
|
623
|
+
distinctId: getDistinctId(),
|
|
624
|
+
event: 'plan canceled',
|
|
625
|
+
properties: { plan_id: msg.planId },
|
|
626
|
+
});
|
|
576
627
|
const currentState = getState();
|
|
577
628
|
const cancelPlan = currentState.activePlans[msg.planId];
|
|
578
629
|
if (cancelPlan) {
|
|
@@ -625,6 +676,15 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
625
676
|
]);
|
|
626
677
|
broadcastTask(task.id);
|
|
627
678
|
broadcastAgent(agent.id);
|
|
679
|
+
getPostHog().capture({
|
|
680
|
+
distinctId: getDistinctId(),
|
|
681
|
+
event: 'worker agent spawned',
|
|
682
|
+
properties: {
|
|
683
|
+
agent_id: agent.id,
|
|
684
|
+
task_id: task.id,
|
|
685
|
+
plan_id: task.planId,
|
|
686
|
+
},
|
|
687
|
+
});
|
|
628
688
|
return;
|
|
629
689
|
}
|
|
630
690
|
// Client updates a chat message list — store on agent
|
|
@@ -695,6 +755,16 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
695
755
|
broadcastTask(msg.taskId);
|
|
696
756
|
if (agentId)
|
|
697
757
|
broadcastAgent(agentId);
|
|
758
|
+
getPostHog().capture({
|
|
759
|
+
distinctId: getDistinctId(),
|
|
760
|
+
event: 'task completed',
|
|
761
|
+
properties: {
|
|
762
|
+
task_id: msg.taskId,
|
|
763
|
+
task_name: taskState?.name,
|
|
764
|
+
plan_id: taskState?.planId,
|
|
765
|
+
agent_id: agentId,
|
|
766
|
+
},
|
|
767
|
+
});
|
|
698
768
|
}
|
|
699
769
|
return;
|
|
700
770
|
}
|
|
@@ -713,16 +783,38 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
713
783
|
broadcastTask(msg.taskId);
|
|
714
784
|
if (agentId)
|
|
715
785
|
broadcastAgent(agentId);
|
|
786
|
+
getPostHog().capture({
|
|
787
|
+
distinctId: getDistinctId(),
|
|
788
|
+
event: 'task failed',
|
|
789
|
+
properties: {
|
|
790
|
+
task_id: msg.taskId,
|
|
791
|
+
task_name: taskState?.name,
|
|
792
|
+
plan_id: taskState?.planId,
|
|
793
|
+
agent_id: agentId,
|
|
794
|
+
error: msg.error,
|
|
795
|
+
},
|
|
796
|
+
});
|
|
716
797
|
}
|
|
717
798
|
return;
|
|
718
799
|
}
|
|
719
800
|
case 'task_started': {
|
|
720
801
|
if (msg.taskId) {
|
|
802
|
+
const taskState = getState().tasks[msg.taskId];
|
|
721
803
|
mutateBatch([
|
|
722
804
|
{ path: `tasks.${msg.taskId}.status`, value: 'in_progress' },
|
|
723
805
|
{ path: `tasks.${msg.taskId}.assignedAgentId`, value: msg.lizardId },
|
|
724
806
|
]);
|
|
725
807
|
broadcastTask(msg.taskId);
|
|
808
|
+
getPostHog().capture({
|
|
809
|
+
distinctId: getDistinctId(),
|
|
810
|
+
event: 'task started',
|
|
811
|
+
properties: {
|
|
812
|
+
task_id: msg.taskId,
|
|
813
|
+
task_name: taskState?.name,
|
|
814
|
+
plan_id: taskState?.planId,
|
|
815
|
+
agent_id: msg.lizardId,
|
|
816
|
+
},
|
|
817
|
+
});
|
|
726
818
|
}
|
|
727
819
|
return;
|
|
728
820
|
}
|
|
@@ -930,6 +1022,14 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
930
1022
|
broadcastAgent(lizardId);
|
|
931
1023
|
}
|
|
932
1024
|
const images = msg.images;
|
|
1025
|
+
getPostHog().capture({
|
|
1026
|
+
distinctId: getDistinctId(),
|
|
1027
|
+
event: 'agent message sent',
|
|
1028
|
+
properties: {
|
|
1029
|
+
agent_id: lizardId,
|
|
1030
|
+
has_images: Boolean(images?.length),
|
|
1031
|
+
},
|
|
1032
|
+
});
|
|
933
1033
|
await sendMessage(lizardId, msg.content, ws, images);
|
|
934
1034
|
}
|
|
935
1035
|
catch (err) {
|
|
@@ -943,6 +1043,11 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
943
1043
|
else {
|
|
944
1044
|
resetSession(lizardId);
|
|
945
1045
|
}
|
|
1046
|
+
getPostHog().capture({
|
|
1047
|
+
distinctId: getDistinctId(),
|
|
1048
|
+
event: 'agent reset',
|
|
1049
|
+
properties: { agent_id: lizardId },
|
|
1050
|
+
});
|
|
946
1051
|
ws.send(JSON.stringify({ type: 'state', lizardId, state: 'ready' }));
|
|
947
1052
|
break;
|
|
948
1053
|
case 'revert_files': {
|
|
@@ -953,6 +1058,15 @@ export function setupAgentWebSocket(server, path = '/__gekto/agent') {
|
|
|
953
1058
|
reverted: revertResult.reverted,
|
|
954
1059
|
failed: revertResult.failed,
|
|
955
1060
|
}));
|
|
1061
|
+
getPostHog().capture({
|
|
1062
|
+
distinctId: getDistinctId(),
|
|
1063
|
+
event: 'files reverted',
|
|
1064
|
+
properties: {
|
|
1065
|
+
agent_id: lizardId,
|
|
1066
|
+
reverted_count: revertResult.reverted.length,
|
|
1067
|
+
failed_count: revertResult.failed.length,
|
|
1068
|
+
},
|
|
1069
|
+
});
|
|
956
1070
|
// Remove reverted file changes from agent state
|
|
957
1071
|
const agentState = getState().agents[lizardId];
|
|
958
1072
|
if (agentState?.fileChanges) {
|
package/dist/posthog.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { PostHog } from 'posthog-node';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
// Public project API key — phc_ keys are safe to ship in client code.
|
|
6
|
+
const DEFAULT_API_KEY = 'phc_s8GT8r8YG23NJqUhpyq2XcNegWyLomZjnsJmmtxOKMf';
|
|
7
|
+
const DEFAULT_HOST = 'https://us.i.posthog.com';
|
|
8
|
+
// Lazy singleton — created on first use
|
|
9
|
+
let _client = null;
|
|
10
|
+
// Distinct ID used for all events — set once per process
|
|
11
|
+
let _distinctId = 'anonymous';
|
|
12
|
+
export function getPostHog() {
|
|
13
|
+
if (!_client) {
|
|
14
|
+
_client = new PostHog(process.env.POSTHOG_API_KEY || DEFAULT_API_KEY, {
|
|
15
|
+
host: process.env.POSTHOG_HOST || DEFAULT_HOST,
|
|
16
|
+
enableExceptionAutocapture: true,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return _client;
|
|
20
|
+
}
|
|
21
|
+
/** Return the current distinct ID for event capture. */
|
|
22
|
+
export function getDistinctId() {
|
|
23
|
+
return _distinctId;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Set the distinct ID from the user's stored email or a persisted install UUID.
|
|
27
|
+
* Should be called once during startup / after onboarding completes.
|
|
28
|
+
*/
|
|
29
|
+
export function initDistinctId(email) {
|
|
30
|
+
if (email && email.trim()) {
|
|
31
|
+
_distinctId = email.trim();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Fall back to a persisted install UUID stored alongside gekto-store.json
|
|
35
|
+
const installIdPath = join(process.cwd(), '.gekto-install-id');
|
|
36
|
+
if (existsSync(installIdPath)) {
|
|
37
|
+
try {
|
|
38
|
+
const id = readFileSync(installIdPath, 'utf8').trim();
|
|
39
|
+
if (id) {
|
|
40
|
+
_distinctId = id;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch { /* ignore */ }
|
|
45
|
+
}
|
|
46
|
+
// First run — generate and persist an install UUID
|
|
47
|
+
const id = randomUUID();
|
|
48
|
+
try {
|
|
49
|
+
writeFileSync(installIdPath, id, 'utf8');
|
|
50
|
+
}
|
|
51
|
+
catch { /* ignore */ }
|
|
52
|
+
_distinctId = id;
|
|
53
|
+
}
|
package/dist/proxy.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as p from '@clack/prompts';
|
|
3
|
+
import { getPostHog, getDistinctId, initDistinctId } from './posthog.js';
|
|
3
4
|
// Colors for TUI
|
|
4
5
|
const c = {
|
|
5
6
|
reset: '\x1b[0m',
|
|
@@ -100,6 +101,7 @@ async function runOnboarding() {
|
|
|
100
101
|
PROJECT_TYPE = existingSettings.projectType;
|
|
101
102
|
TARGET_PORT = existingSettings.targetPort;
|
|
102
103
|
PROXY_PORT = existingSettings.proxyPort;
|
|
104
|
+
initDistinctId(existingSettings.email);
|
|
103
105
|
console.log(`${c.dim}Loaded settings from gekto-store.json${c.reset}`);
|
|
104
106
|
return;
|
|
105
107
|
}
|
|
@@ -162,6 +164,17 @@ async function runOnboarding() {
|
|
|
162
164
|
onboardingCompleted: true,
|
|
163
165
|
email,
|
|
164
166
|
});
|
|
167
|
+
initDistinctId(email);
|
|
168
|
+
getPostHog().capture({
|
|
169
|
+
distinctId: getDistinctId(),
|
|
170
|
+
event: 'onboarding completed',
|
|
171
|
+
properties: {
|
|
172
|
+
project_type: PROJECT_TYPE,
|
|
173
|
+
has_email: Boolean(email),
|
|
174
|
+
$set: email ? { email } : undefined,
|
|
175
|
+
$set_once: { initial_project_type: PROJECT_TYPE },
|
|
176
|
+
},
|
|
177
|
+
});
|
|
165
178
|
spinner.stop('Ready!');
|
|
166
179
|
p.outro(`${c.green}Starting Gekto...${c.reset}`);
|
|
167
180
|
}
|
|
@@ -182,9 +195,12 @@ async function main() {
|
|
|
182
195
|
// Prevent EPIPE and other uncaught errors from crashing the server
|
|
183
196
|
process.on('uncaughtException', (err) => {
|
|
184
197
|
console.error('[proxy] Uncaught exception (server stays running):', err.message);
|
|
198
|
+
getPostHog().captureException(err, getDistinctId());
|
|
185
199
|
});
|
|
186
200
|
process.on('unhandledRejection', (err) => {
|
|
187
201
|
console.error('[proxy] Unhandled rejection (server stays running):', err);
|
|
202
|
+
if (err instanceof Error)
|
|
203
|
+
getPostHog().captureException(err, getDistinctId());
|
|
188
204
|
});
|
|
189
205
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
190
206
|
// Parse CLI arguments (for overrides)
|
|
@@ -487,7 +503,24 @@ async function main() {
|
|
|
487
503
|
// === STEP 4: Show logo and start listening ===
|
|
488
504
|
console.clear();
|
|
489
505
|
printLogo();
|
|
506
|
+
// Graceful shutdown — flush PostHog before exit
|
|
507
|
+
const shutdown = async () => {
|
|
508
|
+
await getPostHog().shutdown();
|
|
509
|
+
process.exit(0);
|
|
510
|
+
};
|
|
511
|
+
process.on('SIGINT', shutdown);
|
|
512
|
+
process.on('SIGTERM', shutdown);
|
|
490
513
|
server.listen(PROXY_PORT, () => {
|
|
514
|
+
getPostHog().capture({
|
|
515
|
+
distinctId: getDistinctId(),
|
|
516
|
+
event: 'proxy started',
|
|
517
|
+
properties: {
|
|
518
|
+
project_type: PROJECT_TYPE,
|
|
519
|
+
proxy_port: PROXY_PORT,
|
|
520
|
+
target_port: TARGET_PORT,
|
|
521
|
+
dev_mode: DEV_MODE,
|
|
522
|
+
},
|
|
523
|
+
});
|
|
491
524
|
if (PROJECT_TYPE === 'frontend') {
|
|
492
525
|
printBox([
|
|
493
526
|
`${c.bold}Gekto is ready!${c.reset}`,
|
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,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gekto",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "AI coding assistant widget - inject into any web app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@clack/prompts": "^1.0.0",
|
|
29
29
|
"node-pty": "^1.1.0-beta30",
|
|
30
|
+
"posthog-node": "^5.21.2",
|
|
30
31
|
"ws": "^8.18.3"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|