@gxp-dev/tools 2.0.63 → 2.0.65
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
package/bin/lib/tui/App.tsx
CHANGED
|
@@ -1,669 +1,718 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback } from
|
|
2
|
-
import { Box, Text, useApp, useInput, useStdout } from
|
|
3
|
-
import WelcomeScreen from
|
|
4
|
-
import Header from
|
|
5
|
-
import TabBar from
|
|
6
|
-
import LogPanel from
|
|
7
|
-
import CommandInput from
|
|
8
|
-
import AIPanel from
|
|
1
|
+
import React, { useState, useEffect, useCallback } from "react"
|
|
2
|
+
import { Box, Text, useApp, useInput, useStdout } from "ink"
|
|
3
|
+
import WelcomeScreen from "./components/WelcomeScreen.js"
|
|
4
|
+
import Header from "./components/Header.js"
|
|
5
|
+
import TabBar from "./components/TabBar.js"
|
|
6
|
+
import LogPanel from "./components/LogPanel.js"
|
|
7
|
+
import CommandInput from "./components/CommandInput.js"
|
|
8
|
+
import AIPanel from "./components/AIPanel.js"
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} from
|
|
10
|
+
serviceManager,
|
|
11
|
+
startVite,
|
|
12
|
+
stopVite,
|
|
13
|
+
startSocket,
|
|
14
|
+
stopSocket,
|
|
15
|
+
startExtension,
|
|
16
|
+
stopExtension,
|
|
17
|
+
listSocketEvents,
|
|
18
|
+
sendSocketEvent,
|
|
19
|
+
ServiceStatus,
|
|
20
|
+
BrowserType,
|
|
21
|
+
aiService,
|
|
22
|
+
getAvailableProviders,
|
|
23
|
+
getProviderStatus,
|
|
24
|
+
AIProvider,
|
|
25
|
+
} from "./services/index.js"
|
|
26
26
|
|
|
27
27
|
export interface Service {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
id: string
|
|
29
|
+
name: string
|
|
30
|
+
status: ServiceStatus
|
|
31
|
+
logs: string[]
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
interface ExtractedConfig {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
strings: Record<string, string>
|
|
36
|
+
settings: Record<string, unknown>
|
|
37
|
+
assets: Record<string, string>
|
|
38
|
+
triggerState: Record<string, unknown>
|
|
39
|
+
dependencies: Array<{
|
|
40
|
+
identifier: string
|
|
41
|
+
path: string
|
|
42
|
+
events?: Record<string, string>
|
|
43
|
+
}>
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
export interface AppProps {
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
autoStart?: string[]
|
|
48
|
+
args?: Record<string, unknown>
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
export default function App({ autoStart, args }: AppProps) {
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
52
|
+
const { exit } = useApp()
|
|
53
|
+
const { stdout } = useStdout()
|
|
54
|
+
const [showAIPanel, setShowAIPanel] = useState(false)
|
|
55
|
+
const [services, setServices] = useState<Service[]>([])
|
|
56
|
+
const [activeTab, setActiveTab] = useState(0)
|
|
57
|
+
const [suggestionRows, setSuggestionRows] = useState(0)
|
|
58
|
+
|
|
59
|
+
// Get terminal height for full screen
|
|
60
|
+
const terminalHeight = stdout?.rows || 24
|
|
61
|
+
|
|
62
|
+
// Stable callback for suggestion row changes to prevent unnecessary re-renders
|
|
63
|
+
const handleSuggestionsChange = useCallback((count: number) => {
|
|
64
|
+
setSuggestionRows(count)
|
|
65
|
+
}, [])
|
|
66
|
+
|
|
67
|
+
// Sync services from ServiceManager
|
|
68
|
+
const syncServices = useCallback(() => {
|
|
69
|
+
const managerServices = serviceManager.getAllServices()
|
|
70
|
+
setServices(
|
|
71
|
+
managerServices.map((s) => ({
|
|
72
|
+
id: s.id,
|
|
73
|
+
name: s.name,
|
|
74
|
+
status: s.status,
|
|
75
|
+
logs: s.logs,
|
|
76
|
+
})),
|
|
77
|
+
)
|
|
78
|
+
}, [])
|
|
79
|
+
|
|
80
|
+
// Set up ServiceManager event listeners
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const onLog = (id: string, message: string) => {
|
|
83
|
+
setServices((prev) =>
|
|
84
|
+
prev.map((s) =>
|
|
85
|
+
s.id === id ? { ...s, logs: [...s.logs, message] } : s,
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const onStatusChange = (id: string, status: ServiceStatus) => {
|
|
91
|
+
setServices((prev) =>
|
|
92
|
+
prev.map((s) => (s.id === id ? { ...s, status } : s)),
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const onLogsCleared = (id: string) => {
|
|
97
|
+
setServices((prev) =>
|
|
98
|
+
prev.map((s) => (s.id === id ? { ...s, logs: [] } : s)),
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
serviceManager.on("log", onLog)
|
|
103
|
+
serviceManager.on("statusChange", onStatusChange)
|
|
104
|
+
serviceManager.on("logsCleared", onLogsCleared)
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
serviceManager.off("log", onLog)
|
|
108
|
+
serviceManager.off("statusChange", onStatusChange)
|
|
109
|
+
serviceManager.off("logsCleared", onLogsCleared)
|
|
110
|
+
}
|
|
111
|
+
}, [])
|
|
112
|
+
|
|
113
|
+
// Cleanup on exit
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
const cleanup = () => {
|
|
116
|
+
serviceManager.forceStopAll()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
process.on("SIGINT", cleanup)
|
|
120
|
+
process.on("SIGTERM", cleanup)
|
|
121
|
+
|
|
122
|
+
return () => {
|
|
123
|
+
cleanup()
|
|
124
|
+
process.off("SIGINT", cleanup)
|
|
125
|
+
process.off("SIGTERM", cleanup)
|
|
126
|
+
}
|
|
127
|
+
}, [])
|
|
128
|
+
|
|
129
|
+
// Handle keyboard shortcuts
|
|
130
|
+
useInput((input, key) => {
|
|
131
|
+
// Ctrl+C to exit
|
|
132
|
+
if (key.ctrl && input === "c") {
|
|
133
|
+
serviceManager.forceStopAll()
|
|
134
|
+
exit()
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Ctrl+L to clear current log
|
|
139
|
+
if (key.ctrl && input === "l") {
|
|
140
|
+
if (services[activeTab]) {
|
|
141
|
+
serviceManager.clearLogs(services[activeTab].id)
|
|
142
|
+
}
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Ctrl+K to stop current service
|
|
147
|
+
if (key.ctrl && input === "k") {
|
|
148
|
+
if (services[activeTab] && services[activeTab].id !== "system") {
|
|
149
|
+
stopService(services[activeTab].id)
|
|
150
|
+
}
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Left/Right arrow to switch tabs (Tab is reserved for command autocomplete)
|
|
155
|
+
if (key.leftArrow && services.length > 0) {
|
|
156
|
+
setActiveTab((activeTab - 1 + services.length) % services.length)
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
if (key.rightArrow && services.length > 0) {
|
|
160
|
+
setActiveTab((activeTab + 1) % services.length)
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Ctrl+1-9 or Cmd+1-9 to switch tabs directly
|
|
165
|
+
if ((key.ctrl || key.meta) && /^[1-9]$/.test(input)) {
|
|
166
|
+
const tabIndex = parseInt(input) - 1
|
|
167
|
+
if (tabIndex < services.length) {
|
|
168
|
+
setActiveTab(tabIndex)
|
|
169
|
+
}
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Handle auto-start commands
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
if (autoStart?.length) {
|
|
177
|
+
setTimeout(() => {
|
|
178
|
+
autoStart.forEach((cmd) => {
|
|
179
|
+
handleCommand(`/${cmd}`)
|
|
180
|
+
})
|
|
181
|
+
}, 100)
|
|
182
|
+
}
|
|
183
|
+
}, [])
|
|
184
|
+
|
|
185
|
+
const handleCommand = (input: string) => {
|
|
186
|
+
const trimmed = input.trim()
|
|
187
|
+
if (!trimmed.startsWith("/")) return
|
|
188
|
+
|
|
189
|
+
const parts = trimmed.slice(1).split(" ")
|
|
190
|
+
const command = parts[0]
|
|
191
|
+
const cmdArgs = parts.slice(1)
|
|
192
|
+
|
|
193
|
+
switch (command) {
|
|
194
|
+
case "help":
|
|
195
|
+
addSystemLog(getHelpText())
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
case "dev":
|
|
199
|
+
startDevServer(cmdArgs)
|
|
200
|
+
break
|
|
201
|
+
|
|
202
|
+
case "socket":
|
|
203
|
+
if (cmdArgs[0] === "send") {
|
|
204
|
+
handleSocketSend(cmdArgs.slice(1))
|
|
205
|
+
} else if (cmdArgs[0] === "list") {
|
|
206
|
+
handleSocketList()
|
|
207
|
+
} else {
|
|
208
|
+
const socketWithMock =
|
|
209
|
+
cmdArgs.includes("--with-mock") || args?.withMock === true
|
|
210
|
+
startSocketServer(socketWithMock)
|
|
211
|
+
}
|
|
212
|
+
break
|
|
213
|
+
|
|
214
|
+
case "mock":
|
|
215
|
+
// Shorthand for /socket --with-mock
|
|
216
|
+
startSocketServer(true)
|
|
217
|
+
break
|
|
218
|
+
|
|
219
|
+
case "ext":
|
|
220
|
+
const browser = cmdArgs[0] || "chrome"
|
|
221
|
+
launchExtension(browser)
|
|
222
|
+
break
|
|
223
|
+
|
|
224
|
+
case "stop":
|
|
225
|
+
stopService(cmdArgs[0])
|
|
226
|
+
break
|
|
227
|
+
|
|
228
|
+
case "restart":
|
|
229
|
+
restartService(cmdArgs[0])
|
|
230
|
+
break
|
|
231
|
+
|
|
232
|
+
case "clear":
|
|
233
|
+
if (services[activeTab]) {
|
|
234
|
+
serviceManager.clearLogs(services[activeTab].id)
|
|
235
|
+
}
|
|
236
|
+
break
|
|
237
|
+
|
|
238
|
+
case "quit":
|
|
239
|
+
case "exit":
|
|
240
|
+
serviceManager.forceStopAll()
|
|
241
|
+
exit()
|
|
242
|
+
break
|
|
243
|
+
|
|
244
|
+
case "ai":
|
|
245
|
+
handleAICommand(cmdArgs)
|
|
246
|
+
break
|
|
247
|
+
|
|
248
|
+
case "extract-config":
|
|
249
|
+
case "extract":
|
|
250
|
+
handleExtractConfig(cmdArgs)
|
|
251
|
+
break
|
|
252
|
+
|
|
253
|
+
case "add-dependency":
|
|
254
|
+
handleAddDependency(cmdArgs)
|
|
255
|
+
break
|
|
256
|
+
|
|
257
|
+
default:
|
|
258
|
+
addSystemLog(
|
|
259
|
+
`Unknown command: ${command}. Type /help for available commands.`,
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const startDevServer = (cmdArgs: string[]) => {
|
|
265
|
+
const noHttps = cmdArgs.includes("--no-https") || args?.noHttps === true
|
|
266
|
+
const noSocket = cmdArgs.includes("--no-socket") || args?.noSocket === true
|
|
267
|
+
const withMock = cmdArgs.includes("--with-mock") || args?.withMock === true
|
|
268
|
+
const withFirefox = cmdArgs.includes("--firefox") || args?.firefox === true
|
|
269
|
+
const withChrome = cmdArgs.includes("--chrome") || args?.chrome === true
|
|
270
|
+
|
|
271
|
+
// Determine port from env or default
|
|
272
|
+
const port = process.env.NODE_PORT || 3060
|
|
273
|
+
const useHttps = !noHttps
|
|
274
|
+
|
|
275
|
+
// Socket server starts by default unless --no-socket is passed
|
|
276
|
+
const shouldStartSocket = !noSocket
|
|
277
|
+
|
|
278
|
+
// Check if already running
|
|
279
|
+
if (serviceManager.isRunning("vite")) {
|
|
280
|
+
addSystemLog("Vite dev server is already running.")
|
|
281
|
+
// Switch to vite tab
|
|
282
|
+
const viteIdx = services.findIndex((s) => s.id === "vite")
|
|
283
|
+
if (viteIdx >= 0) setActiveTab(viteIdx)
|
|
284
|
+
return
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
startVite({ noHttps })
|
|
288
|
+
|
|
289
|
+
// Also start socket server based on flags/env (with mock if requested)
|
|
290
|
+
if (shouldStartSocket && !serviceManager.isRunning("socket")) {
|
|
291
|
+
startSocket({ withMock })
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Launch browser extensions if requested (pass URL options)
|
|
295
|
+
if (withFirefox && !serviceManager.isRunning("ext-firefox")) {
|
|
296
|
+
startExtension({ browser: "firefox", useHttps, port })
|
|
297
|
+
}
|
|
298
|
+
if (withChrome && !serviceManager.isRunning("ext-chrome")) {
|
|
299
|
+
startExtension({ browser: "chrome", useHttps, port })
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Sync and switch to the new vite tab
|
|
303
|
+
const updatedServices = serviceManager.getAllServices()
|
|
304
|
+
const viteIdx = updatedServices.findIndex((s) => s.id === "vite")
|
|
305
|
+
setServices(
|
|
306
|
+
updatedServices.map((s) => ({
|
|
307
|
+
id: s.id,
|
|
308
|
+
name: s.name,
|
|
309
|
+
status: s.status,
|
|
310
|
+
logs: s.logs,
|
|
311
|
+
})),
|
|
312
|
+
)
|
|
313
|
+
setActiveTab(
|
|
314
|
+
viteIdx >= 0 ? viteIdx : Math.max(0, updatedServices.length - 1),
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const startSocketServer = (withMock: boolean = false) => {
|
|
319
|
+
if (serviceManager.isRunning("socket")) {
|
|
320
|
+
addSystemLog("Socket.IO server is already running.")
|
|
321
|
+
const socketIdx = services.findIndex((s) => s.id === "socket")
|
|
322
|
+
if (socketIdx >= 0) setActiveTab(socketIdx)
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
startSocket({ withMock })
|
|
327
|
+
|
|
328
|
+
// Sync and switch to the new socket tab
|
|
329
|
+
const updatedServices = serviceManager.getAllServices()
|
|
330
|
+
const socketIdx = updatedServices.findIndex((s) => s.id === "socket")
|
|
331
|
+
setServices(
|
|
332
|
+
updatedServices.map((s) => ({
|
|
333
|
+
id: s.id,
|
|
334
|
+
name: s.name,
|
|
335
|
+
status: s.status,
|
|
336
|
+
logs: s.logs,
|
|
337
|
+
})),
|
|
338
|
+
)
|
|
339
|
+
setActiveTab(
|
|
340
|
+
socketIdx >= 0 ? socketIdx : Math.max(0, updatedServices.length - 1),
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const launchExtension = (browser: string) => {
|
|
345
|
+
const browserType = browser.toLowerCase() as BrowserType
|
|
346
|
+
if (browserType !== "chrome" && browserType !== "firefox") {
|
|
347
|
+
addSystemLog(`Invalid browser: ${browser}. Use 'chrome' or 'firefox'.`)
|
|
348
|
+
return
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const serviceId = `ext-${browserType}`
|
|
352
|
+
if (serviceManager.isRunning(serviceId)) {
|
|
353
|
+
addSystemLog(`${browser} extension is already running.`)
|
|
354
|
+
const extIdx = services.findIndex((s) => s.id === serviceId)
|
|
355
|
+
if (extIdx >= 0) setActiveTab(extIdx)
|
|
356
|
+
return
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
startExtension({ browser: browserType })
|
|
360
|
+
|
|
361
|
+
// Sync and switch to the new extension tab
|
|
362
|
+
const updatedServices = serviceManager.getAllServices()
|
|
363
|
+
const extIdx = updatedServices.findIndex((s) => s.id === serviceId)
|
|
364
|
+
setServices(
|
|
365
|
+
updatedServices.map((s) => ({
|
|
366
|
+
id: s.id,
|
|
367
|
+
name: s.name,
|
|
368
|
+
status: s.status,
|
|
369
|
+
logs: s.logs,
|
|
370
|
+
})),
|
|
371
|
+
)
|
|
372
|
+
setActiveTab(extIdx >= 0 ? extIdx : Math.max(0, updatedServices.length - 1))
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const stopService = (serviceId?: string) => {
|
|
376
|
+
const targetId = serviceId || services[activeTab]?.id
|
|
377
|
+
if (!targetId) {
|
|
378
|
+
addSystemLog("No service specified. Usage: /stop <service-id>")
|
|
379
|
+
return
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (targetId === "vite") {
|
|
383
|
+
stopVite()
|
|
384
|
+
} else if (targetId === "socket") {
|
|
385
|
+
stopSocket()
|
|
386
|
+
} else if (targetId.startsWith("ext-")) {
|
|
387
|
+
const browser = targetId.replace("ext-", "") as BrowserType
|
|
388
|
+
stopExtension(browser)
|
|
389
|
+
} else {
|
|
390
|
+
serviceManager.stop(targetId)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
syncServices()
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const restartService = (serviceId?: string) => {
|
|
397
|
+
const targetId = serviceId || services[activeTab]?.id
|
|
398
|
+
if (!targetId) {
|
|
399
|
+
addSystemLog("No service to restart. Usage: /restart [service-id]")
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (targetId === "system") {
|
|
404
|
+
addSystemLog("Cannot restart the system service.")
|
|
405
|
+
return
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const success = serviceManager.restart(targetId)
|
|
409
|
+
if (!success) {
|
|
410
|
+
addSystemLog(`Cannot restart "${targetId}". Service config not found.`)
|
|
411
|
+
}
|
|
412
|
+
syncServices()
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const handleAICommand = async (cmdArgs: string[]) => {
|
|
416
|
+
const subCommand = cmdArgs[0]
|
|
417
|
+
|
|
418
|
+
switch (subCommand) {
|
|
419
|
+
case "model":
|
|
420
|
+
// Set or show current AI provider
|
|
421
|
+
const providerArg = cmdArgs[1] as AIProvider | undefined
|
|
422
|
+
if (providerArg) {
|
|
423
|
+
const result = aiService.setProvider(providerArg)
|
|
424
|
+
if (result.success) {
|
|
425
|
+
addSystemLog(`✅ ${result.message}`)
|
|
426
|
+
} else {
|
|
427
|
+
addSystemLog(`❌ ${result.message}`)
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
// Show current provider and available providers
|
|
431
|
+
const current = aiService.getProviderInfo()
|
|
432
|
+
const providers = getAvailableProviders()
|
|
433
|
+
let message = `Current AI provider: ${current ? getProviderStatus(current) : "None"}\n\nAvailable providers:`
|
|
434
|
+
for (const p of providers) {
|
|
435
|
+
const status = p.available
|
|
436
|
+
? getProviderStatus(p)
|
|
437
|
+
: `${p.name} (not available)`
|
|
438
|
+
const marker = p.id === current?.id ? " ← current" : ""
|
|
439
|
+
message += `\n ${p.id}: ${status}${marker}`
|
|
440
|
+
if (!p.available && p.reason) {
|
|
441
|
+
message += `\n ${p.reason}`
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
message += "\n\nUsage: /ai model <claude|codex|gemini>"
|
|
445
|
+
addSystemLog(message)
|
|
446
|
+
}
|
|
447
|
+
break
|
|
448
|
+
|
|
449
|
+
case "status":
|
|
450
|
+
// Show detailed status of all providers
|
|
451
|
+
const providers = getAvailableProviders()
|
|
452
|
+
const currentProvider = aiService.getProvider()
|
|
453
|
+
let statusMsg = "AI Provider Status:\n"
|
|
454
|
+
for (const p of providers) {
|
|
455
|
+
const icon = p.available ? "✅" : "❌"
|
|
456
|
+
const current = p.id === currentProvider ? " (current)" : ""
|
|
457
|
+
statusMsg += `\n ${icon} ${getProviderStatus(p)}${current}`
|
|
458
|
+
if (!p.available && p.reason) {
|
|
459
|
+
statusMsg += `\n ${p.reason}`
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
addSystemLog(statusMsg)
|
|
463
|
+
break
|
|
464
|
+
|
|
465
|
+
case "ask":
|
|
466
|
+
// Quick question without opening panel
|
|
467
|
+
const question = cmdArgs.slice(1).join(" ")
|
|
468
|
+
if (!question) {
|
|
469
|
+
addSystemLog("Usage: /ai ask <your question>")
|
|
470
|
+
return
|
|
471
|
+
}
|
|
472
|
+
if (!aiService.isAvailable()) {
|
|
473
|
+
addSystemLog(
|
|
474
|
+
`Current provider (${aiService.getProvider()}) is not available. Run /ai model to select a different provider.`,
|
|
475
|
+
)
|
|
476
|
+
return
|
|
477
|
+
}
|
|
478
|
+
const providerName = aiService.getProviderInfo()?.name || "AI"
|
|
479
|
+
addSystemLog(`Asking ${providerName}: ${question}`)
|
|
480
|
+
try {
|
|
481
|
+
aiService.loadProjectContext(process.cwd())
|
|
482
|
+
const response = await aiService.sendMessage(question)
|
|
483
|
+
addSystemLog(`${providerName}: ${response}`)
|
|
484
|
+
} catch (err) {
|
|
485
|
+
addSystemLog(
|
|
486
|
+
`Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
487
|
+
)
|
|
488
|
+
}
|
|
489
|
+
break
|
|
490
|
+
|
|
491
|
+
case "clear":
|
|
492
|
+
aiService.clearConversation()
|
|
493
|
+
addSystemLog("Conversation history cleared.")
|
|
494
|
+
break
|
|
495
|
+
|
|
496
|
+
case "chat":
|
|
497
|
+
default:
|
|
498
|
+
// Open AI chat panel
|
|
499
|
+
if (!aiService.isAvailable()) {
|
|
500
|
+
addSystemLog(
|
|
501
|
+
`Current provider (${aiService.getProvider()}) is not available. Run /ai model to select a different provider.`,
|
|
502
|
+
)
|
|
503
|
+
return
|
|
504
|
+
}
|
|
505
|
+
setShowAIPanel(true)
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const addSystemLog = (message: string) => {
|
|
510
|
+
// Use functional update to properly handle rapid successive calls
|
|
511
|
+
setServices((prev) => {
|
|
512
|
+
const existingSystem = prev.find((s) => s.id === "system")
|
|
513
|
+
if (existingSystem) {
|
|
514
|
+
// Add message to existing system service
|
|
515
|
+
return prev.map((s) =>
|
|
516
|
+
s.id === "system" ? { ...s, logs: [...s.logs, message] } : s,
|
|
517
|
+
)
|
|
518
|
+
} else {
|
|
519
|
+
// Create new system service with the message
|
|
520
|
+
const newService: Service = {
|
|
521
|
+
id: "system",
|
|
522
|
+
name: "System",
|
|
523
|
+
status: "running",
|
|
524
|
+
logs: [message],
|
|
525
|
+
}
|
|
526
|
+
return [...prev, newService]
|
|
527
|
+
}
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
// Switch to system tab
|
|
531
|
+
setTimeout(() => {
|
|
532
|
+
setServices((current) => {
|
|
533
|
+
const sysIdx = current.findIndex((s) => s.id === "system")
|
|
534
|
+
if (sysIdx >= 0) setActiveTab(sysIdx)
|
|
535
|
+
return current // Don't modify, just read
|
|
536
|
+
})
|
|
537
|
+
}, 50)
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const handleSocketSend = async (eventArgs: string[]) => {
|
|
541
|
+
if (!eventArgs.length) {
|
|
542
|
+
addSystemLog("Usage: /socket send <event-name> [identifier]")
|
|
543
|
+
return
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const eventName = eventArgs[0]
|
|
547
|
+
const identifier = eventArgs[1]
|
|
548
|
+
|
|
549
|
+
addSystemLog(`Sending socket event: ${eventName}...`)
|
|
550
|
+
|
|
551
|
+
const result = await sendSocketEvent(eventName, identifier)
|
|
552
|
+
if (result.success) {
|
|
553
|
+
addSystemLog(`✅ ${result.message}`)
|
|
554
|
+
} else {
|
|
555
|
+
addSystemLog(`❌ ${result.message}`)
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const handleSocketList = () => {
|
|
560
|
+
const events = listSocketEvents()
|
|
561
|
+
if (events.length === 0) {
|
|
562
|
+
addSystemLog(
|
|
563
|
+
"No socket events found. Check your socket-events directory.",
|
|
564
|
+
)
|
|
565
|
+
return
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
let message = "Available socket events:\n"
|
|
569
|
+
for (const event of events) {
|
|
570
|
+
message += `\n ${event.name}\n`
|
|
571
|
+
message += ` Event: ${event.event}\n`
|
|
572
|
+
message += ` Channel: ${event.channel}`
|
|
573
|
+
}
|
|
574
|
+
message += "\n\nUsage: /socket send <event-name> [identifier]"
|
|
575
|
+
addSystemLog(message)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const handleExtractConfig = async (cmdArgs: string[]) => {
|
|
579
|
+
const dryRun = cmdArgs.includes("--dry-run") || cmdArgs.includes("-d")
|
|
580
|
+
const overwrite = cmdArgs.includes("--overwrite") || cmdArgs.includes("-o")
|
|
581
|
+
|
|
582
|
+
addSystemLog("Scanning source files for GxP configuration...")
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
// Use dynamic imports for ES modules
|
|
586
|
+
const path = await import("path")
|
|
587
|
+
const fs = await import("fs")
|
|
588
|
+
const url = await import("url")
|
|
589
|
+
const { createRequire } = await import("module")
|
|
590
|
+
|
|
591
|
+
// Get the directory of this file and resolve to the utils directory
|
|
592
|
+
const __filename = url.fileURLToPath(import.meta.url)
|
|
593
|
+
const __dirname = path.dirname(__filename)
|
|
594
|
+
|
|
595
|
+
// The compiled JS is in dist/tui/, utils is in bin/lib/utils/
|
|
596
|
+
// From dist/tui/ we need to go up to package root, then into bin/lib/utils/
|
|
597
|
+
const packageRoot = path.resolve(__dirname, "..", "..")
|
|
598
|
+
const utilsPath = path.join(
|
|
599
|
+
packageRoot,
|
|
600
|
+
"bin",
|
|
601
|
+
"lib",
|
|
602
|
+
"utils",
|
|
603
|
+
"extract-config.js",
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
// Create a require function to load CommonJS modules
|
|
607
|
+
const requireCjs = createRequire(import.meta.url)
|
|
608
|
+
const extractConfigUtils = requireCjs(utilsPath) as {
|
|
609
|
+
extractConfigFromSource: (srcDir: string) => ExtractedConfig
|
|
610
|
+
mergeConfig: (
|
|
611
|
+
existing: Record<string, unknown>,
|
|
612
|
+
extracted: ExtractedConfig,
|
|
613
|
+
options: { overwrite: boolean },
|
|
614
|
+
) => Record<string, unknown>
|
|
615
|
+
generateSummary: (config: ExtractedConfig) => string
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const projectPath = process.cwd()
|
|
619
|
+
const srcDir = path.join(projectPath, "src")
|
|
620
|
+
const manifestPath = path.join(projectPath, "app-manifest.json")
|
|
621
|
+
|
|
622
|
+
// Check if src directory exists
|
|
623
|
+
if (!fs.existsSync(srcDir)) {
|
|
624
|
+
addSystemLog("Source directory not found: src/")
|
|
625
|
+
return
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Extract configuration
|
|
629
|
+
const extractedConfig = extractConfigUtils.extractConfigFromSource(srcDir)
|
|
630
|
+
const summary = extractConfigUtils.generateSummary(extractedConfig)
|
|
631
|
+
addSystemLog(summary)
|
|
632
|
+
|
|
633
|
+
// Count total items
|
|
634
|
+
const totalItems =
|
|
635
|
+
Object.keys(extractedConfig.strings).length +
|
|
636
|
+
Object.keys(extractedConfig.settings).length +
|
|
637
|
+
Object.keys(extractedConfig.assets).length +
|
|
638
|
+
Object.keys(extractedConfig.triggerState).length +
|
|
639
|
+
extractedConfig.dependencies.length
|
|
640
|
+
|
|
641
|
+
if (totalItems === 0) {
|
|
642
|
+
addSystemLog("No GxP configuration found in source files.")
|
|
643
|
+
return
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (dryRun) {
|
|
647
|
+
addSystemLog("Dry run mode - no changes made.")
|
|
648
|
+
addSystemLog("Run /extract-config without --dry-run to apply changes.")
|
|
649
|
+
return
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Load or create manifest
|
|
653
|
+
let existingManifest: Record<string, unknown> = {}
|
|
654
|
+
if (fs.existsSync(manifestPath)) {
|
|
655
|
+
try {
|
|
656
|
+
existingManifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"))
|
|
657
|
+
} catch {
|
|
658
|
+
addSystemLog("Could not parse existing manifest, creating new one.")
|
|
659
|
+
existingManifest = getDefaultManifest()
|
|
660
|
+
}
|
|
661
|
+
} else {
|
|
662
|
+
addSystemLog("Creating new app-manifest.json")
|
|
663
|
+
existingManifest = getDefaultManifest()
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Merge and write
|
|
667
|
+
const mergedManifest = extractConfigUtils.mergeConfig(
|
|
668
|
+
existingManifest,
|
|
669
|
+
extractedConfig,
|
|
670
|
+
{ overwrite },
|
|
671
|
+
)
|
|
672
|
+
fs.writeFileSync(manifestPath, JSON.stringify(mergedManifest, null, "\t"))
|
|
673
|
+
addSystemLog("Updated app-manifest.json")
|
|
674
|
+
} catch (err) {
|
|
675
|
+
addSystemLog(
|
|
676
|
+
`Error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
677
|
+
)
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const handleAddDependency = async (cmdArgs: string[]) => {
|
|
682
|
+
// The add-dependency wizard requires interactive terminal access (raw stdin)
|
|
683
|
+
// which conflicts with Ink's own stdin handling. Run it in a separate terminal.
|
|
684
|
+
const envFlag = cmdArgs.find((a) => a === "-e" || a === "--env")
|
|
685
|
+
const envVal = envFlag ? cmdArgs[cmdArgs.indexOf(envFlag) + 1] : ""
|
|
686
|
+
const cmd = `gxdev add-dependency${envVal ? ` -e ${envVal}` : ""}`
|
|
687
|
+
|
|
688
|
+
addSystemLog("")
|
|
689
|
+
addSystemLog(
|
|
690
|
+
"The Add Dependency wizard requires interactive terminal access.",
|
|
691
|
+
)
|
|
692
|
+
addSystemLog("Run this command in a separate terminal:")
|
|
693
|
+
addSystemLog("")
|
|
694
|
+
addSystemLog(` \x1B[36m${cmd}\x1B[0m`)
|
|
695
|
+
addSystemLog("")
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const getDefaultManifest = () => ({
|
|
699
|
+
name: "GxToolkit",
|
|
700
|
+
version: "1.0.0",
|
|
701
|
+
description: "GxToolkit Plugin",
|
|
702
|
+
manifest_version: 3,
|
|
703
|
+
asset_dir: "/src/assets/",
|
|
704
|
+
configurationFile: "configuration.json",
|
|
705
|
+
appInstructionsFile: "app-instructions.md",
|
|
706
|
+
defaultStylingFile: "default-styling.css",
|
|
707
|
+
settings: {},
|
|
708
|
+
strings: { default: {} },
|
|
709
|
+
assets: {},
|
|
710
|
+
triggerState: {},
|
|
711
|
+
dependencies: [],
|
|
712
|
+
permissions: [],
|
|
713
|
+
})
|
|
714
|
+
|
|
715
|
+
const getHelpText = () => `
|
|
667
716
|
Available commands:
|
|
668
717
|
|
|
669
718
|
Development Server:
|
|
@@ -720,55 +769,62 @@ Keyboard shortcuts:
|
|
|
720
769
|
Tab Autocomplete command
|
|
721
770
|
↑/↓ Navigate suggestions or command history
|
|
722
771
|
Esc Clear input
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
772
|
+
`
|
|
773
|
+
|
|
774
|
+
// Show AI panel
|
|
775
|
+
if (showAIPanel) {
|
|
776
|
+
return (
|
|
777
|
+
<AIPanel onClose={() => setShowAIPanel(false)} onLog={addSystemLog} />
|
|
778
|
+
)
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const currentService = services[activeTab]
|
|
782
|
+
|
|
783
|
+
// Calculate log panel height to make room for suggestions
|
|
784
|
+
// Fixed elements: Header (3), TabBar (1), Log border (2), Input (3), Hints (1) = 10 rows minimum
|
|
785
|
+
const fixedRows = 10
|
|
786
|
+
const availableForLog = terminalHeight - fixedRows - suggestionRows
|
|
787
|
+
const logPanelHeight = Math.max(3, availableForLog) // Minimum 3 rows for log panel
|
|
788
|
+
|
|
789
|
+
return (
|
|
790
|
+
<Box flexDirection="column" height={terminalHeight}>
|
|
791
|
+
<Header projectName={process.cwd().split("/").pop() || "gxdev"} />
|
|
792
|
+
|
|
793
|
+
{services.length > 0 && (
|
|
794
|
+
<TabBar
|
|
795
|
+
services={services}
|
|
796
|
+
activeTab={activeTab}
|
|
797
|
+
onTabChange={setActiveTab}
|
|
798
|
+
/>
|
|
799
|
+
)}
|
|
800
|
+
|
|
801
|
+
<Box
|
|
802
|
+
height={logPanelHeight}
|
|
803
|
+
flexDirection="column"
|
|
804
|
+
borderStyle="single"
|
|
805
|
+
borderColor="gray"
|
|
806
|
+
overflow="hidden"
|
|
807
|
+
>
|
|
808
|
+
{currentService ? (
|
|
809
|
+
<LogPanel logs={currentService.logs} maxHeight={logPanelHeight} />
|
|
810
|
+
) : (
|
|
811
|
+
<WelcomeScreen />
|
|
812
|
+
)}
|
|
813
|
+
</Box>
|
|
814
|
+
|
|
815
|
+
<CommandInput
|
|
816
|
+
onSubmit={handleCommand}
|
|
817
|
+
activeService={
|
|
818
|
+
currentService
|
|
819
|
+
? {
|
|
820
|
+
id: currentService.id,
|
|
821
|
+
name: currentService.name,
|
|
822
|
+
status: currentService.status,
|
|
823
|
+
}
|
|
824
|
+
: null
|
|
825
|
+
}
|
|
826
|
+
onSuggestionsChange={handleSuggestionsChange}
|
|
827
|
+
/>
|
|
828
|
+
</Box>
|
|
829
|
+
)
|
|
774
830
|
}
|