@exreve/exk 1.0.55 → 1.0.57

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.
@@ -1,142 +0,0 @@
1
- /**
2
- * App Control Handlers Module
3
- *
4
- * Handles app:start, app:stop, app:restart, app:status, app:logs.
5
- */
6
- import { getProjectConfig } from './projectAnalyzer.js';
7
- import { startApp, stopApp, restartApp, getAppStatuses, getAppLogs } from './appManager.js';
8
- export function registerAppHandlers(socket, foreground) {
9
- socket.on('app:start', async (data, callback) => {
10
- try {
11
- const { projectId, projectPath, appName } = data;
12
- const config = await getProjectConfig(projectPath);
13
- if (!config) {
14
- callback?.({ success: false, error: 'Project config not found' });
15
- return;
16
- }
17
- const app = config.apps.find(a => a.name === appName);
18
- if (!app) {
19
- callback?.({ success: false, error: `App "${appName}" not found in project config` });
20
- return;
21
- }
22
- const result = await startApp(projectPath, projectId, app);
23
- if (result.success) {
24
- if (foreground)
25
- console.log(`✓ Started app: ${appName} (PID: ${result.pid})`);
26
- socket.emit('app:started', {
27
- projectId,
28
- appName,
29
- processId: result.processId,
30
- pid: result.pid,
31
- });
32
- }
33
- callback?.(result);
34
- }
35
- catch (error) {
36
- if (foreground)
37
- console.error(`✗ Error starting app: ${error.message}`);
38
- callback?.({ success: false, error: error.message });
39
- }
40
- });
41
- socket.on('app:stop', async (data, callback) => {
42
- try {
43
- const { projectId, appName } = data;
44
- const config = await getProjectConfig(data.projectPath);
45
- const app = config?.apps.find(a => a.name === appName);
46
- const result = await stopApp(projectId, appName, app);
47
- if (result.success) {
48
- if (foreground)
49
- console.log(`✓ Stopped app: ${appName}`);
50
- socket.emit('app:stopped', {
51
- projectId,
52
- appName,
53
- appControlId: data.appControlId,
54
- });
55
- }
56
- callback?.(result);
57
- }
58
- catch (error) {
59
- if (foreground)
60
- console.error(`✗ Error stopping app: ${error.message}`);
61
- callback?.({ success: false, error: error.message });
62
- }
63
- });
64
- socket.on('app:restart', async (data, callback) => {
65
- try {
66
- const { projectId, projectPath, appName } = data;
67
- const config = await getProjectConfig(projectPath);
68
- if (!config) {
69
- callback?.({ success: false, error: 'Project config not found' });
70
- return;
71
- }
72
- const app = config.apps.find(a => a.name === appName);
73
- if (!app) {
74
- callback?.({ success: false, error: `App "${appName}" not found in project config` });
75
- return;
76
- }
77
- const result = await restartApp(projectPath, projectId, app);
78
- if (result.success) {
79
- if (foreground)
80
- console.log(`✓ Restarted app: ${appName} (PID: ${result.pid})`);
81
- socket.emit('app:restarted', {
82
- projectId,
83
- appName,
84
- processId: result.processId,
85
- pid: result.pid,
86
- appControlId: data.appControlId,
87
- });
88
- }
89
- callback?.(result);
90
- }
91
- catch (error) {
92
- if (foreground)
93
- console.error(`✗ Error restarting app: ${error.message}`);
94
- callback?.({ success: false, error: error.message });
95
- }
96
- });
97
- socket.on('app:status', async (data, callback) => {
98
- try {
99
- const { projectId, projectPath, appName } = data;
100
- const config = await getProjectConfig(projectPath);
101
- if (!config) {
102
- callback?.({ success: false, error: 'Project config not found' });
103
- return;
104
- }
105
- const appsToCheck = appName
106
- ? config.apps.filter(a => a.name === appName)
107
- : config.apps;
108
- const statuses = getAppStatuses(projectId, appsToCheck);
109
- if (data.appControlId) {
110
- socket.emit('app:control:response', {
111
- appControlId: data.appControlId,
112
- success: true,
113
- apps: statuses,
114
- });
115
- }
116
- callback?.({ success: true, apps: statuses });
117
- }
118
- catch (error) {
119
- if (foreground)
120
- console.error(`✗ Error getting app status: ${error.message}`);
121
- callback?.({ success: false, error: error.message });
122
- }
123
- });
124
- socket.on('app:logs', async (data, callback) => {
125
- try {
126
- const { projectId, appName } = data;
127
- const result = await getAppLogs(projectId, appName, 100);
128
- if (data.appControlId) {
129
- socket.emit('app:control:response', {
130
- appControlId: data.appControlId,
131
- ...result,
132
- });
133
- }
134
- callback?.(result);
135
- }
136
- catch (error) {
137
- if (foreground)
138
- console.error(`✗ Error getting app logs: ${error.message}`);
139
- callback?.({ success: false, error: error.message });
140
- }
141
- });
142
- }
@@ -1,212 +0,0 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import os from 'os';
4
- import { createRunner } from './appRunner.js';
5
- // Store running app runners: projectId -> appName -> runner
6
- const runningApps = new Map();
7
- const LOGS_DIR = path.join(os.homedir(), '.talk-to-code', 'app-logs');
8
- /**
9
- * Ensure logs directory exists
10
- */
11
- const ensureLogsDir = () => fs.mkdir(LOGS_DIR, { recursive: true });
12
- /**
13
- * Helper to remove app from running apps and clean up empty project maps
14
- */
15
- const removeAppFromRunning = (projectId, appName) => {
16
- const projectApps = runningApps.get(projectId);
17
- if (projectApps) {
18
- projectApps.delete(appName);
19
- if (projectApps.size === 0) {
20
- runningApps.delete(projectId);
21
- }
22
- }
23
- };
24
- /**
25
- * Get log file path for an app
26
- */
27
- function getLogFilePath(projectId, appName) {
28
- return path.join(LOGS_DIR, `${projectId}-${appName}.log`);
29
- }
30
- /**
31
- * Start an app using wrapper runner
32
- */
33
- export async function startApp(projectPath, projectId, app) {
34
- try {
35
- // Check if already running
36
- if (isAppRunning(projectId, app.name)) {
37
- const running = getRunningApp(projectId, app.name);
38
- return {
39
- success: true,
40
- processId: `${projectId}-${app.name}`,
41
- pid: running?.pid
42
- };
43
- }
44
- // Ensure logs directory exists
45
- await ensureLogsDir();
46
- const logFile = getLogFilePath(projectId, app.name);
47
- // Create runner based on app type
48
- const runner = createRunner(app, projectPath, projectId, {
49
- onOutput: async (_output) => {
50
- // Logs are handled by the runner itself
51
- },
52
- onError: (_error) => {
53
- // Errors are logged by runner
54
- },
55
- onExit: (_code) => {
56
- removeAppFromRunning(projectId, app.name);
57
- },
58
- onStats: (_stats) => {
59
- // Stats updates can be used for monitoring
60
- },
61
- });
62
- // Start the runner
63
- const result = await runner.start();
64
- if (!result.success) {
65
- return result;
66
- }
67
- // Store runner info
68
- if (!runningApps.has(projectId)) {
69
- runningApps.set(projectId, new Map());
70
- }
71
- runningApps.get(projectId).set(app.name, {
72
- runner,
73
- pid: result.pid,
74
- startTime: Date.now(),
75
- logFile,
76
- });
77
- return {
78
- success: true,
79
- processId: `${projectId}-${app.name}`,
80
- pid: result.pid,
81
- };
82
- }
83
- catch (error) {
84
- return {
85
- success: false,
86
- error: error.message || 'Failed to start app'
87
- };
88
- }
89
- }
90
- /**
91
- * Stop an app using wrapper runner
92
- */
93
- export async function stopApp(projectId, appName, _app) {
94
- try {
95
- const running = getRunningApp(projectId, appName);
96
- if (!running) {
97
- return {
98
- success: false,
99
- error: 'App is not running'
100
- };
101
- }
102
- // Stop using runner
103
- const result = await running.runner.stop();
104
- if (result.success) {
105
- removeAppFromRunning(projectId, appName);
106
- }
107
- return result;
108
- }
109
- catch (error) {
110
- return {
111
- success: false,
112
- error: error.message || 'Failed to stop app'
113
- };
114
- }
115
- }
116
- /**
117
- * Restart an app
118
- */
119
- export async function restartApp(projectPath, projectId, app) {
120
- // Stop first if running
121
- if (isAppRunning(projectId, app.name)) {
122
- const stopResult = await stopApp(projectId, app.name, app);
123
- if (!stopResult.success) {
124
- return stopResult;
125
- }
126
- // Wait a bit before restarting
127
- await new Promise(resolve => setTimeout(resolve, 1000));
128
- }
129
- // Start
130
- return startApp(projectPath, projectId, app);
131
- }
132
- /**
133
- * Check if an app is running
134
- */
135
- export function isAppRunning(projectId, appName) {
136
- const projectApps = runningApps.get(projectId);
137
- if (!projectApps)
138
- return false;
139
- const app = projectApps.get(appName);
140
- if (!app)
141
- return false;
142
- // Check if process is still alive
143
- try {
144
- process.kill(app.pid, 0); // Signal 0 just checks if process exists
145
- return true;
146
- }
147
- catch {
148
- // Process doesn't exist, clean up
149
- removeAppFromRunning(projectId, appName);
150
- return false;
151
- }
152
- }
153
- /**
154
- * Get running app info
155
- */
156
- export function getRunningApp(projectId, appName) {
157
- const projectApps = runningApps.get(projectId);
158
- if (!projectApps)
159
- return null;
160
- const app = projectApps.get(appName);
161
- if (!app)
162
- return null;
163
- // Verify process is still alive
164
- if (!isAppRunning(projectId, appName)) {
165
- return null;
166
- }
167
- return app;
168
- }
169
- /**
170
- * Get status of all apps for a project
171
- */
172
- export function getAppStatuses(projectId, apps) {
173
- return apps.map(app => {
174
- const running = getRunningApp(projectId, app.name);
175
- return {
176
- name: app.name,
177
- running: !!running,
178
- processId: running ? `${projectId}-${app.name}` : undefined,
179
- pid: running?.pid,
180
- };
181
- });
182
- }
183
- /**
184
- * Get logs for an app
185
- */
186
- export async function getAppLogs(projectId, appName, lines = 100) {
187
- try {
188
- const logFile = getLogFilePath(projectId, appName);
189
- try {
190
- await fs.access(logFile);
191
- }
192
- catch {
193
- return {
194
- success: true,
195
- logs: 'No logs available yet'
196
- };
197
- }
198
- const content = await fs.readFile(logFile, 'utf-8');
199
- const logLines = content.split('\n');
200
- const recentLogs = logLines.slice(-lines).join('\n');
201
- return {
202
- success: true,
203
- logs: recentLogs || 'No logs available'
204
- };
205
- }
206
- catch (error) {
207
- return {
208
- success: false,
209
- error: error.message || 'Failed to read logs'
210
- };
211
- }
212
- }