@gxp-dev/tools 2.0.63 → 2.0.64
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/README.md +32 -31
- package/bin/gx-devtools.js +74 -54
- package/bin/lib/cli.js +23 -21
- package/bin/lib/commands/add-dependency.js +366 -325
- package/bin/lib/commands/assets.js +137 -139
- package/bin/lib/commands/build.js +169 -174
- package/bin/lib/commands/datastore.js +181 -183
- package/bin/lib/commands/dev.js +127 -131
- package/bin/lib/commands/extensions.js +147 -149
- package/bin/lib/commands/extract-config.js +73 -67
- package/bin/lib/commands/index.js +12 -12
- package/bin/lib/commands/init.js +342 -240
- package/bin/lib/commands/publish.js +69 -75
- package/bin/lib/commands/socket.js +69 -69
- package/bin/lib/commands/ssl.js +14 -14
- package/bin/lib/constants.js +10 -24
- package/bin/lib/tui/App.tsx +761 -705
- package/bin/lib/tui/components/AIPanel.tsx +191 -171
- package/bin/lib/tui/components/CommandInput.tsx +394 -343
- package/bin/lib/tui/components/GeminiPanel.tsx +175 -151
- package/bin/lib/tui/components/Header.tsx +23 -21
- package/bin/lib/tui/components/LogPanel.tsx +244 -220
- package/bin/lib/tui/components/TabBar.tsx +50 -48
- package/bin/lib/tui/components/WelcomeScreen.tsx +126 -71
- package/bin/lib/tui/index.tsx +37 -39
- package/bin/lib/tui/services/AIService.ts +518 -462
- package/bin/lib/tui/services/ExtensionService.ts +140 -129
- package/bin/lib/tui/services/GeminiService.ts +367 -337
- package/bin/lib/tui/services/ServiceManager.ts +344 -322
- package/bin/lib/tui/services/SocketService.ts +168 -168
- package/bin/lib/tui/services/ViteService.ts +88 -88
- package/bin/lib/tui/services/index.ts +47 -22
- package/bin/lib/utils/ai-scaffold.js +291 -280
- package/bin/lib/utils/extract-config.js +157 -140
- package/bin/lib/utils/files.js +82 -86
- package/bin/lib/utils/index.js +7 -7
- package/bin/lib/utils/paths.js +34 -34
- package/bin/lib/utils/prompts.js +194 -169
- package/bin/lib/utils/ssl.js +79 -81
- package/browser-extensions/README.md +0 -1
- package/browser-extensions/chrome/background.js +244 -237
- package/browser-extensions/chrome/content.js +32 -29
- package/browser-extensions/chrome/devtools.html +7 -7
- package/browser-extensions/chrome/devtools.js +19 -19
- package/browser-extensions/chrome/inspector.js +802 -767
- package/browser-extensions/chrome/manifest.json +71 -63
- package/browser-extensions/chrome/panel.html +674 -636
- package/browser-extensions/chrome/panel.js +722 -712
- package/browser-extensions/chrome/popup.html +586 -543
- package/browser-extensions/chrome/popup.js +282 -244
- package/browser-extensions/chrome/rules.json +1 -1
- package/browser-extensions/chrome/test-chrome.html +216 -136
- package/browser-extensions/chrome/test-mixed-content.html +284 -189
- package/browser-extensions/chrome/test-uri-pattern.html +221 -198
- package/browser-extensions/firefox/README.md +9 -6
- package/browser-extensions/firefox/background.js +221 -218
- package/browser-extensions/firefox/content.js +55 -52
- package/browser-extensions/firefox/debug-errors.html +386 -228
- package/browser-extensions/firefox/debug-https.html +153 -105
- package/browser-extensions/firefox/devtools.html +7 -7
- package/browser-extensions/firefox/devtools.js +23 -20
- package/browser-extensions/firefox/inspector.js +802 -767
- package/browser-extensions/firefox/manifest.json +68 -68
- package/browser-extensions/firefox/panel.html +674 -636
- package/browser-extensions/firefox/panel.js +722 -712
- package/browser-extensions/firefox/popup.html +572 -535
- package/browser-extensions/firefox/popup.js +281 -236
- package/browser-extensions/firefox/test-gramercy.html +170 -125
- package/browser-extensions/firefox/test-imports.html +59 -55
- package/browser-extensions/firefox/test-masking.html +231 -140
- package/browser-extensions/firefox/test-uri-pattern.html +221 -198
- package/dist/tui/App.d.ts +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +154 -150
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/AIPanel.d.ts.map +1 -1
- package/dist/tui/components/AIPanel.js +42 -35
- package/dist/tui/components/AIPanel.js.map +1 -1
- package/dist/tui/components/CommandInput.d.ts +1 -1
- package/dist/tui/components/CommandInput.d.ts.map +1 -1
- package/dist/tui/components/CommandInput.js +92 -62
- package/dist/tui/components/CommandInput.js.map +1 -1
- package/dist/tui/components/GeminiPanel.d.ts.map +1 -1
- package/dist/tui/components/GeminiPanel.js +37 -30
- package/dist/tui/components/GeminiPanel.js.map +1 -1
- package/dist/tui/components/Header.d.ts.map +1 -1
- package/dist/tui/components/Header.js +1 -1
- package/dist/tui/components/Header.js.map +1 -1
- package/dist/tui/components/LogPanel.d.ts +1 -1
- package/dist/tui/components/LogPanel.d.ts.map +1 -1
- package/dist/tui/components/LogPanel.js +26 -24
- package/dist/tui/components/LogPanel.js.map +1 -1
- package/dist/tui/components/TabBar.d.ts +2 -2
- package/dist/tui/components/TabBar.d.ts.map +1 -1
- package/dist/tui/components/TabBar.js +11 -11
- package/dist/tui/components/TabBar.js.map +1 -1
- package/dist/tui/components/WelcomeScreen.d.ts.map +1 -1
- package/dist/tui/components/WelcomeScreen.js +6 -6
- package/dist/tui/components/WelcomeScreen.js.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +8 -8
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/services/AIService.d.ts +2 -2
- package/dist/tui/services/AIService.d.ts.map +1 -1
- package/dist/tui/services/AIService.js +165 -125
- package/dist/tui/services/AIService.js.map +1 -1
- package/dist/tui/services/ExtensionService.d.ts +1 -1
- package/dist/tui/services/ExtensionService.d.ts.map +1 -1
- package/dist/tui/services/ExtensionService.js +33 -26
- package/dist/tui/services/ExtensionService.js.map +1 -1
- package/dist/tui/services/GeminiService.d.ts +1 -1
- package/dist/tui/services/GeminiService.d.ts.map +1 -1
- package/dist/tui/services/GeminiService.js +87 -76
- package/dist/tui/services/GeminiService.js.map +1 -1
- package/dist/tui/services/ServiceManager.d.ts +3 -3
- package/dist/tui/services/ServiceManager.d.ts.map +1 -1
- package/dist/tui/services/ServiceManager.js +72 -58
- package/dist/tui/services/ServiceManager.js.map +1 -1
- package/dist/tui/services/SocketService.d.ts.map +1 -1
- package/dist/tui/services/SocketService.js +32 -32
- package/dist/tui/services/SocketService.js.map +1 -1
- package/dist/tui/services/ViteService.d.ts.map +1 -1
- package/dist/tui/services/ViteService.js +26 -28
- package/dist/tui/services/ViteService.js.map +1 -1
- package/dist/tui/services/index.d.ts +6 -6
- package/dist/tui/services/index.d.ts.map +1 -1
- package/dist/tui/services/index.js +6 -6
- package/dist/tui/services/index.js.map +1 -1
- package/mcp/gxp-api-server.js +83 -81
- package/package.json +109 -93
- package/runtime/PortalContainer.vue +258 -234
- package/runtime/dev-tools/DevToolsModal.vue +153 -155
- package/runtime/dev-tools/LayoutSwitcher.vue +144 -140
- package/runtime/dev-tools/MockDataEditor.vue +456 -433
- package/runtime/dev-tools/SocketSimulator.vue +379 -371
- package/runtime/dev-tools/StoreInspector.vue +517 -455
- package/runtime/dev-tools/index.js +5 -5
- package/runtime/fallback-layouts/PrivateLayout.vue +2 -2
- package/runtime/fallback-layouts/PublicLayout.vue +2 -2
- package/runtime/fallback-layouts/SystemLayout.vue +2 -2
- package/runtime/gxpStringsPlugin.js +159 -134
- package/runtime/index.html +17 -19
- package/runtime/main.js +24 -22
- package/runtime/mock-api/auth-middleware.js +15 -15
- package/runtime/mock-api/image-generator.js +46 -46
- package/runtime/mock-api/index.js +55 -55
- package/runtime/mock-api/response-generator.js +116 -105
- package/runtime/mock-api/route-generator.js +107 -84
- package/runtime/mock-api/socket-triggers.js +94 -93
- package/runtime/mock-api/spec-loader.js +79 -80
- package/runtime/package.json +3 -0
- package/runtime/server.js +68 -68
- package/runtime/stores/gxpPortalConfigStore.js +204 -186
- package/runtime/stores/index.js +2 -2
- package/runtime/vite-inspector-plugin.js +858 -707
- package/runtime/vite-source-tracker-plugin.js +132 -113
- package/runtime/vite.config.js +191 -139
- package/scripts/launch-chrome.js +41 -41
- package/scripts/pack-chrome.js +38 -39
- package/socket-events/AiSessionMessageCreated.json +17 -17
- package/socket-events/SocialStreamPostCreated.json +23 -23
- package/socket-events/SocialStreamPostVariantCompleted.json +22 -22
- package/template/.claude/agents/gxp-developer.md +100 -99
- package/template/.claude/settings.json +7 -7
- package/template/AGENTS.md +30 -23
- package/template/GEMINI.md +20 -20
- package/template/README.md +70 -53
- package/template/app-manifest.json +2 -4
- package/template/configuration.json +10 -10
- package/template/default-styling.css +1 -1
- package/template/index.html +18 -20
- package/template/main.js +24 -22
- package/template/src/DemoPage.vue +415 -362
- package/template/src/Plugin.vue +76 -85
- package/template/src/stores/index.js +3 -3
- package/template/src/stores/test-data.json +164 -172
- package/template/theme-layouts/AdditionalStyling.css +50 -50
- package/template/theme-layouts/PrivateLayout.vue +8 -12
- package/template/theme-layouts/PublicLayout.vue +8 -12
- package/template/theme-layouts/SystemLayout.vue +8 -12
- package/template/vite.extend.js +45 -0
- package/template/vite.config.js +0 -409
|
@@ -1,336 +1,358 @@
|
|
|
1
|
-
import { spawn, ChildProcess, execSync } from
|
|
2
|
-
import { EventEmitter } from
|
|
1
|
+
import { spawn, ChildProcess, execSync } from "child_process"
|
|
2
|
+
import { EventEmitter } from "events"
|
|
3
3
|
|
|
4
|
-
export type ServiceStatus =
|
|
4
|
+
export type ServiceStatus = "stopped" | "starting" | "running" | "error"
|
|
5
5
|
|
|
6
6
|
export interface ServiceConfig {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
id: string
|
|
8
|
+
name: string
|
|
9
|
+
command: string
|
|
10
|
+
args: string[]
|
|
11
|
+
cwd: string
|
|
12
|
+
env?: Record<string, string>
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// Store configs for restart capability
|
|
16
|
-
const serviceConfigs: Map<string, ServiceConfig> = new Map()
|
|
16
|
+
const serviceConfigs: Map<string, ServiceConfig> = new Map()
|
|
17
17
|
|
|
18
18
|
export interface ServiceState {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
id: string
|
|
20
|
+
name: string
|
|
21
|
+
status: ServiceStatus
|
|
22
|
+
logs: string[]
|
|
23
|
+
process?: ChildProcess
|
|
24
|
+
pid?: number
|
|
25
|
+
error?: string
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export class ServiceManager extends EventEmitter {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
29
|
+
private services: Map<string, ServiceState> = new Map()
|
|
30
|
+
private maxLogLines = 1000
|
|
31
|
+
private cleanupRegistered = false
|
|
32
|
+
|
|
33
|
+
constructor() {
|
|
34
|
+
super()
|
|
35
|
+
this.registerCleanupHandlers()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Register cleanup handlers to kill all services on exit
|
|
39
|
+
private registerCleanupHandlers(): void {
|
|
40
|
+
if (this.cleanupRegistered) return
|
|
41
|
+
this.cleanupRegistered = true
|
|
42
|
+
|
|
43
|
+
const cleanup = () => {
|
|
44
|
+
this.forceStopAll()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Handle various exit scenarios
|
|
48
|
+
process.on("exit", cleanup)
|
|
49
|
+
process.on("SIGINT", cleanup)
|
|
50
|
+
process.on("SIGTERM", cleanup)
|
|
51
|
+
process.on("SIGHUP", cleanup)
|
|
52
|
+
process.on("beforeExit", cleanup)
|
|
53
|
+
|
|
54
|
+
// Handle uncaught exceptions
|
|
55
|
+
process.on("uncaughtException", (err) => {
|
|
56
|
+
console.error("Uncaught exception:", err)
|
|
57
|
+
cleanup()
|
|
58
|
+
process.exit(1)
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getService(id: string): ServiceState | undefined {
|
|
63
|
+
return this.services.get(id)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getAllServices(): ServiceState[] {
|
|
67
|
+
return Array.from(this.services.values())
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
isRunning(id: string): boolean {
|
|
71
|
+
const service = this.services.get(id)
|
|
72
|
+
return service?.status === "running" || service?.status === "starting"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
start(config: ServiceConfig): ServiceState {
|
|
76
|
+
// Store config for restart capability
|
|
77
|
+
serviceConfigs.set(config.id, config)
|
|
78
|
+
|
|
79
|
+
// Check if already running
|
|
80
|
+
const existing = this.services.get(config.id)
|
|
81
|
+
if (
|
|
82
|
+
existing &&
|
|
83
|
+
(existing.status === "running" || existing.status === "starting")
|
|
84
|
+
) {
|
|
85
|
+
this.addLog(config.id, `[${config.name}] Already running`)
|
|
86
|
+
return existing
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Create or reset service state
|
|
90
|
+
const state: ServiceState = {
|
|
91
|
+
id: config.id,
|
|
92
|
+
name: config.name,
|
|
93
|
+
status: "starting",
|
|
94
|
+
logs: existing?.logs || [],
|
|
95
|
+
}
|
|
96
|
+
this.services.set(config.id, state)
|
|
97
|
+
|
|
98
|
+
this.addLog(config.id, `[${config.name}] Starting...`)
|
|
99
|
+
this.emit("statusChange", config.id, "starting")
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Spawn the process with environment variables to prevent stdin access
|
|
103
|
+
// CI=true disables interactive prompts in many tools
|
|
104
|
+
// These prevent child processes from trying to use raw stdin mode
|
|
105
|
+
// detached: true creates a new process group that doesn't share the parent's tty
|
|
106
|
+
const proc = spawn(config.command, config.args, {
|
|
107
|
+
cwd: config.cwd,
|
|
108
|
+
env: {
|
|
109
|
+
...process.env,
|
|
110
|
+
...config.env,
|
|
111
|
+
FORCE_COLOR: "1",
|
|
112
|
+
CI: "true",
|
|
113
|
+
},
|
|
114
|
+
shell: true,
|
|
115
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
116
|
+
detached: true,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
state.process = proc
|
|
120
|
+
state.pid = proc.pid
|
|
121
|
+
|
|
122
|
+
// Handle stdout
|
|
123
|
+
proc.stdout?.on("data", (data: Buffer) => {
|
|
124
|
+
const lines = data
|
|
125
|
+
.toString()
|
|
126
|
+
.split("\n")
|
|
127
|
+
.filter((line) => line.trim())
|
|
128
|
+
lines.forEach((line) => {
|
|
129
|
+
this.addLog(config.id, line)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
// Detect when service is ready
|
|
133
|
+
const output = data.toString()
|
|
134
|
+
if (state.status === "starting") {
|
|
135
|
+
// Vite ready indicators
|
|
136
|
+
if (
|
|
137
|
+
output.includes("ready in") ||
|
|
138
|
+
output.includes("Local:") ||
|
|
139
|
+
output.includes("VITE")
|
|
140
|
+
) {
|
|
141
|
+
state.status = "running"
|
|
142
|
+
this.emit("statusChange", config.id, "running")
|
|
143
|
+
}
|
|
144
|
+
// Socket.IO ready indicator
|
|
145
|
+
if (
|
|
146
|
+
output.includes("Socket.IO server") ||
|
|
147
|
+
output.includes("listening on port")
|
|
148
|
+
) {
|
|
149
|
+
state.status = "running"
|
|
150
|
+
this.emit("statusChange", config.id, "running")
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// Handle stderr
|
|
156
|
+
proc.stderr?.on("data", (data: Buffer) => {
|
|
157
|
+
const lines = data
|
|
158
|
+
.toString()
|
|
159
|
+
.split("\n")
|
|
160
|
+
.filter((line) => line.trim())
|
|
161
|
+
lines.forEach((line) => {
|
|
162
|
+
this.addLog(config.id, `[stderr] ${line}`)
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
// Handle process exit
|
|
167
|
+
proc.on("close", (code) => {
|
|
168
|
+
state.process = undefined
|
|
169
|
+
if (code === 0 || code === null) {
|
|
170
|
+
state.status = "stopped"
|
|
171
|
+
this.addLog(config.id, `[${config.name}] Stopped`)
|
|
172
|
+
} else {
|
|
173
|
+
state.status = "error"
|
|
174
|
+
state.error = `Process exited with code ${code}`
|
|
175
|
+
this.addLog(
|
|
176
|
+
config.id,
|
|
177
|
+
`[${config.name}] Error: exited with code ${code}`,
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
this.emit("statusChange", config.id, state.status)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
// Handle spawn errors
|
|
184
|
+
proc.on("error", (err) => {
|
|
185
|
+
state.status = "error"
|
|
186
|
+
state.error = err.message
|
|
187
|
+
state.process = undefined
|
|
188
|
+
this.addLog(config.id, `[${config.name}] Error: ${err.message}`)
|
|
189
|
+
this.emit("statusChange", config.id, "error")
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// Set running after a short delay if no ready message detected
|
|
193
|
+
setTimeout(() => {
|
|
194
|
+
if (state.status === "starting" && state.process) {
|
|
195
|
+
state.status = "running"
|
|
196
|
+
this.emit("statusChange", config.id, "running")
|
|
197
|
+
}
|
|
198
|
+
}, 3000)
|
|
199
|
+
} catch (err) {
|
|
200
|
+
state.status = "error"
|
|
201
|
+
state.error = err instanceof Error ? err.message : "Unknown error"
|
|
202
|
+
this.addLog(config.id, `[${config.name}] Failed to start: ${state.error}`)
|
|
203
|
+
this.emit("statusChange", config.id, "error")
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return state
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
stop(id: string): boolean {
|
|
210
|
+
const service = this.services.get(id)
|
|
211
|
+
if (!service || !service.process) {
|
|
212
|
+
return false
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.addLog(id, `[${service.name}] Stopping...`)
|
|
216
|
+
|
|
217
|
+
// Kill the process tree
|
|
218
|
+
try {
|
|
219
|
+
process.kill(-service.process.pid!, "SIGTERM")
|
|
220
|
+
} catch {
|
|
221
|
+
// Process group kill failed, try direct kill
|
|
222
|
+
service.process.kill("SIGTERM")
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Force kill after timeout
|
|
226
|
+
setTimeout(() => {
|
|
227
|
+
if (service.process && !service.process.killed) {
|
|
228
|
+
try {
|
|
229
|
+
process.kill(-service.process.pid!, "SIGKILL")
|
|
230
|
+
} catch {
|
|
231
|
+
service.process.kill("SIGKILL")
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}, 2000)
|
|
235
|
+
|
|
236
|
+
return true
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
stopAll(): void {
|
|
240
|
+
for (const [id] of this.services) {
|
|
241
|
+
this.stop(id)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Force stop all services synchronously - used during process exit
|
|
246
|
+
forceStopAll(): void {
|
|
247
|
+
for (const [id, service] of this.services) {
|
|
248
|
+
if (service.pid) {
|
|
249
|
+
try {
|
|
250
|
+
// Try to kill the process group first
|
|
251
|
+
process.kill(-service.pid, "SIGKILL")
|
|
252
|
+
} catch {
|
|
253
|
+
// Process group kill failed, try direct kill
|
|
254
|
+
try {
|
|
255
|
+
process.kill(service.pid, "SIGKILL")
|
|
256
|
+
} catch {
|
|
257
|
+
// Process may already be dead
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (service.process && !service.process.killed) {
|
|
262
|
+
try {
|
|
263
|
+
service.process.kill("SIGKILL")
|
|
264
|
+
} catch {
|
|
265
|
+
// Ignore errors
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Also try to kill any orphaned vite/nodemon processes using lsof on common ports
|
|
271
|
+
try {
|
|
272
|
+
// Kill processes on typical dev ports synchronously
|
|
273
|
+
execSync("lsof -ti :3060 | xargs kill -9 2>/dev/null || true", {
|
|
274
|
+
stdio: "ignore",
|
|
275
|
+
})
|
|
276
|
+
execSync("lsof -ti :3069 | xargs kill -9 2>/dev/null || true", {
|
|
277
|
+
stdio: "ignore",
|
|
278
|
+
})
|
|
279
|
+
} catch {
|
|
280
|
+
// Ignore errors - best effort cleanup
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
restart(id: string): boolean {
|
|
285
|
+
const config = serviceConfigs.get(id)
|
|
286
|
+
if (!config) {
|
|
287
|
+
return false
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const service = this.services.get(id)
|
|
291
|
+
if (service?.process) {
|
|
292
|
+
// Stop first, then restart after process exits
|
|
293
|
+
this.addLog(id, `[${config.name}] Restarting...`)
|
|
294
|
+
|
|
295
|
+
const onExit = () => {
|
|
296
|
+
// Small delay to ensure cleanup
|
|
297
|
+
setTimeout(() => {
|
|
298
|
+
this.start(config)
|
|
299
|
+
}, 500)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Listen for process exit once
|
|
303
|
+
service.process.once("close", onExit)
|
|
304
|
+
|
|
305
|
+
// Kill the process
|
|
306
|
+
try {
|
|
307
|
+
process.kill(-service.process.pid!, "SIGTERM")
|
|
308
|
+
} catch {
|
|
309
|
+
service.process.kill("SIGTERM")
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Force kill after timeout
|
|
313
|
+
setTimeout(() => {
|
|
314
|
+
if (service.process && !service.process.killed) {
|
|
315
|
+
try {
|
|
316
|
+
process.kill(-service.process.pid!, "SIGKILL")
|
|
317
|
+
} catch {
|
|
318
|
+
service.process.kill("SIGKILL")
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}, 2000)
|
|
322
|
+
} else {
|
|
323
|
+
// Not running, just start
|
|
324
|
+
this.start(config)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return true
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
getConfig(id: string): ServiceConfig | undefined {
|
|
331
|
+
return serviceConfigs.get(id)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
clearLogs(id: string): void {
|
|
335
|
+
const service = this.services.get(id)
|
|
336
|
+
if (service) {
|
|
337
|
+
service.logs = []
|
|
338
|
+
this.emit("logsCleared", id)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private addLog(id: string, message: string): void {
|
|
343
|
+
const service = this.services.get(id)
|
|
344
|
+
if (!service) return
|
|
345
|
+
|
|
346
|
+
service.logs.push(message)
|
|
347
|
+
|
|
348
|
+
// Trim logs if too many
|
|
349
|
+
if (service.logs.length > this.maxLogLines) {
|
|
350
|
+
service.logs = service.logs.slice(-this.maxLogLines)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
this.emit("log", id, message)
|
|
354
|
+
}
|
|
333
355
|
}
|
|
334
356
|
|
|
335
357
|
// Singleton instance
|
|
336
|
-
export const serviceManager = new ServiceManager()
|
|
358
|
+
export const serviceManager = new ServiceManager()
|